伙伴匹配系统(移动端 H5 网站(APP 风格)基于Spring Boot 后端 + Vue3 - 02
项目地址:
- Github:https://github.com/China-Rainbow-sea/yupao
- Gitee:https://gitee.com/Rainbow--Sea/yupao
@
- 伙伴匹配系统(移动端 H5 网站(APP 风格)基于Spring Boot 后端 + Vue3 - 02
- 后端整合 Swagger + Knife4j 接口文档
- 使用 swagger 日志
- 网页内容抓取-存量用户信息导入及同步
- Easy Excel 读取 Excel 当中的信息
- 前端页面跳转传值
- banner.txt 广告位
- 后端接受前端值时出现的问题:
@CrossOrigin
后端跨域,允许任何请求都同意- 改造用户中心,把单机登录改为分布式 Session 登录
- Session 共享
- Redis Windows 安装
- 最后:
后端整合 Swagger + Knife4j 接口文档
什么是接口文档,写接口信息的文档。
每个接口的信息包括:
- 请求参数
- 响应参数:
- 错误码
- 接口地址
- 接口名称
- 请求类型
- 请求格式
- 备注
谁用接口文档?
答:一般是后端或者负责人来提供,后端和前端都要使用
为什么需要接口文档?
- 有一个书面内容(背书或者归档),便于大家参考和查阅,便于沉淀和维护,拒绝口口相传。
- 接口文档便于前端和后端开发对接,前后端联调的介质,后端 => 接口文档 <= 前端。
- 好的接口文档支持在线调试,在线测试,可以作为工具提高我们的开发测试效率。
怎么做接口文档?
- 手写:比如腾讯文档、Markdown笔记
- 自动化接口文档生成:自动根据项目代码生成完整的文档或在线调试的网页。Swagger、Postman(侧重接口管理)(国外);apifox、apipost、eolink(国产)
使用 Swagger
- 引l入依赖(Swagger或Knife4j:https://doc.xiaominfo.com/knife4j/documentation/get_start.html))
- 自定义Swagger配置类
- 定义需要生成接口文档的代码位置(Controller)
- 千万注意:线上环境不要把接口暴露出去!!!可以通过在SwaggerConfig配置文件开头加上@Profile({"dev","test"})限定配置仅在部分环境开启
- 启动即可
- 可以通过在controller方法上添加[@Api、@ApilmplicitParam(name]VApi、@ApilmplicitParam(name)="name",value="姓名",required=true)[@ApiOperation(value]/ApiOperation(value)=“向客人问好")等注解来自定义生成的接口描述信息
使用 swagger 日志
swagger 官网地址:https://swagger.io/
导入相关的依赖
<!-- swagger --><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>3.0.0</version></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId><version>3.0.0</version></dependency>
在配置文件config目录下,添加swagger 的配置文件 SwaggerConfig.java
package com.yupi.yupao.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;/*** 自定义 Swagger 接口文档的配置** @author <a href="https://github.com/rainbowsea">Raibnowsea</a>*/
@Configuration
//@EnableSwagger2WebMvc
@EnableSwagger2
@Profile({"dev", "test"}) // 表示该项目在什么样的开发环境下,对外开发接口文档
public class SwaggerConfig {@Bean(value = "defaultApi2") //public Docket defaultApi2() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()// 这里一定要标注你控制器的位置.apis(RequestHandlerSelectors.basePackage("com.rainbowsea.yupao.controller")).paths(PathSelectors.any()).build();}/*** api 信息** @return*/private ApiInfo apiInfo() {return new ApiInfoBuilder().title("鱼皮用户中心").description("鱼皮用户中心接口文档").termsOfServiceUrl("https://github.com/rainbowsea").contact(new Contact("yupi", "https://github.com/rainbowsea", "xxx@qq.com")).version("1.0").build();}
}
apis(RequestHandler)
如果 Spring Boot Verision >= 2.6 ,需要添加如下配置
spring:mvc:pathmatch:matching-strategy: ANT_PATH_MATCHER
访问路径:你的项目名映射路径+/doc.html ;比如: localhost:8080/api/doc.html
网页内容抓取-存量用户信息导入及同步
看上了网页信息,怎么抓到的
-
分析原网站是怎么获取这些数据的?哪个接口? F12,刷新——>触发请求
-
用程序去调用接口
-
处理(清洗)一下数据,之后就可以写到数据库里。
流程:
- 从 Excel 中导入全量用户数据,判重。
- 抓取写了自我介绍的同学信息,提取出用户昵称,用户唯一 id,自我介绍信息。
- 从自我介绍中提取信息,
Easy Excel 读取 Excel 当中的信息
Easy Excel 官网地址:https://alibaba-easyexcel.github.io/index.html
Easy Excel 官方文档:https://easyexcel.opensource.alibaba.com/docs/current/
两种读 Excel 的方式:
- 确定表头:建立对象;和表头形成映射
- 不确定表头:每一行数据映射为 Map<String.Object>
两种读取模式:
- 监听器:先创建监听器,再读取文件时绑定监听器。单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。
- 同步读:无需创建监听器,一次性获取完整数据。方便简单,但是数据量大时会有等待时常,也可能内存溢出。
导入 Easy Excel 依赖
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version>
</dependency>
这里我们创建一个:最简单的读的对象。用于存放我们读取到的 Excel 信息将其映射为一个对象。
- 确定表头:建立对象;和表头形成映射
- 不确定表头:每一行数据映射为 Map<String.Object>
package com.rainbowsea.yupao.one;import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;@Data
public class DemoExcelData {//@ExcelProperty(index = 1) 可以用列表匹配/*** id * 强制读取第几个,这里不建议用 index 和 name 同时使用,* 要么一个对象只用 index ,要么一个对象只用 name 去匹配*/@ExcelProperty("成员编号")private String planeCode;/*** 用户昵称*/@ExcelProperty("成员昵称")private String username;}
读取方式一:监听器:先创建监听器,再读取文件时绑定监听器。单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。
package com.rainbowsea.yupao.one;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import lombok.extern.slf4j.Slf4j;/*** Excel 读取监听**/
@Slf4j
public class TableListener implements ReadListener<DemoExcelData> {/*** 这个每一条数据解析都会来调用** @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(DemoExcelData data, AnalysisContext context) {System.out.println(data);}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {System.out.println("已解析完成");}
}
package com.rainbowsea.yupao.one;import com.alibaba.excel.EasyExcel;import java.util.List;/*** 导入 Excel**/
public class ImportExcel {/*** 读取数据*/public static void main(String[] args) {// todo 记得改为自己的测试文件String fileName = "E:\\Java\\project\\鱼皮星球项目\\伙伴匹配系统\\yupao\\yupao-backend\\src\\main\\resources\\testExcel.xlsx";readByListener(fileName);}/*** 监听器读取* @param fileName*/public static void readByListener(String fileName) {EasyExcel.read(fileName, DemoExcelData.class, new TableListener()).sheet().doRead();}}
读取方式二:同步读:无需创建监听器,一次性获取完整数据。方便简单,但是数据量大时会有等待时常,也可能内存溢出。
package com.rainbowsea.yupao.one;import com.alibaba.excel.EasyExcel;import java.util.List;/*** 导入 Excel**/
public class ImportExcel {/*** 读取数据*/public static void main(String[] args) {// todo 记得改为自己的测试文件String fileName = "E:\\Java\\project\\鱼皮星球项目\\伙伴匹配系统\\yupao\\yupao-backend\\src\\main\\resources\\testExcel.xlsx";synchronousRead(fileName);}/*** 同步读** @param fileName*/public static void synchronousRead(String fileName) {// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finishList<DemoExcelData> totalDataList =EasyExcel.read(fileName).head(DemoExcelData.class).sheet().doReadSync();for (DemoExcelData xingQiuTableUserInfo : totalDataList) {System.out.println(xingQiuTableUserInfo);}}}
技巧:读取 Excel 当前的数据,进行一个过滤,提交操作
package com.yupi.yupao.once.importuser;import com.alibaba.excel.EasyExcel;
import org.apache.commons.lang3.StringUtils;import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 导入星球用户到数据库** @author <a href="https://github.com/liyupi">程序员鱼皮</a>* @from <a href="https://yupi.icu">编程导航知识星球</a>*/
public class ImportXingQiuUser {public static void main(String[] args) {// todo 记得改为自己的测试文件String fileName = "E:\\星球项目\\yupao-backend\\src\\main\\resources\\prodExcel.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finishList<XingQiuTableUserInfo> userInfoList =EasyExcel.read(fileName).head(XingQiuTableUserInfo.class).sheet().doReadSync();System.out.println("总数 = " + userInfoList.size());Map<String, List<XingQiuTableUserInfo>> listMap =userInfoList.stream().filter(userInfo -> StringUtils.isNotEmpty(userInfo.getUsername())).collect(Collectors.groupingBy(XingQiuTableUserInfo::getUsername));for (Map.Entry<String, List<XingQiuTableUserInfo>> stringListEntry : listMap.entrySet()) {if (stringListEntry.getValue().size() > 1) {System.out.println("username = " + stringListEntry.getKey());System.out.println("1");}}System.out.println("不重复昵称数 = " + listMap.keySet().size());}
}
关于 Easy Excel 的写入到 Excel 的操作,大家可以参考官方文档:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write
前端页面跳转传值
- query => url serachParams ,url 后附加参数,传递的值长度有限。
- vuex(全局状态管理),eg:搜索页将关键词塞到状态中,搜索结果页从状态取值。
banner.txt 广告位
只需要在 resource根目录下创建一个: banner.txt 文件即可
文件当中输入/填写你所想要让你的项目启动的时候的一些提示信息即可。
这样其他人沿用了你的项目,启动该项目的时候,则会看到你的这个广告信息。
用户中心——> RainbowSea CSDN 博客地址: rainbowsea.blog.csdn.net
后端接受前端值时出现的问题:
问题:我们需要将前端的传值的格式修改一下,修改为一个可以被后端识别为一个字符串的值。
**这里使用 **axios-js**
的一个前端库,进行解决 **
axios-js 官网地址:https://www.axios-http.cn/docs/intro
yarn add axios
@CrossOrigin
后端跨域,允许任何请求都同意
一般都是后端处理跨域问题的,更加灵活。后端统一防守。只能防前端,不能防止后端
@CrossOrigin(origins="localhost:8080")
改造用户中心,把单机登录改为分布式 Session 登录
还有一种方式就是:使用 Tociket 。但是 Tociket 过期时间不是那么容易简单控制的(需要额外配置,但是更加灵活)
Session 共享
种 Session 的时候注意范围:cookie.domain
比如两个域名:
- aaa.yupi.com
- bbb.yupi.com
如果要共享 cookie,可以种一个更高层的公共域名,比如:yupi.com 。
为什么服务器 A 登录后,请求发送到服务器 B,不认识该用户?
思考:为什么服务器 A 登录后,请求发到服务器 B,不认识该用户?
原因如下:
- 用户在 A 登录,所以 Session(用户登录信息)存在了 A 上
- 结果请求 B 时,B 没有用户信息,所以不认识
如图:
解决方案:共享存储,而不是把数据放到单台服务器的内存中。
Redis 基于内存的 K/V 数据库 ,此处选择 Redis,因为用户信息读取?(是否登录的判断极其频繁),Redis 基于内存,读写性能很高,简单的数据单机 qps 5w-10w
通过将打包的项目,使用java -jar .\yupao-backend-0.0.1-SNAPSHOT.jar --server.port=8081
定义 8081 作为该项目的新的端口,从而启动一个新的项目上的作为一个新的服务器启动一个项目——》到达分布式服务器的一种方式。
E:\Java\project\鱼皮星球项目\伙伴匹配系统\yupao\yupao-backend\target>java -jar .\yupao-backend-0.0.1-SNAPSHOT.jar --server.port=8081
Redis Windows 安装
- 安装 Windows 版本的 Redis
在 application.yaml 文件配置 Redis 的相关配置:
spring:# Redis 配置redis:port: 6379host: localhostdatabase: 0
- 引入 redis. 能够操作 redis:尽量和你的 Spring Boot 的版本一一对应上。
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.6.4</version>
</dependency>
- 引入 spring-session 和 Redis 的整合,使得自动将 session 存储到 Redis 当中。同样:尽量和你的 Spring Boot 的版本一一对应上。
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId><version>2.6.4</version>
</dependency>
- 修改 spring-session 存储配置,默认是在
spring.session.store-type
默认是 none ,表示存储在单台服务器。
store-type:redis
表示从 redis 读写两个都从 Redis 来 session 。
spring:session:timeout: 86400store-type: redis
server:port: 8080servlet:context-path: /api
spring:profiles:active: devapplication:name: yupao-backendmvc:pathmatch:matching-strategy: ant_path_matcher# DataSource Configdatasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/usercenterusername: rootpassword: MySQL123# session 失效时间session:timeout: 86400store-type: redis# Redis 配置redis:port: 6379host: localhostdatabase: 0
mybatis-plus:global-config:db-config:logic-delete-field: isDelete # 全局逻辑删除字段名,所以项目表当中所有逻辑删除,都用这个字段名,保证全局性logic-delete-value: 1 # 逻辑已删除值(默认值为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认值为 0)configuration:# 取消数据库驼峰映射map-underscore-to-camel-case: falselog-impl: org.apache.ibatis.logging.stdout.StdOutImpl
重新启动两个项目,模拟分布式,测试
最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”