组队功能开发

需求分析:

理想的应用场景:

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


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

直接给我报了一个日期格式不匹配的错误

后面问了GPT

把过期时间改成了2024-07-14

这才测试成功

Cannot deserialize value of type `java.util.Date` from String "2024 7 14":

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue和Spring Boot是两个不同的技术栈,Vue用于前端开发,Spring Boot用于后端开发。因此,组队功能需要同时涉及到前端和后端的开发。以下是一个基本的组队功能开发流程: 1. 前端开发人员使用Vue框架开发用户界面,包括登录、注册、创建组队、加入组队功能。前端开发人员需要使用Vue Router进行页面跳转,使用Axios发送HTTP请求与后端进行交互。 2. 后端开发人员使用Spring Boot框架开发RESTful API,包括用户认证、组队管理、成员管理等功能。后端开发人员需要使用Spring Security进行用户认证和授权,使用Spring Data JPA进行数据库操作。 3. 前后端通过HTTP协议进行通信。前端发送请求到后端API,后端返回JSON格式的数据。前端根据返回的数据进行页面渲染。 4. 数据库需要设计用户表和组队表,用户表用于存储用户信息,组队表用于存储组队信息。组队表需要包含组队名称、组队描述、组队成员等字段。 5. 在开发过程中需要注意安全性问题,例如防止SQL注入、XSS攻击等。 6. 开发完成后需要进行测试,包括单元测试和集成测试。对于组队功能,需要测试用户登录、创建组队、加入组队、退出组队等场景。 总之,开发组队功能需要前后端开发人员协同工作,需要对Vue和Spring Boot框架有一定的了解。同时需要注意安全性和测试等问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值