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

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

项目地址:

@[toc]


后端整合 Swagger + Knife4j 接口文档

什么是接口文档,写接口信息的文档。

每个接口的信息包括:

  • 请求参数
  • 响应参数:
    • 错误码
  • 接口地址
  • 接口名称
  • 请求类型
  • 请求格式
  • 备注

谁用接口文档?

答:一般是后端或者负责人来提供,后端和前端都要使用

为什么需要接口文档?

  • 有一个书面内容(背书或者归档),便于大家参考和查阅,便于沉淀和维护,拒绝口口相传。
  • 接口文档便于前端和后端开发对接,前后端联调的介质,后端 => 接口文档 <= 前端。
  • 好的接口文档支持在线调试,在线测试,可以作为工具提高我们的开发测试效率。

怎么做接口文档?

  • 手写:比如腾讯文档、Markdown笔记
  • 自动化接口文档生成:自动根据项目代码生成完整的文档或在线调试的网页。Swagger、Postman(侧重接口管理)(国外);apifox、apipost、eolink(国产)

使用 Swagger

  1. 引l入依赖(Swagger或Knife4j:doc.xiaominfo.com/knife4j/doc...)
  2. 自定义Swagger配置类
  3. 定义需要生成接口文档的代码位置(Controller)
  4. 千万注意:线上环境不要把接口暴露出去!!!可以通过在SwaggerConfig配置文件开头加上@Profile({"dev","test"})限定配置仅在部分环境开启
  5. 启动即可
  6. 可以通过在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

网页内容抓取-存量用户信息导入及同步

看上了网页信息,怎么抓到的

  1. 分析原网站是怎么获取这些数据的?哪个接口? F12,刷新------>触发请求

  2. 用程序去调用接口

  3. 处理(清洗)一下数据,之后就可以写到数据库里。

流程:

  1. 从 Excel 中导入全量用户数据,判重
  2. 抓取写了自我介绍的同学信息,提取出用户昵称,用户唯一 id,自我介绍信息。
  3. 从自我介绍中提取信息,

Easy Excel 读取 Excel 当中的信息

Easy Excel 官网地址:alibaba-easyexcel.github.io/index.html

Easy Excel 官方文档:easyexcel.opensource.alibaba.com/docs/curren...

两种读 Excel 的方式:

  1. 确定表头:建立对象;和表头形成映射
  2. 不确定表头:每一行数据映射为 Map<String.Object>

两种读取模式:

  1. 监听器:先创建监听器,再读取文件时绑定监听器。单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。
  2. 同步读:无需创建监听器,一次性获取完整数据。方便简单,但是数据量大时会有等待时常,也可能内存溢出。

导入 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 信息将其映射为一个对象。

  1. 确定表头:建立对象;和表头形成映射
  2. 不确定表头:每一行数据映射为 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...

前端页面跳转传值

  1. query => url serachParams ,url 后附加参数,传递的值长度有限。
  2. 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,不认识该用户?

原因如下:

  1. 用户在 A 登录,所以 Session(用户登录信息)存在了 A 上
  2. 结果请求 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 安装

  1. 安装 Windows 版本的 Redis

在 application.yaml 文件配置 Redis 的相关配置:

yaml 复制代码
spring:
  # Redis 配置
  redis:
    port: 6379
    host: localhost
    database: 0
  1. 引入 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>
  1. 引入 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>
  1. 修改 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

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

最后:

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

相关推荐
森林里的程序猿猿3 分钟前
并发设计模式
java·开发语言·jvm
222you12 分钟前
四个主要的函数式接口
java·开发语言
Javatutouhouduan28 分钟前
Java全栈面试进阶宝典:内容全面,题目高频!
java·高并发·java面试·java面试题·后端开发·java程序员·java八股文
SEO-狼术42 分钟前
RAD Studio 13.1 Florence adds
java
reembarkation42 分钟前
vue3中使用howler播放音频列表
前端·vue.js·音视频
ywf12151 小时前
Spring Boot接收参数的19种方式
java·spring boot·后端
敲代码的瓦龙2 小时前
Java?面向对象三大特性!!!
java·开发语言
架构师沉默2 小时前
AI 写的代码,你敢上线吗?
java·后端·架构
骑龙赶鸭2 小时前
java开发项目中遇到的难点,面试!
java·开发语言·面试