心链 — 伙伴匹配系统
接口设计
查询队伍列表
:::success
分页展示队伍列表,根据名称、最大人数等搜索队伍 P0,信息流中不展示已过期的队伍
- 从请求参数中取出队伍名称等查询条件,如果存在则作为查询条件
- 不展示已过期的队伍(根据过期时间筛选)
- 可以通过某个关键词同时对名称和描述查询
- 只有管理员才能查看加密还有非公开的房间
- 关联查询已加入队伍的用户信息
- 关联查询已加入队伍的用户信息(可能会很耗费性能,建议大家用自己写 SQL 的方式实现)todo
:::
为了保护数据不被暴露,所以我们要新建封装类
在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;
}
在TeamService里编写查询队伍方法并在TeamServiceImpl里实现
在TeamService里面实现listTeams方法并实现
编写业务层,其中只有管理员才能查看加密还有非公开的房间,所以我们需要从请求中获得是否为管理员
所以我们这边提前修改listTeams方法(鱼皮是在写的过程中发现要获得是否为管理员,才去修改)
@Overridepublic List<TeamUserVO> listTeams(TeamQuery teamQuery, boolean isAdmin) {QueryWrapper<Team> queryWrapper = new QueryWrapper<>();//组合查询条件if (teamQuery != null) {Long id = teamQuery.getId();if (id != null && id > 0) {queryWrapper.eq("id", id);}String searchText = teamQuery.getSearchText();if (StringUtils.isNotBlank(searchText)) {queryWrapper.and(qw -> qw.like("name", searchText).or().like("expireTime", 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("maxMum", maxNum);}Long userId = teamQuery.getUserId();//根据创建人来查询if (userId != null && userId > 0) {queryWrapper.eq("userId", userId);}//根据状态来查询Integer status = teamQuery.getStatus();TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);if (statusEnum == null) {statusEnum = TeamStatusEnum.PUBLIC;}if (!isAdmin && !statusEnum.equals(TeamStatusEnum.PUBLIC)) {throw new BusinessException(ErrorCode.NO_AUTH);}queryWrapper.eq("status", statusEnum.getValue());}//不展示已过期的队伍//expireTime is null or expireTime > now()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) {Long userId = team.getUserId();if (userId == null) {continue;}User user = userService.getById(userId);TeamUserVO teamUserVO = new TeamUserVO();BeanUtils.copyProperties(team, teamUserVO);//脱敏用户信息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);}
测试,打开knife4j接口,直接不带参数发送(等于查询全部)
修改队伍信息
:::success
- 判断请求参数是否为空
- 查询队伍是否存在
- 只有管理员或者队伍的创建者可以修改
- 如果用户传入的新值和老值一致,就不用 update 了(可自行实现,降低数据库使用次数)TODO
- 如果队伍状态改为加密,必须要有密码
- 更新成功
:::
在TeamService里编写修改队伍信息方法并在TeamServiceImpl里实现
同样在请求包里封装一个用户登录请求体
/*** 用户登录请求体** @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;
}
在TeamService里面实现updateTeam方法并实现
修改接口更改为
@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);}
编写实现类
@Overridepublic 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);if (oldTeam==null){throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在");}//只有管理员或者队伍的创建者才可以修改if (oldTeam.getUserId()!=loginUser.getId()&&!userService.isAdmin(loginUser)){throw new BusinessException(ErrorCode.NO_AUTH);}Team updateTeam = new Team();BeanUtils.copyProperties(teamUpdateRequest,updateTeam);return this.updateById(updateTeam);}
但是这段代码还有有bug,如果状态为公开,也可以修改信息(需要把密码清除),所以**如果队伍状态改为加密,必须要有密码,**修改实现类的代码,增加红框处校验
用户加入队伍
:::success
其他人、未满、未过期,允许加入多个队伍,但是要有个上限 P0
- 用户最多加入 5 个队伍
- 队伍必须存在,只能加入未满、未过期的队伍
- 不能加入自己的队伍,不能重复加入已加入的队伍(幂等性)
- 禁止加入私有的队伍
- 如果加入的队伍是加密的,必须密码匹配才可以
- 新增队伍 - 用户关联信息
注意,一定要加上事务注解!!!!
:::
在TeamService里编写用户加入队伍方法并在TeamServiceImpl里实现
同样在请求包里封装一个用户加入队伍请求体
/*** 用户加入队伍请求体** */
@Data
public class TeamJoinRequest implements Serializable {private static final long serialVersionUID = -24663018187059425L;/*** id*/private Long teamId;/*** 密码*/private String password;
}
在TeamService里面实现joinTeam方法并实现
编写用户加入队伍接口
@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);}
@Overridepublic 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 = this.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, "队伍已过期");}Integer status = team.getStatus();TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);if (teamStatusEnum.PRIVATE.equals(teamStatusEnum)) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "禁止加入私有队伍");}String password = teamJoinRequest.getPassword();if (teamStatusEnum.SECRET.equals(teamStatusEnum)) {if (StringUtils.isBlank(password) || !password.equals(team.getPassword())) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");}}//该用户已加入的队伍数量 数据库查询所以放到下面,减少查询时间Long userId = loginUser.getId();QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();userTeamQueryWrapper.eq("userId", userId);long hasJoinNum = userTeamService.count(userTeamQueryWrapper);if (hasJoinNum > 5) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多创建和加入5个队伍");}//不能重复加入已加入的队伍userTeamQueryWrapper = new QueryWrapper<>();userTeamQueryWrapper.eq("userId", userId);userTeamQueryWrapper.eq("teamId", teamId);long hasUserJoinTeam = userTeamService.count(userTeamQueryWrapper);if (hasUserJoinTeam > 0) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户已加入该队伍");}//已加入队伍的人数userTeamQueryWrapper = new QueryWrapper<>();userTeamQueryWrapper.eq("teamId", teamId);long teamHasJoinNum = userTeamService.count(userTeamQueryWrapper);if (teamHasJoinNum >= team.getMaxNum()) {throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已满");}//修改队伍信息UserTeam userTeam = new UserTeam();userTeam.setUserId(userId);userTeam.setTeamId(teamId);userTeam.setJoinTime(new Date());return userTeamService.save(userTeam);}
换个用户测试加入队伍
数据库中队伍1有两个用户id,测试成功