理想的应用场景:
我需要去创建一个队伍,这也是伙伴匹配项目的核心,别人可以加入队伍一起做某一件事情。
添加队伍需求分析:
具体的功能:
1:用户可以 创建 一个队伍,设置队伍的人数、队伍名称(标题)、描述、超时时间
- 队长、剩余的人数
- 聊天?
- 公开 或 private 或加密
- 用户创建队伍最多 5 个
2:展示队伍列表,根据名称搜索队伍 ,信息流中不展示已过期的队伍
3:修改队伍信息
4:用户可以加入队伍(其他人、未满、未过期),允许加入多个队伍,但是要有个上限
5:用户可以退出队伍(如果队长 退出,权限转移给第二早加入的用户 —— 先来后到)
6:队长可以解散队伍
7:分享队伍 =》 邀请其他用户加入队伍
需求分析先想到这里,等后面真正写代码的时候再看看有没有什么需要补充
数据库表设计:
我们需要思考,要完成上面这个功能,我们需要几张表
第一反应肯定是要一张队伍表 Team
但是我们继续想,如果只有一张队伍表,你这个队伍中肯定有成员,
一个成员可以加不同的队伍
一个队伍肯定有很多不同的成员
这里就设计到多对多的关系了
数据库表设计如何解决多对多的问题?
有两种方式
1:建立一张关联表 userteam(便于修改,查询性能高一点,可以选择这个,不用全表遍历)
2:在用户表和队伍表中添加字段(便于查询,不用写多对多的代码,可以直接根据队伍查用户、根据用户查队伍)
比如在用户表中添加队伍字段,说明这个用户加入了那些队伍
在队伍表中添加用户字段,说明有哪些用户加入了这个队伍
但是这样确实是不需要再开一张表,查询起来也快
不过我感觉这样有一个问题:就是当你的队伍失效或者解散的时候,你要修改在这个队伍中的每个用户的队伍字段,这就很麻烦了
当然这个项目采用关联表会比较合适,以后做项目得照情况分析。
Team表字段:
create table team
(
id bigint auto_increment comment 'id'
primary key,
name varchar(256) not null comment '队伍名称',
description varchar(1024) null comment '描述',
maxNum int default 1 not null comment '最大人数',
expireTime datetime null comment '过期时间',
userId bigint comment '用户id',
status int default 0 not null comment '0 - 公开,1 - 私有,2 - 加密',
password varchar(512) null comment '密码',
createTime datetime default CURRENT_TIMESTAMP null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
isDelete tinyint default 0 not null comment '是否删除'
)
comment '队伍';
userteam关联表字段:
create table user_team
(
id bigint auto_increment comment 'id'
primary key,
userId bigint comment '用户id',
teamId bigint comment '队伍id',
joinTime datetime null comment '加入时间',
createTime datetime default CURRENT_TIMESTAMP null comment '创建时间',
updateTime datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP,
isDelete tinyint default 0 not null comment '是否删除'
)
comment '用户队伍关系';
下面一步就是编写后端代码了
后端代码编写:
1:实体生成:
利用Mybatis-plus的插件生成Team,UserTeam实体类
Mapper及其注解 ,Service。
2:基本的增删改查:
这个直接用一下Mybatis-plus的框架写起来很快
package com.usercenter.usercenterproject.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.usercenter.usercenterproject.Pojo.*;
import com.usercenter.usercenterproject.Pojo.Request.PageRequest;
import com.usercenter.usercenterproject.Pojo.Request.TeamAddRequest;
import com.usercenter.usercenterproject.Pojo.dto.TeamQuery;
import com.usercenter.usercenterproject.exception.BusinessException;
import com.usercenter.usercenterproject.service.TeamService;
import com.usercenter.usercenterproject.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/*
队伍接口
*/
@RestController
@RequestMapping("/team")
@Slf4j
@CrossOrigin
public class TeamController {
@Resource
private TeamService teamService;
@Resource
private UserService userService;
@PostMapping("/add")
public BaseResponse<Long> addTeam(@RequestBody TeamAddRequest teamAddRequest, HttpServletRequest request){
if(teamAddRequest==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("增加队伍"+teamAddRequest);
User loginUser = userService.getCurrent(request);
Team team = new Team();
BeanUtils.copyProperties(teamAddRequest,team);
final Long save = teamService.addTeam(team, loginUser);
return ResultUtils.success(save);
}
@DeleteMapping("/delete")
public BaseResponse<Boolean> deleteTeam(Long id){
if(id<0){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("删除队伍"+id);
final boolean flag = teamService.removeById(id);
if(!flag){
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
return ResultUtils.success(flag);
}
@PostMapping("/update")
public BaseResponse<Boolean> updateTeam(@RequestBody Team team){
if(team==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("修改队伍信息"+team);
final boolean flag = teamService.updateById(team);
if(!flag){
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
return ResultUtils.success(flag);
}
@GetMapping("/get")
public BaseResponse<Team> getTeam(Long id){
if(id<0){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("查询队伍信息"+id);
final Team team = teamService.getById(id);
if(team==null){
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
return ResultUtils.success(team);
}
@GetMapping("/list")
public BaseResponse<List<Team>> listTeam(TeamQuery teamQuery){
if (teamQuery==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("批量查询队伍信息"+teamQuery);
Team team = new Team();
BeanUtils.copyProperties(team,teamQuery);
QueryWrapper<Team> queryWrapper = new QueryWrapper<>(team);
final List<Team> teamList = teamService.list(queryWrapper);
return ResultUtils.success(teamList);
}
@GetMapping("/list/page")
public BaseResponse<Page<Team>> listpageTeam(TeamQuery teamQuery){
if (teamQuery==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("分页查询队伍信息"+teamQuery);
Page<Team> page = new Page<>(teamQuery.getPageNum(),teamQuery.getPageSize());
Team team = new Team();
BeanUtils.copyProperties(teamQuery,team);
QueryWrapper<Team> queryWrapper = new QueryWrapper<>(team);
Page<Team> teamPage = teamService.page(page, queryWrapper);
return ResultUtils.success(teamPage);
}
}
这里的addTeam方法已经被改过了。
注意点:
1:全量查询时(listTeam):封装了一个TeamQuery这样一个dto用来接收前端参数,其实也是告诉前端,只需要传这个实体里面的属性即可,(这个蛮好理解,有些字段前端不需要,比如id,当前时间字段)
BeanUtils:
在Java中,BeanUtils是Apache Commons库中的一个类,用于对Java Bean对象进行复制、克隆、属性拷贝
这里就用了一个复制属性的方法
BeanUtils.copyProperties(team,teamQuery);
将teamQuery里面的属性赋值给我们新建的一个team对象,然后用Mybatis-plus里的list方法进行查询(Mybatis-plus里的list方法必须要用Team对象进行查询)
2:分页查询时,我们也创建了一个实体类PageRequest
@Data
public class PageRequest {
/*
页面大小
*/
int pageSize = 10;
/*
当前是第几页
*/
int pageNum = 1;
}
然后我们用TeamQuery去继承这个PageRequest,这样我们在进行分页查询时,TeamQuery里面也会有分页的参数。
3:添加队伍功能具体设计:
首先编写代码之前,我们需要想好,我们添加队伍这个需求的具体逻辑
- 请求参数是否为空?
- 是否登录,未登录不允许创建
- 校验信息
-
- 队伍人数 > 1 且 <= 20
- 队伍标题 <= 20
- 描述 <= 512
- status 是否公开(int)不传默认为 0(公开)
- 如果 status 是加密状态,一定要有密码,且密码 <= 32
- 超时时间 > 当前时间
- 校验用户最多创建 5 个队伍
- 插入队伍信息到队伍表
- 插入用户 => 队伍关系到关系表
总结下来就是前四步是校验,后两步是将信息插入数据库中(队伍表和关系表)
具体分析这段代码:
Controller层代码在上面已经写了
Service层代码:
@Override
@Transactional(rollbackFor = Exception.class)
public Long addTeam(Team team, User loginUser) {
//1. 请求参数是否为空?
if (team == null) {
throw new BusinessException(ErrorCode.NULL_ERROR);
}
//2. 是否登录,未登录不允许创建
if (loginUser == null) {
throw new BusinessException(ErrorCode.NOT_LOGIN);
}
final Long userId = loginUser.getId();
//3. 校验信息
//a. 队伍人数 > 1 且 <= 20
int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
if (maxNum < 1 || maxNum > 20) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "队伍人数错误");
}
//b. 队伍标题 <= 20
String name = team.getName();
if (StringUtils.isBlank(name) || name.length() > 20) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "队伍标头错误");
}
//c. 描述 <= 512
String description = team.getDescription();
if (StringUtils.isBlank(description) && description.length() > 512) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "描述信息错误");
}
//d. status 是否公开(int)不传默认为 0(公开)
Integer status = team.getStatus();
TeamStatusConstant teamStatusConstant = TeamStatusConstant.getEnumByValue(status);
if (teamStatusConstant == null) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "队伍状态不满足要求");
}
//e. 如果 status 是加密状态,一定要有密码,且密码 <= 32
String password = team.getPassword();
if (TeamStatusConstant.SECRET.equals(status)) {
if (StringUtils.isBlank(password) || password.length() > 32) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "密码错误");
}
}
//f. 超时时间 > 当前时间
Date expireTime = team.getExpireTime();
if (new Date().after(expireTime)) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "时间错误");
}
//g. 校验用户最多创建 5 个队伍
QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", userId);
final long count = this.count(queryWrapper);
if (count > 5) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "用户创建队伍过多");
}
//4. 插入队伍信息到队伍表
team.setId(null);
team.setUserId(userId);
final boolean save1 = this.save(team);
if (!save1) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "插入队伍错误");
}
//5. 插入用户 => 队伍关系到关系
UserTeam userTeam = new UserTeam();
userTeam.setUserId(userId);
userTeam.setTeamId(team.getId());
userTeam.setCreateTime(new Date());
boolean save = userTeamService.save(userTeam);
if (!save) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "创建队伍失败");
}
return team.getId();
}
Optional类:
Java的Optional类是在Java 8中引入的一个类,用于解决空指针异常的问题,Java的Optional类是一种优雅地处理可能为空的对象的机制
int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
这段就是判断这个team.getMaxNum是否为空,如果为空,就添一个默认值0
如果不为空就为原值
StringUtils类:
这个类提供了很多用于处理字符串的实用工具方法
if (StringUtils.isBlank(name) || name.length() > 20) {}
这一段就是判断这个name字符串是否为空,如果为空,就返回true
测试(Knife4j):
在添加队伍功能的逻辑中有对当前登录用户的校验
所以测试之前记得登录
状态码返回200说明添加成功,在数据库中也看到了对应的队伍
测试时碰到的小坑:
我一开始输入的过期时间是2024-7-14
直接给我报了一个日期格式不匹配的错误
Cannot deserialize value of type `java.util.Date` from String "2024 7 14":
后面问了GPT
把过期时间改成了2024-07-14
这才测试成功
查询队伍需求分析:
具体的功能:
- 从请求参数中取出队伍名称等查询条件,如果存在则作为查询条件
- 不展示已过期的队伍(根据过期时间筛选)
- 可以通过某个关键词同时对名称和描述查询
- 只有管理员才能查看加密还有非公开的房间
- 关联查询已加入队伍的用户信息
- 关联查询已加入队伍的用户信息(可能会很耗费性能,建议大家用自己写 SQL 的方式实现
把上面这6个具体的功能分成两步:
一步就是封装查询条件
一步就是查询
首先来看Controll层代码:
@GetMapping("/list")
public BaseResponse<List<TeamUserVo>> listTeam(TeamQuery teamQuery,HttpServletRequest request){
if (teamQuery==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("批量查询队伍信息"+teamQuery);
List<TeamUserVo> lists = teamService.listTeam(teamQuery,request);
return ResultUtils.success(lists);
}
Controller层没有设计到过多的逻辑
唯一一个需要注意的点就是:这个TeamUserVo对象的包装
Vo对象就是返回给前端的对象,我们不需要把所有对象信息都返回给前端,所以封装一下
下面的Service层代码是重点:
@Override
public List<TeamUserVo> listTeam(TeamQuery teamQuery, HttpServletRequest request) {
QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
//从请求参数中取出队伍名称等查询条件,如果存在则作为查询条件
if(teamQuery != null){
final Long id = teamQuery.getId();
if(id!=null&&id>0){
queryWrapper.eq("id",id);
}
List<Long> idlist = teamQuery.getIdList();
if(!CollectionUtils.isEmpty(idlist)){
queryWrapper.eq("id",idlist);
}
final String name = teamQuery.getName();
if(StringUtils.isNotBlank(name)){
queryWrapper.eq("name",name);
}
final String description = teamQuery.getDescription();
if(StringUtils.isNotBlank(description)){
queryWrapper.eq("description",description);
}
final String searchText = teamQuery.getSearchText();
if(StringUtils.isNotBlank(searchText)){
queryWrapper.and(qw ->qw.like("name",searchText))
.or().like("description",searchText);
}
final Integer maxNum = teamQuery.getMaxNum();
if(maxNum!=null&&maxNum>0){
queryWrapper.eq("maxNum",maxNum);
}
final Long userId = teamQuery.getUserId();
if(userId!=null){
queryWrapper.eq("uesrId",userId);
}
final Integer status = teamQuery.getStatus();
TeamStatusConstant teamStatusConstant = TeamStatusConstant.getEnumByValue(status);
if(teamStatusConstant==null){
teamStatusConstant = TeamStatusConstant.PUBLIC;
}
final User current = userService.getCurrent(request);
final boolean isadmin = isAdmin(current);
if(!isadmin && TeamStatusConstant.PRIVATE.equals(teamStatusConstant)){
throw new BusinessException(ErrorCode.NO_AUTH);
}
queryWrapper.eq("status",teamStatusConstant.getValue());
}
//排除已经过期的队伍
queryWrapper.and(qw -> qw.gt("expireTime", new Date())
.or().isNull("expireTime"));
List<Team> teamList = this.list(queryWrapper);
if(CollectionUtils.isEmpty(teamList)){
return new ArrayList<>();
}
//查询队长(创建人)的用户列表
List<TeamUserVo> teamUserVoList = new ArrayList<>();
for (Team team : teamList) {
TeamUserVo teamUserVo = new TeamUserVo();
BeanUtils.copyProperties(team,teamUserVo);
Long userId = team.getId();
final User user = userService.getById(userId);
if (user!=null) {
UserVo userVo = new UserVo();
BeanUtils.copyProperties(user,userVo);
teamUserVo.setCreateUser(userVo);
}
//查询队伍中的用户列表
List<User> userList = teamMapper.selectUserList(team.getId());
List<UserVo> userVoList = new ArrayList<>();
//用户脱敏
for (User user1 : userList) {
UserVo userVo = new UserVo();
BeanUtils.copyProperties(user1,userVo);
userVoList.add(userVo);
}
teamUserVo.setUserList(userVoList);
teamUserVoList.add(teamUserVo);
}
return teamUserVoList;
}
private boolean isAdmin(User loginUser){
//仅管理员可查询
/*
1:先获取登录状态
2:判断是否是管理员
*/
if(loginUser == null || loginUser.getRole()!= UserConstant.adminrole){
return false;
}
return true;
}
首先第一步:
我们要从TeamQuery中取出查询条件,一些常规的变量:id,name maxNum这些都差不多
拼到条件构造器里面就行
注意点:
1:有一个字段: 搜索关键词(同时对队伍名称和描述搜索) private String searchText;
我们理想的场景就是如果在name和描述中出现了这个searchText,那我们都把这个队伍搜索出来
这就涉及到了一个Mybatis-plus中条件构造器的语法:
queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText))
Mybatis-plus条件构造器:
qw ->
表示lambda表达式,引入一个新的QueryWrapper
对象qw
用于构建条件。qw.like("name", searchText)
表示在name
字段中进行模糊查询,查询条件为包含searchText
的记录。.or()
表示与前一个条件为“或”的关系。qw.like("description", searchText)
表示在description
字段中进行模糊查询,查询条件为包含searchText
的记录。所以这个条件就是:查询条件为
name
字段包含searchText
或者description
字段包含searchText
的记录
同理的还有下面的过期时间查询:
//排除已经过期的队伍 queryWrapper.and(qw -> qw.gt("expireTime", new Date()) .or().isNull("expireTime"));
这段代码逻辑就是,还没过期(过期时间在当前时间之后)的队伍和非空的队伍查出来
eq
(等于)、lt
(小于)、ge
(大于等于)、le
(小于等于)
接着第二步:
第二步的主要工作就是查询
包括根据我们上一步封装好的队伍列表来查队伍列表对应的队长(创建人)的信息:
具体步骤:
首先我们先遍历我们查询出来的队伍列表,遍历出每一个队伍
获取每一个队伍的userId(这个是我们创建好的字段,代表队长或者创建人)
再调用userService中根据id或者用户的方法获取用户
然后封装了一个UserVo来封装返回给前端的对象,这里也用到了BeanUtils这个工具类
最后把这个脱敏之后的UserVo装到teamUserVo中即可
还有一个就是根据队伍列表查找加入到这个队伍里面的成员
这里涉及到了一个联表查询
很明显队伍和用户是一个多对多的关系
这个联表查询有点复杂,我们先写SQL
select *
from user.team t
left join user.user_team ut on t.id = ut.teamId
left join user.user u on ut.userId = u.id
where t.id = 11;
这里直接在后面把队伍id写死了只是为了方便我们看数据
既然这个SQL语句已经很复杂了,我们用Mybatis-plus的接口就有点麻烦了,所以我们这里采用自己写SQL语句的办法
我们在teamMapper中新创建一个方法
然后在Mybatis的配置文件中:
<select id="selectUserList" resultType="com.usercenter.usercenterproject.Pojo.User">
select * from user.team t
left join user.user_team ut on t.id = ut.teamId
left join user.user u on ut.userId = u.id
<where>
<if test="id != null">
and t.id = #{id}
</if>
</where>
</select>
这里稍微复习一下这个配置文件的写法(太久没写,我自己都忘了)
1:首先是这个resultTyp:resultType
元素用于指定查询返回结果的数据类型。它表示查询结果映射到的 Java 对象类型
说白了就是返回值,我这里需要获取一个User对象,我就直接返回User对象
2:还有就是这两个标签:
<where>:where 元素只会在子元素有内容的情况下才插入where子句。而且会自动去除子句的开头的AND 或OR
<if>:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。
修改队伍需求分析:
具体的功能:
- 判断请求参数是否为空
- 查询队伍是否存在
- 只有管理员或者队伍的创建者可以修改
- 如果队伍状态改为加密,必须要有密码
- 更新
和之前操作已经大差不差了,先封装一个请求体TeamUpdateRequest
Controll层:
@PostMapping("/update")
public BaseResponse<Boolean> updateTeam(@RequestBody TeamUpdateRequest team, HttpServletRequest request){
if(team==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("修改队伍信息"+team);
final User loginuser = userService.getCurrent(request);
final boolean flag = teamService.updateTeam(team,loginuser);
if(!flag){
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
return ResultUtils.success(flag);
}
Service层:
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateTeam(TeamUpdateRequest team, User loginuser) {
if(team==null||loginuser==null){
throw new BusinessException(ErrorCode.NULL_ERROR);
}
final Long userId = team.getId();
if(!isAdmin(loginuser)&&userId!=loginuser.getId()){
throw new BusinessException(ErrorCode.NO_AUTH);
}
Team oldteam = this.getById(userId);
if (oldteam == null) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "查找队伍为空");
}
TeamStatusConstant value = TeamStatusConstant.getEnumByValue(team.getStatus());
if(value.equals(TeamStatusConstant.PRIVATE)){
if(StringUtils.isBlank(team.getPassword())){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"私密房间必须有密码");
}
}
Team newteam = new Team();
BeanUtils.copyProperties(team,newteam);
final boolean flag = this.updateById(newteam);
return flag;
}
用户加入队伍需求分析:
具体功能:
- 用户最多加入 5 个队伍
- 队伍必须存在,只能加入未满、未过期的队伍
- 不能加入自己的队伍,不能重复加入已加入的队伍(幂等性)
- 禁止加入私有的队伍
- 如果加入的队伍是加密的,必须密码匹配才可以
- 新增队伍 - 用户关联信息
我感觉这个接口的整体逻辑还有点复杂
照着具体功能来一个一个分析把
用户最多加入五个队伍:
我们用用户的id在关联表中count查询,算最后出来的值是否大于5
队伍必须存在,只能加入未满、未过期的队伍:
队伍必须存在直接用队伍的id查询判空即可
加入未满:需要去算这个队伍的人数是否大于这个队伍的maxNum值
队伍的人数怎么算呢
条件构造器拼接用户id和队伍id去关联表中count查询
未过期队伍:expiretime的时间在当前时间后面
不能加入自己的队伍,不能重复加入已加入的队伍(幂等性)
不能加入自己的队伍:这个只需要去判断队长的id和用户id是否相同即可
不能重复加入已加入的队伍:条件构造器拼接用户id和队伍id去关联表中count查询算出来的值等0说明你没有加入队伍,大于0,说明你已经加入队伍了
禁止加入私有的队伍:
这个只需要获取这个队伍的状态,判断即可
如果加入的队伍是加密的,必须密码匹配才可以
判断队伍是否加密只需要获取队伍状态
密码匹配用equals方法即可
Controll层:
@PostMapping("/join")
public BaseResponse<Boolean> joinTeam(@RequestBody TeamJoinRequest teamJoinRequest,HttpServletRequest request){
if(teamJoinRequest==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("用户加入队伍"+teamJoinRequest);
User loginuser = userService.getCurrent(request);
Boolean flag = teamService.joinTeam(teamJoinRequest,loginuser);
return ResultUtils.success(flag);
}
Service层:
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginuser) {
if(loginuser==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"当前用户为空");
}
final Long userId = loginuser.getId();
final Long teamId = teamJoinRequest.getId();
//校验用户加入队伍的个数
QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId",userId);
final long hasJoinTeam = userTeamService.count(queryWrapper);
if(hasJoinTeam > 5 ){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"最多加入五个队伍");
}
//校验队伍是否存在
final Team team = this.getById(teamId);
if(team==null){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"队伍不存在");
}
//校验加入队伍的人数
queryWrapper = new QueryWrapper<>();
queryWrapper.eq("teamId",teamId);
Long teamHasJoinNum = userTeamService.count(queryWrapper);
if(teamHasJoinNum>=team.getMaxNum()){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"队伍人数已满");
}
//校验队伍是否过期
final Date expireTime = team.getExpireTime();
if(expireTime==null||expireTime.before(new Date())){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"队伍已过期");
}
//校验是否加入自己的队伍
if(team.getUserId()==loginuser.getId()){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"不允许加入自己的队伍");
}
//校验是否重复加入已加入的队伍
queryWrapper = new QueryWrapper<>();
queryWrapper.eq("teamId",teamId);
queryWrapper.eq("userId",userId);
long hasUserJoinTeam = userTeamService.count(queryWrapper);
if (hasUserJoinTeam > 0){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户已加入该队伍");
}
//校验是否加入私有队伍
final TeamStatusConstant value = TeamStatusConstant.getEnumByValue(team.getStatus());
if(TeamStatusConstant.PRIVATE.equals(value)){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"禁止加入私有队伍");
}
final String password = team.getPassword();
if(TeamStatusConstant.SECRET.equals(value)){
if(password==null||!password.equals(teamJoinRequest.getPassword())){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"密码错误");
}
}
UserTeam userTeam = new UserTeam();
userTeam.setUserId(userId);
userTeam.setTeamId(teamId);
userTeam.setJoinTime(new Date());
return userTeamService.save(userTeam);
}
用户退出队伍:
具体功能:
- 校验请求参数
- 校验队伍是否存在
- 校验我是否已加入队伍
- 如果队伍只剩一人,队伍解散
- 如果队伍还有其他人,且退出的人是队长权限转移给第二早加入的用户 —— 先来后到
- 如果队伍还有其他人,且退出的人不是队长自己退出队伍
说几个比较难实现的功能把:
校验我是否已加入队伍:用条件构造器拼接用户id和队伍id去关联表中count查询,如果大于0,说明我在这个队伍里,反之不在,这和上面一样,只是意义相反
如果队伍只剩一人,队伍解散:
这主要就是算队伍人数:队伍人数条件构造器拼接队伍id去关联表中count查询即可
解散需要在队伍表中删除队伍数据,在关联表中删除关联数据。
如果队伍还有其他人,且退出的人是队长权限转移给第二早加入的用户 —— 先来后到:
如果退出的人是队长,首先我们需要去找到这个第二早加入的用户
怎么找呢?
看着两条数据,队伍14是用户6创建的,用户5是第二个加进来的,
我们就可以知道,你越早加进来,你的关联表中的id字段就越小
我们可以根据这个特点去查找id最小的两个人即可,
我们将id进行一个升序排序,然后去取最后两个即可
具体代码看下面
然后找到了这个第二早的用户,我们下一步就是把这个用户设置成队长,设置成队长就是更新嘛
最后再对这个队长进行逻辑删除即可
Controll
@PostMapping("/quit")
public BaseResponse<Boolean> quitTeam(@RequestBody TeamQuitRequest teamQuitRequest,HttpServletRequest request){
if(teamQuitRequest==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("用户退出队伍");
User loginuser = userService.getCurrent(request);
Boolean flag = teamService.quitTeam(teamQuitRequest,loginuser);
return ResultUtils.success(flag);
}
Service:
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginuser) {
if(loginuser==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"当前用户为空");
}
final Long teamId = teamQuitRequest.getTeamId();
final Long userid = loginuser.getId();
//检验队伍是否存在
final Team team = this.getById(teamId);
if(team==null){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"当前队伍为空");
}
//检验我是否已加入队伍
QueryWrapper<UserTeam> qw = new QueryWrapper<>();
qw.eq("userId",userid);
qw.eq("teamId",teamId);
final long count = userTeamService.count(qw);
//如果这里的count为0,说明我没有加入队伍
if(count==0){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"未加入队伍");
}
qw = new QueryWrapper<>();
qw.eq("teamId",teamId);
Long teamHasJoinNum = userTeamService.count(qw);//teamHasJoinNum 队伍剩余人数
if(teamHasJoinNum==1){
this.removeById(teamId);
}else{
if(userid==team.getUserId()){
//说明退出的人是队长
QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("teamId",teamId);
queryWrapper.last("order by id asc limit 2");
List<UserTeam> userTeamList = userTeamService.list(queryWrapper);
UserTeam userTeam = userTeamList.get(1);
Long newUserId = userTeam.getUserId();
//更换队长
Team newteam = new Team();
newteam.setId(userTeam.getTeamId());
newteam.setUserId(newUserId);
final boolean flag = this.updateById(newteam);
if(!flag){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"更换队长失败");
}
}
}
QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId",userid);
queryWrapper.eq("teamId",teamId);
return userTeamService.remove(queryWrapper);
}
队长解散队伍需求分析:
具体功能:
- 校验请求参数
- 校验队伍是否存在
- 校验你是不是队伍的队长
- 移除所有加入队伍的关联信息
- 删除队伍
这整体的功能就非常简单了
就判断你是否是队长,不是,抛异常,
是:就删除两张表的信息即可
Controll:
@DeleteMapping("/delete")
public BaseResponse<Boolean> deleteTeam(Long id,HttpServletRequest request){
if(id<0){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
log.info("队长删除队伍"+id);
User loginuser = userService.getCurrent(request);
final boolean flag = teamService.deleteTeam(id,loginuser);
if(!flag){
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
return ResultUtils.success(flag);
}
Service:
@Override
public Boolean deleteTeam(Long id, User loginuser) {
if(loginuser==null){
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
final Long userId = loginuser.getId();
final Team team = this.getById(id);
final Long CaptainuserId = team.getUserId();
//判断你是否是队长
if(CaptainuserId!=userId){
throw new BusinessException(ErrorCode.FORBIDDEN,"不是队长禁止操作");
}
//删除队伍
final boolean flag = this.removeById(team);
if(!flag){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"删除队伍失败");
}
return userTeamService.removeById(userId);
}