组队功能开发(伙伴匹配系统)

理想的应用场景:

我需要去创建一个队伍,这也是伙伴匹配项目的核心,别人可以加入队伍一起做某一件事情。

添加队伍需求分析:

具体的功能:

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

  1. 队长、剩余的人数
  2. 聊天?
  3. 公开 或 private 或加密
  4. 用户创建队伍最多 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. 请求参数是否为空?
  2. 是否登录,未登录不允许创建
  3. 校验信息
    1. 队伍人数 > 1 且 <= 20
    2. 队伍标题 <= 20
    3. 描述 <= 512
    4. status 是否公开(int)不传默认为 0(公开)
    5. 如果 status 是加密状态,一定要有密码,且密码 <= 32
    6. 超时时间 > 当前时间
    7. 校验用户最多创建 5 个队伍
  4. 插入队伍信息到队伍表
  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

这才测试成功

查询队伍需求分析:

具体的功能:

  1. 从请求参数中取出队伍名称等查询条件,如果存在则作为查询条件
  2. 不展示已过期的队伍(根据过期时间筛选)
  3. 可以通过某个关键词同时对名称和描述查询
  4. 只有管理员才能查看加密还有非公开的房间
  5. 关联查询已加入队伍的用户信息
  6. 关联查询已加入队伍的用户信息(可能会很耗费性能,建议大家用自己写 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。

修改队伍需求分析:

具体的功能:

  1. 判断请求参数是否为空
  2. 查询队伍是否存在
  3. 只有管理员或者队伍的创建者可以修改
  4. 如果队伍状态改为加密,必须要有密码
  5. 更新

和之前操作已经大差不差了,先封装一个请求体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;
    }

用户加入队伍需求分析:

具体功能:

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

我感觉这个接口的整体逻辑还有点复杂

照着具体功能来一个一个分析把

用户最多加入五个队伍:

我们用用户的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);
    }

用户退出队伍:

具体功能:

  1. 校验请求参数
  2. 校验队伍是否存在
  3. 校验我是否已加入队伍
  4. 如果队伍只剩一人,队伍解散
  5. 如果队伍还有其他人,且退出的人是队长权限转移给第二早加入的用户 —— 先来后到
  6. 如果队伍还有其他人,且退出的人不是队长自己退出队伍

 说几个比较难实现的功能把:

校验我是否已加入队伍:用条件构造器拼接用户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);
    }

队长解散队伍需求分析:

具体功能:

  1. 校验请求参数
  2. 校验队伍是否存在
  3. 校验你是不是队伍的队长
  4. 移除所有加入队伍的关联信息
  5. 删除队伍

这整体的功能就非常简单了

就判断你是否是队长,不是,抛异常,

是:就删除两张表的信息即可

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);
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值