伙伴匹配系统笔记

伙伴匹配系统

伙伴匹配系统修改信息集成在移动端页面里,不单独把后台系统拿来用

数据库表设计

​ 标签的分类(要有哪些标签、怎么把标签进行分类)

新增标签表(分类表)

建议用标签,不要用分类,更灵活。

性别:男、女

方向:Java、C++、Go、前端

正在学:Spring

目标:考研、春招、秋招、社招、考公、竞赛(蓝桥杯)、转行、跳槽

段位:初级、中级、高级、王者

身份:小学、初中、高中、大一、大二、大三、大四、学生、待业、已就业、研一、研二、研三

状态:乐观、有点丧、一般、单身、已婚、有对象

字段:

id int 主键

标签名 varchar 非空(必须唯一,唯一索引)

上传标签的用户 userId int(如果要根据 userId 查已上传标签的话,最好加上,普通索引)

父标签 id ,parentId,int(分类)

是否为父标签 isParent, tinyint(0 不是父标签、1 - 父标签)

创建时间 createTime,datetime

更新时间 updateTime,datetime

是否删除 isDelete, tinyint(0、1)

怎么查询所有标签,并且把标签分好组?按父标签 id 分组,能实现 √

根据父标签查询子标签?根据 id 查询,能实现 √

SQL 语言分类:

DDL define 建表、操作表

DML manage 更新删除数据,影响实际表里的内容

DCL control 控制,权限

DQL query 查询,select

https://www.cnblogs.com/fan-yuan/p/7879353.html

修改用户表

用户有哪些标签?

根据自己的实际需求来!!! 此处选择第一种

  1. 直接在用户表补充 tags 字段,['Java', '男'] 存 json 字符串 优点:查询方便、不用新建关联表,标签是用户的固有属性(除了该系统、其他系统可能要用到,标签是用户的固有属性)节省开发成本**查询用户列表,查关系表拿到这 100 个用户有的所有标签 id,再根据标签 id 去查标签表。**哪怕性能低,可以用缓存。缺点:用户表多一列,会有点
  2. 加一个关联表,记录用户和标签的关系关联表的应用场景:查询灵活,可以正查反查缺点:要多建一个表、多维护一个表重点:企业大项目开发中尽量减少关联查询,很影响扩展性,而且会影响查询性能

开发后端接口

模糊查询

只输入内容的一个字或者多个就可以搜索出来

搜索功能

传入多个标签可以搜索出含有这些标签的用户,这样子的话like不太行

1.like

sql语句用like就可以

2.内存查询

先获取全部用户,然后查用户包含的全部标签

引入gson库,可以把java属性json序列化

​ 因为数据库中的tags标签我们是给他定义成json类型的,而json类型在在Java中,JSON字符串通常需要被解析为相应的Java对象才能被使用。这是因为JSON是一种数据交换格式,而Java是一种面向对象的语言。JSON字符串中的数据需要被转换为Java对象,以便在Java代码中进行操作。

在大多数情况下,我们无法直接在JSON字符串中使用Java对象,因为JSON字符串是一种文本格式,而Java对象是一种二进制格式。因此,我们需要使用JSON库(如Gson或Jackson)将JSON字符串转换为Java对象,或者将Java对象转换为JSON字符串。

在你的代码中,tags字段的值是一个JSON数组,其中每个元素都是一个Manageruser对象的JSON表示。因此,我们需要使用Gson库将这个JSON字符串转换为List<Manageruser>对象,以便在Java代码中直接使用这个列表。

如果你想在内部转换,你可以在Manageruser类中添加一个方法,用于将tags字段的值转换为List<Manageruser>对象。

