文章目录
- 需求分析
- 数据库表设计
- 基础接口开发
- 系统设计及开发
- 创建队伍
- 业务逻辑
- 细化业务层代码
- 优化完成控制层
- 查询队伍列表
- 业务逻辑
- vo层
- 业务层代码
- 接口代码
- 修改队伍信息
- 业务逻辑
- 同样在请求包里封装一个用户登录请求体
- 接口修改
- 业务实现类
- 用户可以加入队伍
- 同样在请求包里封装一个用户加入队伍请求体
- 接口实现
- 业务层实现
- 用户退出队伍
- 业务逻辑
- 新建退出请求体
- 接口实现
- 业务实现
- 队长解散队伍
- 业务逻辑
- 接口实现
- 业务实现
- 获取当前用户已加入的队伍
- 获取当前用户已创建的队伍
- 遇到的bug:循环依赖问题 todo
需求分析
用户可以创建队伍,设置队伍的人数,队伍名称,描述,超时时间
用户可以加入队伍(其他人,未满,未过期)
用户可以退出队伍(队长退出,权限转移给第二早加入的用户)
创建人可以修改队伍信息
队长可以解散队伍
用户可以分享队伍
数据库表设计
队伍表 team
字段:
- id 主键 bigint
- name 队伍名称
- description 描述
- maxNum 最大人数
- expireTime 过期时间
- userId 创建人id
- status 0-公开,1-私有,2-加密
- password密码
- createTime
- updateTime
- isDelete
-- 队伍表
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(队长 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 '队伍';
两个关系:
- 用户加了哪些队伍
- 队伍里有哪些用户
方式:
- 建立用户-队伍关系表 teamId userId(便于修改,查询性能高,不用全表遍历)
- 用户表补充已加入队伍,队伍表补充已加入的用户字段 (便于查询,不用写多对多的代码,可以直接根据队伍查用户,根据用户查队伍)
用户-队伍表 user_team
字段:
- id主键
- userId 用户id
- teamId 队伍id
- joinTime 加入时间
- createTime
- updateTime
- isDelete
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 '用户队伍关系';
基础接口开发
:::info
为什么需要请求参数包装类?
:::
- 请求参数名称/类型和实体类不一样
- 有一些参数用不到,如果要自动生成接口文档,会增加理解成本
- 多个实体类映射到同一个对象
:::info
为什么需要包装类?
:::
有些字段需要隐藏,不能返回给前端
或者有些字段某些方法是不关心的
在model.dto下新建一个类
@Data
public class TeamQuery {
/**
* id
*/
private Long id;
/**
* 队伍名称
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 最大人数
*/
private Integer maxNum;
/**
* 用户id
*/
private Long userId;
/**
* 0 - 公开,1 - 私有,2 - 加密
*/
private Integer status;
}
在common包下新建分页请求参数包装类
@Data
public class PageRequest implements Serializable {
private static final long serialVersionUID = -4162304142710323660L;
/**
* 页面大小
*/
protected int pageSize;
/**
* 当前是第几页
*/
protected int pageNum;
}
同时在TeamQuery上继承这个分页请求,后面接口会用上
add添加队伍,delete删除队伍,update更新队伍,list查询,list/page分页查询
先硬写增删改查 —— > 细化逻辑
@PostMapping("/add")
public BaseResponse<Long> addTeam(@RequestBody Team team) {
if (team == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
boolean save = teamService.save(team);
if (!save) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "添加失败");
}
return ResultUtils.success(team.getId());
}
@PostMapping("/delete")
public BaseResponse<Boolean> deleteTeam(@RequestBody Long id) {
if (id <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
boolean result = teamService.removeById(id);
if (!result) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除失败");
}
return ResultUtils.success(true);
}
@PostMapping("/update")
public BaseResponse<Boolean> updateTeam(@RequestBody Team team) {
if (team == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
boolean result = teamService.updateById(team);
if (!result) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新失败");
}
return ResultUtils.success(true);
}
@GetMapping("/get")
public BaseResponse<Team> getTeamById(Long id) {
if (id <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Team team = teamService.getById(id);
if (team == null) {
throw new BusinessException(ErrorCode.NULL_ERROR);
}
return ResultUtils.success(team);
}
/**
* 前端闯入一些信息,可以查询到与这些信息相匹配的队伍信息
* @param teamQuery
* @return
*/
@GetMapping("/list")
public BaseResponse<List<Team>> listTeams(TeamQuery teamQuery) {
if (teamQuery == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Team team = new Team();
BeanUtils.copyProperties(teamQuery,team);
QueryWrapper<Team> queryWrapper = new QueryWrapper<>(team);
List<Team> teamList = teamService.list(queryWrapper);
return ResultUtils.success(teamList);
}
@GetMapping("/list/page")
public BaseResponse<Page<Team>> listTeamsByPage(TeamQuery teamQuery){
if(teamQuery == null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Team team = new Team();
BeanUtils.copyProperties(teamQuery,team);
QueryWrapper<Team> teamQueryWrapper = new QueryWrapper<>(team);
int current = teamQuery.getPageNum();
int pageSize = teamQuery.getPageSize();
Page<Team> page = new Page<>(current,pageSize);
Page<Team> pageTeams = teamService.page(page, teamQueryWrapper);
return ResultUtils.success(pageTeams);
}
系统设计及开发
创建队伍
业务逻辑
细化业务层代码
@Override
@Transactional(rollbackFor = Exception.class)
public Long addTeam(Team team, User loginUser) {
//1.请求参数是否为空
if (team == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
//2.是否登录,未登录不允许创建
if (loginUser == null) {
throw new BusinessException(ErrorCode.NOT_LOGIN);
}
//3.检验信息
//(1).队伍人数>1且<=20
int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
if (maxNum <= 1 || maxNum > 20) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数不符合要求");
}
//(2).队伍标题 <=20
String name = team.getName();
if (name.length() > 20) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题过长");
}
// 3. 描述<= 512
String description = team.getDescription();
if (description.length() > 0 && description.length() > 512) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长");
}
//4.status 是否公开,不传默认为0
Integer status = Optional.ofNullable(team.getStatus()).orElse(0);
TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
if (teamStatusEnum == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求");
}
//5.如果status是加密状态,一定要密码 且密码<=32
String password = team.getPassword();
if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
if (StringUtils.isBlank(password) || password.length() > 32) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码设置不正确");
}
}
//6.超出时间 > 当前时间
Date expireTime = team.getExpireTime();
if (new Date().after(expireTime)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间 > 过期时间");
}
//7.校验用户最多创建5个队伍
//todo 有bug。可能同时创建100个队伍
Long userId = loginUser.getId();
QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", userId);
long count = this.count(queryWrapper);
if (count >= 5) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建5个队伍");
}
//8.插入队伍消息到队伍表
team.setId(null);
team.setUserId(userId);
boolean save = this.save(team);
if (!save) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "创建队伍失败");
}
//9. 插入用户 ==> 队伍关系 到关系表
UserTeam userTeam = new UserTeam();
Long teamId = team.getId();
userTeam.setTeamId(teamId);
userTeam.setUserId(userId);
userTeam.setJoinTime(new Date());
boolean result = userTeamService.save(userTeam);
if(!result){
throw new BusinessException(ErrorCode.SYSTEM_ERROR,"创建队伍失败");
}
return teamId;
}
优化完成控制层
这里我们因为完善了业务层,所以在controller层可以简便下代码,我们需要新建一个队伍添加请求封装类(便于前端知道该输入哪些参数) 新的请求封装类位于model包里的request包
@Data
public class TeamAddRequest {
private static final long serialVersionUID = -4162304142710323660L;
/**
* 队伍名称
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 最大人数
*/
private Integer maxNum;
/**
* 过期时间
*/
private Date expireTime;
/**
* 用户id
*/
private Long userId;
/**
* 0 - 公开,1 - 私有,2 - 加密
*/
private Integer status;
/**
* 密码
*/
private String password;
}
修改的addTeam接口如下:
@PostMapping("/add")
public BaseResponse<Long> addTeam(@RequestBody TeamAddRequest teamAddRequest, HttpServletRequest request) {
if (teamAddRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Team team = new Team();
BeanUtils.copyProperties(teamAddRequest,team);
User loginUser = userService.getLoginUser(request);
Long teamId = teamService.addTeam(team, loginUser);
return ResultUtils.success(teamId);
}
查询队伍列表
业务逻辑
分页展示队伍的列表,根据名称,最大人数搜索队伍,信息流中不展示已过期的队伍
- 从请求参数中取出队伍名称,如果存在则作为查询条件
- 不展示已过期队伍
- 可以通过某个关键词同时对名称和描述进行查询
- 只有管理员才能查看加密还有未公开的房间
- 关联查询已加入队伍的用户信息
关联查询是什么来着???(数据库知识又忘了)
vo层
是返回给前端的数据封装类(脱敏)
在model包新建vo包,并创建TeamUserVO(队伍和用户信息封装类),UserVO类(用户封装类),这两个类是返回给前端看
/**
* 队伍和用户信息封装类(脱敏)
*
* @author yupi
*/
@Data
public class TeamUserVO implements Serializable {
private static final long serialVersionUID = 163478861968488713L;
/**
* id
*/
private Long id;
/**
* 队伍名称
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 最大人数
*/
private Integer maxNum;
/**
* 过期时间
*/
private Date expireTime;
/**
* 用户id
*/
private Long userId;
/**
* 0 - 公开,1 - 私有,2 - 加密
*/
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
/**
* 创建人用户信息
*/
private UserVO createUser;
/**
* 已加入的用户数
*/
private Integer hasJoinNum;
/**
* 是否已加入队伍
*/
private boolean hasJoin = false;
}
/**
* 用户包装类(脱敏)
*/
@Data
public class UserVO implements Serializable {
/**
* id
*/
private long id;
/**
* 用户昵称
*/
private String username;
/**
* 账号
*/
private String userAccount;
/**
* 用户头像
*/
private String avatarUrl;
/**
* 性别
*/
private Integer gender;
/**
* 电话
*/
private String phone;
/**
* 邮箱
*/
private String email;
/**
* 标签列表 json
*/
private String tags;
/**
* 状态 0 - 正常
*/
private Integer userStatus;
/**
* 创建时间
*/
private Date createTime;
/**
*
*/
private Date updateTime;
/**
* 用户角色 0 - 普通用户 1 - 管理员
*/
private Integer userRole;
/**
* 星球编号
*/
private String planetCode;
private static final long serialVersionUID = 1L;
}
(todo)问:dto与vo有什么区别?
vo是封装了返回给前端的数据对象,是为了防止数据暴露
dto是封装了请求参数对象,跟request包下一样,是前端需要用到的,前端拿到这个可以看自己需要用什么
业务层代码
@Override
public List<TeamUserVO> listTeams(TeamQuery teamQuery, boolean isAdmin) {
if(teamQuery == null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
String searchText = teamQuery.getSearchText();
if(StringUtils.isNotBlank(searchText)){
queryWrapper.and(qw -> qw.like("name",searchText).or().like("description",searchText));
}
String name = teamQuery.getName();
if(StringUtils.isNotBlank(name)){
queryWrapper.like("name",name);
}
String description = teamQuery.getDescription();
if(StringUtils.isNotBlank(description)){
queryWrapper.like("description",description);
}
Integer maxNum = teamQuery.getMaxNum();
if(maxNum != null && maxNum > 0){
queryWrapper.eq("maxNum",maxNum);
}
Long userId = teamQuery.getUserId();
if(userId != null && userId > 0) {
queryWrapper.eq("userId",userId);
}
//状态校验
Integer status = teamQuery.getStatus();
TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
if(teamStatusEnum == null){
teamStatusEnum = TeamStatusEnum.PUBLIC;
}
if(!isAdmin && !TeamStatusEnum.PUBLIC.equals(teamStatusEnum)){
throw new BusinessException(ErrorCode.NO_AUTH);
}
queryWrapper.eq("status",teamStatusEnum.getValue());
//不展示已过期的队伍
//要求查询结果中的 "expireTime" 字段的值要么大于当前时间(未过期),要么为空(未设置过期时间)。
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){
//关联查询创建人的用户信息?
userId = team.getUserId();
if(userId == null){
continue;
}
TeamUserVO teamUserVO = new TeamUserVO();
BeanUtils.copyProperties(team,teamUserVO);
User user = userService.getUserById(userId);
//脱敏信息
if(user != null){
UserVO userVO = new UserVO();
BeanUtils.copyProperties(user,userVO);
teamUserVO.setCreateUser(userVO);
}
teamUserVOList.add(teamUserVO);
}
return teamUserVOList;
}
}
接口代码
@GetMapping("/list")
public BaseResponse<List<TeamUserVO>> listTeams(TeamQuery teamQuery,HttpServletRequest request) {
if (teamQuery == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
boolean isAdmin = userService.isAdmin(request);
List<TeamUserVO> teamList = teamService.listTeams(teamQuery,isAdmin);
return ResultUtils.success(teamList);
}
在这里记录一下,自己第一次debug找出错误的经历,什么都不传应该是全部都能查出来的,但是却什么都没有查出来,之前一直不会看,这一个好好分析了一下,用快捷键一步一步往下走,发现到这里的时候status为null,还去数据库查询了,应该是查teamStatusEnum.getValue()才对,把status改为teamStatusEnum.getValue()就ok了
修改队伍信息
业务逻辑
- 请求参数是否为空
- 查询队伍是否存在
- 只有管理员和队伍的创建者可以修改
- 用户传入的新值和老值一致,就不用update了(可自行实现,降低数据库使用次数)
- 如果队伍状态为加密,必须要有密码才能修改
- 更新成功
同样在请求包里封装一个用户登录请求体
/**
* 用户登录请求体
*
* @author shaosao
*/
@Data
public class TeamUpdateRequest implements Serializable {
private static final long serialVersionUID = -6043915331807008592L;
/**
* id
*/
private Long id;
/**
* 队伍名称
*/
private String name;
/**
* 描述
*/
private String description;
/**
* 过期时间
*/
private Date expireTime;
/**
* 0 - 公开,1 - 私有,2 - 加密
*/
private Integer status;
/**
* 密码
*/
private String password;
}
接口修改
@PostMapping("/update")
public BaseResponse<Boolean> updateTeam(@RequestBody TeamUpdateRequest teamUpdateRequest,HttpServletRequest request) {
if (teamUpdateRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
boolean result = teamService.updateTeam(teamUpdateRequest,loginUser);
if (!result) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新失败");
}
return ResultUtils.success(true);
}
业务实现类
@Override
public boolean updateTeam(TeamUpdateRequest teamUpdateRequest,User loginUser) {
if (teamUpdateRequest == null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Long id = teamUpdateRequest.getId();
if(id == null || id < 0){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Team oldTeam = this.getById(id);
boolean isAdmin = userService.isAdmin(loginUser);
if(oldTeam.getUserId() != loginUser.getId() && !isAdmin){
throw new BusinessException(ErrorCode.NO_AUTH);
}
String password = teamUpdateRequest.getPassword();
Integer status = teamUpdateRequest.getStatus();
TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
if(statusEnum.equals(TeamStatusEnum.SECRET)){
if(StringUtils.isBlank(password)){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"加密房间必须要设置密码");
}
}
Team team = new Team();
BeanUtils.copyProperties(teamUpdateRequest,team);
return this.updateById(team);
}
用户可以加入队伍
- 用户最多加入5个队伍 todo
- 只能加入未满,未过期
- 不能重复加入已加入的队伍(幂等性)
- 如果加入的队伍是加密的,必须密码匹配才可以
- 禁止加入私有的队伍
- 新增队伍-用户关联信息
(数据库查询操作放在后面,节省性能)
todo 注意并发请求时可能会出现问题
同样在请求包里封装一个用户加入队伍请求体
/**
* 用户加入队伍请求体
*
* @author shaosao
*/
@Data
public class TeamJoinRequest implements Serializable {
private static final long serialVersionUID = -24663018187059425L;
/**
* id
*/
private Long teamId;
/**
* 密码
*/
private String password;
}
接口实现
@PostMapping("/join")
public BaseResponse<Boolean> joinTeam(@RequestBody TeamJoinRequest teamJoinRequest,HttpServletRequest request){
if(teamJoinRequest == null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUSer = userService.getLoginUser(request);
boolean result = teamService.joinTeam(teamJoinRequest,loginUSer);
return ResultUtils.success(result);
}
业务层实现
@Override
public boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUSer) {
if (teamJoinRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Long teamId = teamJoinRequest.getTeamId();
if(teamId == null || teamId <= 0){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Team team = teamService.getById(teamId);
if (team == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");
}
//用户只能加入未过期的队伍
Date expireTime = team.getExpireTime();
if (expireTime != null && expireTime.before(new Date())) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户只能加入未过期的队伍");
}
//如果加入的队伍是加密的,必须密码匹配才可以
String password = teamJoinRequest.getPassword();
Integer status = team.getStatus();
TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
if (TeamStatusEnum.SECRET.equals(statusEnum)) {
if (StringUtils.isBlank(password) || !password.equals(team.getPassword())) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
}
}
//禁止加入私有的队伍
if (TeamStatusEnum.PRIVATE.equals(statusEnum)) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "禁止加入私有队伍");
}
//用户最多加入五个队伍
Long userId = loginUSer.getId();
QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
userTeamQueryWrapper.eq("userId", userId);
long count = userTeamService.count(userTeamQueryWrapper);
if (count > 5) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多创建和加入五个队伍");
}
//用户只能加入未满的队伍
userTeamQueryWrapper = new QueryWrapper<>();
userTeamQueryWrapper.eq("teamId", teamId);
count = userTeamService.count(userTeamQueryWrapper);
if (count >= team.getMaxNum()) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已满");
}
//.不能重复加入已加入的队伍(幂等性)
userTeamQueryWrapper = new QueryWrapper<>();
userTeamQueryWrapper.eq("userId", userId);
userTeamQueryWrapper.eq("teamId", teamId);
count = userTeamService.count(userTeamQueryWrapper);
if(count > 0){
throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户已加入该队伍");
}
//新增用户-队伍关系
UserTeam userTeam = new UserTeam();
userTeam.setUserId(userId);
userTeam.setTeamId(teamId);
boolean save = userTeamService.save(userTeam);
return save;
}
用户退出队伍
业务逻辑
新建退出请求体
@Data
public class TeamQuitRequest implements Serializable {
private static final long serialVersionUID = -2038884913144640407L;
/**
* id
*/
private Long teamId;
}
接口实现
@PostMapping("/quit")
public BaseResponse<Boolean> quitTeam(@RequestBody TeamQuitRequest teamQuitRequest,HttpServletRequest request){
if(teamQuitRequest == null){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
boolean result = teamService.quitTeam(teamQuitRequest,loginUser);
return ResultUtils.success(result);
}
业务实现
@Override
@Transactional(rollbackFor = Exception.class)
public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
if (teamQuitRequest == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Long teamId = teamQuitRequest.getTeamId();
//缓解缓存穿透问题
if (teamId == null || teamId <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Team team = getById(teamId);
if (team == null) {
throw new BusinessException(ErrorCode.NULL_ERROR, "队伍不存在");
}
//校验是否已经加入队伍,只能退出已加入队伍
Long userId = loginUser.getId();
UserTeam userTeam = new UserTeam();
userTeam.setUserId(userId);
userTeam.setTeamId(teamId);
QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>(userTeam);
long count = userTeamService.count(queryWrapper);
if (count == 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR, "未加入该队伍");
}
//校验队伍人数
QueryWrapper TqueryWrapper = new QueryWrapper<>();
TqueryWrapper.eq("teamId", teamId);
count = userTeamService.count(TqueryWrapper);
//1.队伍只剩一人
if (count == 1) {
this.removeById(teamId);
return userTeamService.remove(queryWrapper);
} else {
//2.队伍还剩其他人
//2.1是队长
if (team.getUserId().equals(userId)) {
//权限转移给第二早加入的用户
//1.查询前两个最早加入用户
QueryWrapper<UserTeam>userTeamQueryWrapper = new QueryWrapper<>();
userTeamQueryWrapper.eq("teamId", teamId);
userTeamQueryWrapper.last("order by id asc limit 2");
List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
if (CollectionUtils.isEmpty(userTeamList) || userTeamList.size() <= 1) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
//权限转移给第二个用户
userTeam = userTeamList.get(1);
Team updateTeam = new Team();
//更新队伍表
updateTeam.setUserId(userTeam.getUserId());
updateTeam.setId(teamId);
boolean result = this.updateById(updateTeam);
if (!result) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新队长失败");
}
//把之前队伍-用户关系表删除
return userTeamService.remove(queryWrapper);
} else {
//2.2不是队长
return userTeamService.remove(queryWrapper);
}
}
}
队长解散队伍
业务逻辑
- 校验请求参数(队伍id)
- 校验队伍是否存在
- 校验你是不是队伍的队长
- 移出所有加入队伍的关联信息
- 删除队伍
接口实现
@PostMapping("/delete")
public BaseResponse<Boolean> deleteTeam(@RequestBody Long id,HttpServletRequest request) {
if (id <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
boolean result = teamService.deleteTeam(id,loginUser);
if (!result) {
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除失败");
}
return ResultUtils.success(true);
}
业务实现
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteTeam(Long id, User loginUser) {
if(id == null || id <= 0){
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
Team team = this.getById(id);
if(team == null){
throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在");
}
Long userId = loginUser.getId();
if(!team.getUserId().equals(userId)){
throw new BusinessException(ErrorCode.NO_AUTH,"无访问权限");
}
UserTeam userTeam = new UserTeam();
userTeam.setTeamId(id);
QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>(userTeam);
boolean result = userTeamService.remove(queryWrapper);
if(!result){
throw new BusinessException(ErrorCode.SYSTEM_ERROR);
}
boolean ans = teamService.removeById(id);
return ans;
}
获取当前用户已加入的队伍
(开复用 listTeam 方法,只新增查询条件,不做修改,开闭原则)
/**
* 获取我创建的队伍
*
* @param teamQuery
* @param request
* @return
*/
@GetMapping("/list/my/create")
public BaseResponse<List<TeamUserVO>> listMyCreateTeams(TeamQuery teamQuery, HttpServletRequest request) {
if (teamQuery == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
teamQuery.setUserId(loginUser.getId());
List<TeamUserVO> teamList = teamService.listTeams(teamQuery, true);
return ResultUtils.success(teamList);
}
获取当前用户已创建的队伍
我们查询加入的队伍需要用到id的列表,所以在Teamquery里增加idList字段
获取我加入的队伍
/**
* 获取我加入的队伍
*
* @param teamQuery
* @param request
* @return
*/
@GetMapping("/list/my/join")
public BaseResponse<List<TeamUserVO>> listMyJoinTeams(TeamQuery teamQuery, HttpServletRequest request) {
if (teamQuery == null) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
User loginUser = userService.getLoginUser(request);
QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("userId", loginUser.getId());
List<UserTeam> userTeamList = userTeamService.list(queryWrapper);
// 取出不重复的队伍 id
// teamId userId
Map<Long, List<UserTeam>> listMap = userTeamList.stream()
.collect(Collectors.groupingBy(UserTeam::getTeamId));
List<Long> idList = new ArrayList<>(listMap.keySet());
teamQuery.setIdList(idList);
List<TeamUserVO> teamList = teamService.listTeams(teamQuery, true);
return ResultUtils.success(teamList);
}
修改下listTeam方法,加一层校验
遇到的bug:循环依赖问题 todo
(springboot 2.6出现的问题)
todo 什么是循环依赖?可以结合bean的生命周期去理解
解决方案:spring.main.allow-circular-references=true 设置自动打破循环