伙伴匹配系统(移动端 H5 网站(APP 风格)基于Spring Boot 后端 + Vue3 - 02

项目地址:
- Github:github.com/China-Rainb...
- Gitee:gitee.com/Rainbow--Se...
@[toc]
后端整合 Swagger + Knife4j 接口文档
什么是接口文档,写接口信息的文档。
每个接口的信息包括:
- 请求参数
- 响应参数:
- 错误码
- 接口地址
- 接口名称
- 请求类型
- 请求格式
- 备注
谁用接口文档?
答:一般是后端或者负责人来提供,后端和前端都要使用
为什么需要接口文档?
- 有一个书面内容(背书或者归档),便于大家参考和查阅,便于沉淀和维护,拒绝口口相传。
- 接口文档便于前端和后端开发对接,前后端联调的介质,后端 => 接口文档 <= 前端。
- 好的接口文档支持在线调试,在线测试,可以作为工具提高我们的开发测试效率。
怎么做接口文档?
- 手写:比如腾讯文档、Markdown笔记
- 自动化接口文档生成:自动根据项目代码生成完整的文档或在线调试的网页。Swagger、Postman(侧重接口管理)(国外);apifox、apipost、eolink(国产)
使用 Swagger
- 引l入依赖(Swagger或Knife4j:doc.xiaominfo.com/knife4j/doc...)
- 自定义Swagger配置类
- 定义需要生成接口文档的代码位置(Controller)
- 千万注意:线上环境不要把接口暴露出去!!!可以通过在SwaggerConfig配置文件开头加上@Profile({"dev","test"})限定配置仅在部分环境开启
- 启动即可
- 可以通过在controller方法上添加[@Api、@ApilmplicitParam(name]VApi、@ApilmplicitParam(name)="name",value="姓名",required=true)[@ApiOperation(value]/ApiOperation(value)="向客人问好")等注解来自定义生成的接口描述信息
使用 swagger 日志
swagger 官网地址:swagger.io/

导入相关的依赖
xml
<!-- 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

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();
}
}

java
apis(RequestHandler)

如果 Spring Boot Verision >= 2.6 ,需要添加如下配置
yaml
spring:
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER

访问路径:你的项目名映射路径+/doc.html ;比如: localhost:8080/api/doc.html

网页内容抓取-存量用户信息导入及同步
看上了网页信息,怎么抓到的
-
分析原网站是怎么获取这些数据的?哪个接口? F12,刷新------>触发请求
-
用程序去调用接口
-
处理(清洗)一下数据,之后就可以写到数据库里。
流程:
- 从 Excel 中导入全量用户数据,判重。
- 抓取写了自我介绍的同学信息,提取出用户昵称,用户唯一 id,自我介绍信息。
- 从自我介绍中提取信息,
Easy Excel 读取 Excel 当中的信息
Easy Excel 官网地址:alibaba-easyexcel.github.io/index.html

Easy Excel 官方文档:easyexcel.opensource.alibaba.com/docs/curren...
两种读 Excel 的方式:
- 确定表头:建立对象;和表头形成映射
- 不确定表头:每一行数据映射为 Map<String.Object>
两种读取模式:
- 监听器:先创建监听器,再读取文件时绑定监听器。单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。
- 同步读:无需创建监听器,一次性获取完整数据。方便简单,但是数据量大时会有等待时常,也可能内存溢出。
导入 Easy Excel 依赖
xml
<!-- 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>
java
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;
}

读取方式一:监听器:先创建监听器,再读取文件时绑定监听器。单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。

java
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
*/
@Override
public void invoke(DemoExcelData data, AnalysisContext context) {
System.out.println(data);
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("已解析完成");
}
}

java
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();
}
}

读取方式二:同步读:无需创建监听器,一次性获取完整数据。方便简单,但是数据量大时会有等待时常,也可能内存溢出。

java
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 同步读取会自动finish
List<DemoExcelData> totalDataList =
EasyExcel.read(fileName).head(DemoExcelData.class).sheet().doReadSync();
for (DemoExcelData xingQiuTableUserInfo : totalDataList) {
System.out.println(xingQiuTableUserInfo);
}
}
}

**技巧:**读取 Excel 当前的数据,进行一个过滤,提交操作
java
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 同步读取会自动finish
List<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 的操作,大家可以参考官方文档: easyexcel.opensource.alibaba.com/docs/curren...
前端页面跳转传值
- query => url serachParams ,url 后附加参数,传递的值长度有限。
- vuex(全局状态管理),eg:搜索页将关键词塞到状态中,搜索结果页从状态取值。

banner.txt 广告位
只需要在 resource根目录下创建一个: banner.txt 文件即可
文件当中输入/填写你所想要让你的项目启动的时候的一些提示信息即可。
这样其他人沿用了你的项目,启动该项目的时候,则会看到你的这个广告信息。
latex
用户中心------> RainbowSea CSDN 博客地址: rainbowsea.blog.csdn.net


后端接受前端值时出现的问题:


问题:我们需要将前端的传值的格式修改一下,修改为一个可以被后端识别为一个字符串的值。
**这里使用 ****axios-js**
**的一个前端库,进行解决 **
axios-js 官网地址:www.axios-http.cn/docs/intro


bash
yarn add axios




@CrossOrigin
后端跨域,允许任何请求都同意
一般都是后端处理跨域问题的,更加灵活。后端统一防守。只能防前端,不能防止后端
java
@CrossOrigin(origins="localhost:8080")

改造用户中心,把单机登录改为分布式 Session 登录
还有一种方式就是:使用 Tociket 。但是 Tociket 过期时间不是那么容易简单控制的(需要额外配置,但是更加灵活)
Session 共享
种 Session 的时候注意范围:cookie.domain
比如两个域名:
如果要共享 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 作为该项目的新的端口,从而启动一个新的项目上的作为一个新的服务器启动一个项目------》到达分布式服务器的一种方式。
bash
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 的相关配置:
yaml
spring:
# Redis 配置
redis:
port: 6379
host: localhost
database: 0

- 引入 redis. 能够操作 redis:尽量和你的 Spring Boot 的版本一一对应上。
xml
<!-- 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 的版本一一对应上。
xml
<!-- 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 。

yaml
spring:
session:
timeout: 86400
store-type: redis

yaml
server:
port: 8080
servlet:
context-path: /api
spring:
profiles:
active: dev
application:
name: yupao-backend
mvc:
pathmatch:
matching-strategy: ant_path_matcher
# DataSource Config
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/usercenter
username: root
password: MySQL123
# session 失效时间
session:
timeout: 86400
store-type: redis
# Redis 配置
redis:
port: 6379
host: localhost
database: 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: false
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

重新启动两个项目,模拟分布式,测试



最后:
"在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。"