jade
复制代码
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.9</version> </dependency>
java
复制代码
@Override public List<Manageruser> searchUserByTags(List<String> tagList) { // 这边是sql写法 // if (CollectionUtils.isEmpty(tagList)){ // throw new BesinessException(ErrorCode.PARAMS_ERROR,"标签为空"); // } // QueryWrapper<Manageruser> queryWrapper = new QueryWrapper<>(); //// 拼接and // for (String tag : tagList) { // queryWrapper=queryWrapper.like("tags", tag); // } //// 查询出准确的 与数据库对应的标签然后再,通过这些标签查用户 // List<Manageruser> manageruserList = manageruserMapper.selectList(queryWrapper); // 这边利用内存来查询 // 查询出所有用户 QueryWrapper<Manageruser> queryWrapper = new QueryWrapper<>(); List<Manageruser> selectList = manageruserMapper.selectList(queryWrapper); // 引入gson Gson gson = new Gson(); for (Manageruser manageruser : selectList) { String str=manageruser.getTags(); Set<String> tagnameSet = gson.fromJson(str, new TypeToken<Set<String>>() {}.getType()); // 现在tagnameset就包含了用户数据表中所有的tag标签 // 遍历传进来的列表,看看用户数据表的全部标签(tagnameSet)里面是否包含,如果全部都有tagnameSet包含就true,没有就false // 因为你有标签tagnameSet没有,那肯定就没有用户符合条件了嘛 for (String tagName : tagList) { if (!tagnameSet.contains(tagName)) { return false; } } return true; } return selectList.stream().map(this::getsafetyManageruser).collect(Collectors.toList()); } }

这里简化,用filter来代替for循环

java
复制代码
userlist.stream().filter(manageruser -> { String str=manageruser.getTags(); Set<String> tagnameSet = gson.fromJson(str, new TypeToken<Set<String>>() {}.getType()); // 现在tagnameset就包含了用户数据表中所有的tag标签 // 遍历传进来的列表,看看用户数据表的全部标签(tagnameSet)里面是否包含,如果全部都有tagnameSet包含就true,没有就false // 因为你有标签tagnameSet没有,那肯定就没有用户符合条件了嘛 for (String tagName : tagList) { if (!tagnameSet.contains(tagName)) { return false; } } return true; })

swagger和knife4j

一个后端测试工具,可以很方便的看到 调试接口

配置 Springboot3

1.引入maven包

xml
复制代码
<!-- knife4j 接口文档 --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.4.0</version> </dependency>

2.配置类(改名字的没找到)

java
复制代码
package com.example.parntersystem.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * web层配置类,实现静态资源映射,将knife4j相关资源放行,保证生成的接口文档能够正常进行展示 * @author Hva */ @Configuration public class WebMvcConfig implements WebMvcConfigurer { /** * 设置静态资源映射 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 添加静态资源映射规则 registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); //配置 knife4j 的静态资源请求映射地址 registry.addResourceHandler("/doc.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); } }

yml配置

yaml
复制代码
knife4j: enable: true setting: language: zh_cn

然后在访问这个网址(后缀加/doc.html)

http
复制代码
http://localhost:8081/api/doc.html

![幕截图 2024-10-27 02451](C:\Users\HYR\Pictures\Screenshots\屏幕截图 2024-10-27 024513.png)

toast样式背景变白问题

踩了一个坑,关于vant的toast样式背景变白问题,这是由于我们同时使用了propu组件导致样式冲突

解决方法也很简单,自定义一个css文件,main.js引入就行了(在vant的css文件后)

css
复制代码
/* vant4的 Toast 和 Popup 样式冲突,会导致 Toast 变成白底 */ .van-popup.van-toast { background: var(--van-toast-background) !important; box-sizing: content-box !important; /* 下面3条css,影响不大 */ transition: all var(--van-duration-fast) !important; width: var(--van-toast-default-width) !important; max-width: var(--van-toast-max-width) !important; } .van-popup.van-toast .van-toast__icon { font-size: var(--van-toast-icon-size) !important; }

跨域问题解决(后端方法)

注:一般来说跨域是由后端解决的

在后端controller层的主接口上加上@CrossOrigin注解

java
复制代码
@RestController @RequestMapping("/user") public class ManagerUserController { @Resource private ManageruserService manageruserService; ... }

这样所有的网址访问这个都不会有跨域问题了,但是这样有问题不安全。所以我们可以给他指定不跨域的网址(前端的)

java
复制代码
@CrossOrigin(origins = {"http://localhost:5174"})

前端发送接口类型要求为数组问题

由于后端要求要数组类型,但是前端发的是字符串,所以报错

解决

1:引入qs库

vue
复制代码
npm install qs

2.在axios请求下用

javascript
复制代码
myAxios.get('/user/search/tags',{ withCredentials: false, params: { tagNameList: tags }, //用鱼皮的这个我的头像不会显示。 // paramsSerializer: params =>{ // return qs.stringify(params,{indices: false}) // } //序列化(用这个) paramsSerializer: { serialize: params => qs.stringify(params, { indices: false}), } })

SpringBoot3整合redis

引入依赖(不用指定版本,springboot会自动整合)

xml
复制代码
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- session-data-redis --> <!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis --> <!-- 引入spring和redis的整合,可以自动将session存到redis中--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>

配置yml(springboot3不用搞store-type=redis)

详情见官方文档,一个redis,一个redis和session Spring Boot 参考文档

Spring Session - Spring Boot :: Spring Session - Spring 框架

yaml
复制代码
spring: session: # 设置令牌的失效时间 1天86400秒 redis: namespace: spring:session flush-mode: on_save timeout: 86400 data: redis: port: 6379 host: localhost # database指的是redis的哪个数据库,db0 db1?这里1就表示是db1 database: 1

Axios请求携带cookie

在axios的全局配置给拦截器加下面这行

javascript
复制代码
myaxios.defaults.withCredentials = true

注意后端还要注意这几项配置不然会报跨域

yaml
复制代码
server: servlet: session: cookie: domain: localhost

接口头跨域配置

java
复制代码
@RestController @RequestMapping("/user") 下面这个为true,而且要设置准确地址值 //@CrossOrigin(origins = {"http://localhost:5174"},allowCredentials = "true") public class ManagerUserController {

关于数据批量插入性能优化的问题

这里去看视频里的讲解很详细,个人现在暂时用不到,能实现功能就行了,这里留个坑,以后要到可以看。

##MyBatis-plus分页限制数据

由于数据量大的问题,浏览器加载不出来,鱼皮里面100万条数据加载了30秒,浏览器崩溃了,那么怎么解决这个问题呢?加分页,限制数据的传入,可以减轻浏览器的压力,节约性能,还可以防偷数据的。

官网分页插件配置项

java
复制代码
@Configuration @MapperScan("com.example.parntersystem.mapper") public class MybatisPlusConfig { /** * 添加分页插件 */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 如果配置多个插件, 切记分页最后添加 // 如果有多数据源可以不配具体类型, 否则都建议配上具体的 DbType return interceptor; } }

官网实例

java
复制代码
// 无条件分页查询 IPage<T> page(IPage<T> page); // 条件分页查询 IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper); // 无条件分页查询 IPage<Map<String, Object>> pageMaps(IPage<T> page); // 条件分页查询 IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);
java
复制代码
// 假设要进行无条件的分页查询,每页显示10条记录,查询第1页 IPage<User> page = new Page<>(1, 10); IPage<User> userPage = userService.page(page); // 调用 page 方法 List<User> userList = userPage.getRecords(); long total = userPage.getTotal(); System.out.println("Total users: " + total); for (User user : userList) { System.out.println("User: " + user); }

实际

java
复制代码
@GetMapping("/recommend") public BaseResponse<Page<Manageruser>> UserRecommend(@RequestParam long pageSize,long pageNum, HttpServletRequest request) { // 其实就相当于查全部数据 QueryWrapper<Manageruser> queryWrapper = new QueryWrapper<>(); // 这行再去数据库里查一遍,然后返回list集合 // mybatis plus分页 Page<Manageruser> Userlist = manageruserService.page(new Page<>(pageSize,pageNum),queryWrapper); return ResultUtils.success(Userlist); }

redis整合,存缓存

引入包

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> </dependency>

自定义配置文件(自己封装一个,框架用的是jdk原生的序列化工具,到redis里面会出问题)

java
复制代码
/** * * 自定义序列化 * */ @Configuration public class RedisTemplateConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { //创建RedisTemplate对象 RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); //设置连接工厂 redisTemplate.setConnectionFactory(connectionFactory); //设置Key的序列化 redisTemplate.setKeySerializer(RedisSerializer.string()); //创建Json序列化工具 GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); //设置Value的序列化 redisTemplate.setValueSerializer(jsonRedisSerializer); return redisTemplate; } }

controller层使用

设置key,拿缓存

java
复制代码
// 性能优化,去缓存里查,如果有缓存,直接读缓存 Manageruser loginuser = manageruserService.getloginuser(request); String redisKey = String.format("UserRecommend:%s",loginuser.getId()); ValueOperations valueOperations = redisTemplate.opsForValue(); Page<Manageruser> userPage = ( Page<Manageruser> ) valueOperations.get(redisKey);

设置key,存缓存(2个key一样,毕竟要根据这个来拿)

注意:redis内存不能无限叠加,一定要设置过期时间!!!

java
复制代码
// 如果缓存里没有,再从数据库里查 QueryWrapper<Manageruser> queryWrapper = new QueryWrapper<>(); // mybatis plus分页 userPage = manageruserService.page(new Page<>(pageSize,pageNum),queryWrapper); // 查完数据库写缓存 try { // 设置10秒钟过期,毕竟也不可能一直存,数据是会变化的 valueOperations.set(redisKey, userPage,30000, TimeUnit.MILLISECONDS); } catch (Exception e) { log.error("redis set error",e); } return ResultUtils.success(userPage);

定时任务实现

定时执行某个方法 某个类,比如在每天的2点就执行一次这个方法

这样有什么好处?每天在固定的时间点执行,存一遍缓存(redis),如果要用的话就直接拿预留存好的缓存,可以大幅度的减少请求的时间,毕竟redis已经有了,直接调就行了。

第一种方式:

  1. 主类开启 @EnableScheduling

    java
    复制代码
    @SpringBootApplication @MapperScan("com.example.parntersystem.mapper") @EnableRedisHttpSession @EnableScheduling public class ParnterSystemApplication { public static void main(String[] args) { SpringApplication.run(ParnterSystemApplication.class, args); } }
  2. 给要定时执行的方法添加 @Scheduling 注解,指定 cron 表达式或者执行频率(指定什么时候执行,用这个定义,里面的表达式可以通过网上的工具生成)

    java
    复制代码
    public class PrecacheJob { @Scheduled(cron = "0 12 1 * * *") }

    不要去背 cron 表达式!!!!!

操作redisson

留个空之后理解再来

#组队功能开发

需求分析

理想的应用场景

我要跟别人一起参加竞赛或者做项目,可以发起队伍或者加入别人的队伍

用户可以 创建 一个队伍,设置队伍的人数、队伍名称(标题)、描述、超时时间 P0

  1. 队长、剩余的人数
  2. 聊天?
  3. 公开 或 private 或加密
  4. 用户创建队伍最多 5 个

展示队伍列表,根据名称搜索队伍 P0,信息流中不展示已过期的队伍

修改队伍信息 P0 ~ P1

用户可以加入队伍(其他人、未满、未过期),允许加入多个队伍,但是要有个上限 P0

是否需要队长同意?筛选审批?

用户可以退出队伍(如果队长 退出,权限转移给第二早加入的用户 —— 先来后到) P1

队长可以解散队伍 P0

分享队伍 =》 邀请其他用户加入队伍 P1

业务流程:

  1. 生成分享链接(分享二维码)
  2. 用户访问链接,可以点击加入

队伍人满后发送消息通知 P1

数据库表设计

队伍表 team

字段:

  • id 主键 bigint(最简单、连续,放 url 上比较简短,但缺点是爬虫)
  • name 队伍名称
  • description 描述
  • maxNum 最大人数
  • expireTime 过期时间
  • userId 创建人 id
  • status 0 - 公开,1 - 私有,2 - 加密
  • password 密码
  • createTime 创建时间
  • updateTime 更新时间
  • isDelete 是否删除

用户 - 队伍表 user_team

用户表是用来连接队伍和用户2个表的,统计2个表之间的数据,主要就是,统计某个用户加入了哪个队伍

字段:

  • id 主键

  • userId 用户 id

  • teamId 队伍 id

  • joinTime 加入时间

  • createTime 创建时间

  • updateTime 更新时间

  • isDelete 是否删除

接口系统设计

1、创建队伍

用户可以 创建 一个队伍,设置队伍的人数、队伍名称(标题)、描述、超时时间 P0

队长、剩余的人数

聊天?

公开 或 private 或加密

信息流中不展示已过期的队伍

  1. 请求参数是否为空?

  2. 是否登录,未登录不允许创建

  3. 校验信息

    1. 队伍人数 > 1 且 <= 20
    2. 队伍标题 <= 20
    3. 描述 <= 512
    4. status 是否公开(int)不传默认为 0(公开)
    5. 如果 status 是加密状态,一定要有密码,且密码 <= 32
    6. 超时时间 > 当前时间
    7. 校验用户最多创建 5 个队伍
  4. 插入队伍信息到队伍表

  5. 插入用户 => 队伍关系到关系表SELECT

2. 修改队伍信息
  1. 判断请求参数是否为空
  2. 查询队伍是否存在
  3. 只有管理员或者队伍的创建者可以修改
  4. 如果用户传入的新值和老值一致,就不用 update 了(可自行实现,降低数据库使用次数)TODO
  5. 如果队伍状态改为加密,必须要有密码
  6. 更新成功
3. 用户加入队伍

其他人、未满、未过期,允许加入多个队伍,但是要有个上限 P0

  1. 用户最多加入 5 个队伍
  2. 队伍必须存在,只能加入未满、未过期的队伍
  3. 不能加入自己的队伍,不能重复加入已加入的队伍(幂等性)
  4. 禁止加入私有的队伍
  5. 如果加入的队伍是加密的,必须密码匹配才可以
  6. 新增队伍 - 用户关联信息
5.用户退出队伍

请求参数:队伍 id

  1. 校验请求参数

  2. 校验队伍是否存在

  3. 校验我是否已加入队伍

  4. 如果队伍

    1. 只剩一人,队伍解散
    2. 还有其他人
      1. 如果是队长退出队伍,权限转移给第二早加入的用户 —— 先来后到

只用取 id 最小的 2 条数据

text
复制代码
ⅱ. 非队长,自己退出队伍

​ 总结:是队长就改队伍表和关系表,不是队长就只改关系表,如果队伍只剩一个人删队伍表和关系表

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP