心链9----组队功能开发以及请求参数包装类和包装类实现

心链 — 伙伴匹配系统

组队功能开发

需求分析

理想的应用场景

我要跟别人一起参加竞赛或者做项目,可以发起队伍或者加入别人的队伍

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

  1. 队长、剩余的人数
  2. 聊天?
  3. 公开 或 private 或加密
  4. 用户创建队伍最多 5 个

展示队伍列表,根据名称搜索队伍  P0,信息流中不展示已过期的队伍
修改队伍信息 P0 ~ P1
用户可以加入队伍(其他人、未满、未过期),允许加入多个队伍,但是要有个上限  P0
是否需要队长同意?筛选审批?
用户可以退出队伍(如果队长 退出,权限转移给第二早加入的用户 —— 先来后到) P1
队长可以解散队伍 P0

分享队伍 =》 邀请其他用户加入队伍 P1
业务流程:

  1. 生成分享链接(分享二维码)
  2. 用户访问链接,可以点击加入

队伍人满后发送消息通知 P1

数据库表设计

队伍表 team
字段:

  • id 主键 bigint(最简单、连续,放 url 上比较简短,但缺点是爬虫)
  • 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',
  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 '队伍';

用户 - 队伍表 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 '用户队伍关系';

两个关系:

  1. 用户加了哪些队伍?
  2. 队伍有哪些用户?

方式:

  1. 建立用户 - 队伍关系表 teamId userId(便于修改,查询性能高一点,可以选择这个,不用全表遍历)
  2. 用户表补充已加入的队伍字段,队伍表补充已加入的用户字段(便于查询,不用写多对多的代码,可以直接根据队伍查用户、根据用户查队伍)

后端代码

实体生成

实体生成team和user-team
使用MybatisX-Generator生成domain,service和mapper文件,然后把生成的文件都移到对应的目录里面,别忘了把mapper.xml里的路径改成自己对应的。

如果直接将生成的文件拉到对应的文件,就会自动修改mapper.xml的路径

PS:别忘了在team和user_team类中的is_delete字段添加@TableLogic注解,实现逻辑删除
image.png

队伍controller接口

①增删改查
②PageRequest(序列化)---- TeamQuery继承
③自己测试 http://localhost:8080/api/doc.html#/home

@RestController
@RequestMapping("/user")
@CrossOrigin(origins = {"http://localhost:5173/"})
@Slf4j
public class TeamController {

        @Resource
        private UserService userService;

        @Resource
        private RedisTemplate redisTemplate;

        @Resource
        private TeamService teamService;

        @PostMapping("/add")
        public BaseResponse<Long> addTeam(@RequestBody Team team){
            if (team == null){
                throw new BusinessException(ErrorCode.NULL_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.NULL_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);
        }


        @GetMapping("/list")
        public BaseResponse<List<Team>> listTeams(TeamQuery teamQuery) {
            if (teamQuery == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
                Team team = new Team();
                BeanUtils.copyProperties(team, teamQuery);
                QueryWrapper<Team> queryWrapper = new QueryWrapper<>(team);
                List<Team> teamList = teamService.list(queryWrapper);
                return ResultUtils.success(teamList);
            }
    
              @GetMapping("/list/page")
        public BaseResponse<Page<Team>> listPageTeams(TeamQuery teamQuery) {
            if (teamQuery == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR);
            }
            Team team = new Team();
            BeanUtils.copyProperties(teamQuery, team);
            Page<Team> page = new Page<>(teamQuery.getPageNum(),teamQuery.getPageSize());
            QueryWrapper<Team> queryWrapper = new QueryWrapper<>(team);
            Page<Team> resultPage = teamService.page(page,queryWrapper);
            return ResultUtils.success(resultPage);
        }
            }

:::success

这边我们需要新建请求参数包装类和包装类,原因如下:
为什么需要请求参数包装类?
  1. 请求参数名称 / 类型和实体类不一样
  2. 有一些参数用不到,如果要自动生成接口文档,会增加理解成本
  3. 对个实体类映射到同一个对象
为什么需要包装类?

可能有些字段需要隐藏,不能返回给前端
或者有些字段某些方法是不关心的

在model包里新建一个dto包,写一个包装类TeamQuery
:::

/**
 * 队伍查询封装类
 * @TableName team
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class TeamQuery extends PageRequest {
    /**
     * id
     */
    private Long id;

    /**
     * id 列表
     */
    private List<Long> idList;

    /**
     * 搜索关键词(同时对队伍名称和描述搜索)
     */
    private String searchText;

    /**
     * 队伍名称
     */
    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;
}

接口系统设计

1、创建队伍

用户可以 创建 一个队伍,设置队伍的人数、队伍名称(标题)、描述、超时时间 P0
队长、剩余的人数
聊天?
公开 或 private 或加密
信息流中不展示已过期的队伍

  1. 请求参数是否为空?
  2. 是否登录,未登录不允许创建
  3. 校验信息
    1. 队伍人数 > 1 且 <= 20
    2. 队伍标题 <= 20
    3. 描述 <= 512
    4. status 是否公开(int)不传默认为 0(公开)
    5. 如果 status 是加密状态,一定要有密码,且密码 <= 32
    6. 超时时间 > 当前时间
    7. 校验用户最多创建 5 个队伍
  4. 插入队伍信息到队伍表
  5. 插入用户 => 队伍关系到关系表
    @PostMapping("/add")
    public BaseResponse<Long> addTeam(@RequestBody TeamAddRequest teamAddRequest, HttpServletRequest request){
        if (teamAddRequest == null){
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        User logininUser = userService.getLogininUser(request);
        Team team = new Team();
        BeanUtils.copyProperties(teamAddRequest,team);
        long teamId = teamService.addTeam(team,logininUser);
        return ResultUtils.success(teamId);
    }

public interface TeamService extends IService<Team> {

    /**
     *   添加队伍
     * @param team
     * @param loginUser
     * @return
     */
    long addTeam(Team team, User loginUser);
}
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
    implements TeamService{

    @Resource
    UserTeamService userTeamService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    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
        Integer maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (maxNum < 1 || maxNum >20){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍人数不符合要求");
        }
        //  b. 队伍标题 <= 20
        String name = team.getName();
        if (StringUtils.isBlank(name) || name.length() > 20) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题不满足要求");
        }
        //  c. 描述 <= 512
        String description = team.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长");
        }
        //  d. status 是否公开(int)不传默认为 0(公开)
        int status = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if (statusEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求");
        }
        //  e. 如果 status 是加密状态,一定要有密码,且密码 <= 32
        String password = team.getPassword();
        if (TeamStatusEnum.SECRET.equals(statusEnum)) {
            if (StringUtils.isBlank(password) || password.length() > 32) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码设置不正确");
            }
        }
        //  f. 超时时间 > 当前时间
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间 > 当前时间");
        }
        //  g. 校验用户最多创建 5 个队伍
        // todo 有 bug,可能同时创建 100 个队伍
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long hasTeamNum = this.count(queryWrapper);
        if (hasTeamNum >= 5) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建 5 个队伍");
        }
        //4. 插入队伍信息到队伍表
        team.setId(null);
        team.setUserId(userId);
        boolean result = this.save(team);
        Long teamId = team.getId();
        if (!result || teamId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        //5. 插入用户 => 队伍关系到关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        result = userTeamService.save(userTeam);
        if (!result) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        return teamId;
    }
}
/**
 * 队伍状态枚举
 */
public enum TeamStatusEnum {

    PUBLIC(0, "公开"),
    PRIVATE(1, "私有"),
    SECRET(2, "加密");

    private int value;

    private String text;

    public static TeamStatusEnum getEnumByValue(Integer value) {
        if (value == null) {
            return null;
        }
        TeamStatusEnum[] values = TeamStatusEnum.values();
        for (TeamStatusEnum teamStatusEnum : values) {
            if (teamStatusEnum.getValue() == value) {
                return teamStatusEnum;
            }
        }
        return null;
    }

    TeamStatusEnum(int value, String text) {
        this.value = value;
        this.text = text;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }
}
/**
 * 用户添加队伍请求体
 *
 * @author yupi
 */
@Data
public class TeamAddRequest implements Serializable {

    private static final long serialVersionUID = 3191241716373120793L;

    /**
     * 队伍名称
     */
    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;
}

ps:这里过期时间的获取可从控制台输入一下代码来实现,单单的输入年月日会导致数据库里的时间增加8小时(应该是时区的问题)
image.png
多次发送添加请求,当插入5次之后,再插入会报错
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/680017.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

安防综合管理系统EasyCVR视频汇聚平台GA/T 1400协议中的关键消息交互示例

在当今的信息化时代&#xff0c;公共安全防范日益成为保障社会和谐稳定的关键。视频监控系统作为现代安全防范的重要手段&#xff0c;正不断在公安、交通、城市管理等领域发挥着越来越重要的作用。而GA/T 1400协议视图库&#xff0c;作为公安视频图像信息应用系统的标准&#x…

使用 TinyEngine 低代码引擎实现三方物料集成

本文由体验技术团队 TinyEngine 项目成员炽凌创作&#xff0c;欢迎大家实操体验&#xff0c;本体验内容基于 TinyEngine 低代码引擎提供的环境&#xff0c;介绍了如何通过 TinyEngine 低代码引擎实现三方物料集成&#xff0c;帮助开发者快速开发。 知识背景 1.1 TinyEngine 低…

江苏省汽车及零部件产业协作配套对接会在苏州举行

5月28日&#xff0c;江苏省汽车及零部件产业协作配套对接会暨“百场万企”大中小企业融通对接活动在苏州举办。本次活动以“深化整零协作&#xff0c;促进大中小企业融通发展”为主题&#xff0c;由江苏省工业和信息化厅、中国中检所属中国汽车工程研究院股份有限公司&#xff…

Linux系统Docker部署Apache Superset并实现远程访问详细流程

目录 前言 1. 使用Docker部署Apache Superset 1.1 第一步安装docker 、docker compose 1.2 克隆superset代码到本地并使用docker compose启动 2. 安装cpolar内网穿透&#xff0c;实现公网访问 3. 设置固定连接公网地址 前言 作者简介&#xff1a; 懒大王敲代码&#xff0…

Python第二语言(一、Python start)

目录 1、下载Pyhton注意 2、python下载 3、Python start 3.1 第一个python&#xff08;print("Hello World!")&#xff09; 3.2 执行多条python代码&#xff08;Python解释器&#xff09; 3.3 小结&#xff08;python解释器、.py文件&#xff09; 3.4 开发工具…

Java微服务智慧工地可视化SaaS云解决方案源码

智慧工地是指运用信息化手段&#xff0c;围绕施工过程管理&#xff0c;建立互联协同、智能生产、科学管理的施工项目信息化生态圈&#xff0c;并将此数据在虚拟现实环境下与物联网采集到的工程信息进行数据挖掘分析&#xff0c;提供过程趋势预测及专家预案&#xff0c;实现工程…

计算机基础(8)——音频数字化(模电与数电)

&#x1f497;计算机基础系列文章&#x1f497; &#x1f449;&#x1f340;计算机基础&#xff08;1&#xff09;——计算机的发展史&#x1f340;&#x1f449;&#x1f340;计算机基础&#xff08;2&#xff09;——冯诺依曼体系结构&#x1f340;&#x1f449;&#x1f34…

Springboot 开发-- 集成 Activiti 7 流程引擎

引言 Activiti 7是一款遵循BPMN 2.0标准的开源工作流引擎&#xff0c;旨在为企业提供灵活、可扩展的流程管理功能。它支持图形化的流程设计、丰富的API接口、强大的执行引擎和完善的监控报表&#xff0c;帮助企业实现业务流程的自动化、规范化和智能化。本文将为您详细介绍 Ac…

Spring使用事务的两种方式

1. 为什么需要事务&#xff1f; 前面的博客 对MySQL事务作讲解&#xff0c;事务就是将⼀组操作封装成⼀个执⾏单元&#xff08;封装到⼀起&#xff09;&#xff0c;要么全部成功&#xff0c;要么全部失败。 比如&#xff0c;现在要实现转账操作&#xff1a; 第一步&#xff…

【Python】 Python 中的整数递增:深入理解 `+=` 运算符

基本原理 在 Python 中&#xff0c;整数递增通常指的是将一个整数的值增加一个固定的量&#xff0c;这通常是 1。虽然 Python 没有像 C 或 Java 那样的 运算符&#xff0c;但我们可以使用 运算符来实现相同的功能。 是一个赋值运算符&#xff0c;它将右侧表达式的值加到左侧…

用PlantUML描绘C++世界:通过文本描述精准控制UML图的生成

往期本博主的 C 精讲优质博文可通过这篇导航进行查找&#xff1a; Lemo 的C精华博文导航&#xff1a;进阶、精讲、设计模式文章全收录 前言 在编写程序时&#xff0c;可视化的工具可以极大地帮助我们理解和设计复杂的系统。对于C程序员来说&#xff0c;一个强大的工具是UML&am…

10_JavaWeb过滤器

文章目录 过滤器1.过滤器的实现1.1 实现过滤器1.2 配置过滤器1.2.1 过滤器的xml方式1.2.2 过滤器的注解方式 2. 过滤器的生命周期3. 过滤器链使用 过滤器 生活举例: 公司前台,停车场安保,地铁验票闸机 java中过滤仅仅是对请求做出过滤 客户端向服务器发出请求&#xff0c;在服…

SQLServer 查询指定数据库名和表名及表结构等

查询当前数据库中所有表名&#xff0c;不用指定数据库&#xff0c;选中某数据库直接执行SQL就好 -- U:所有用户表名; S:所有系统表名;V:所有视图表名 SELECT name FROM sysobjects WHERE xtypeU OR xtypeS OR xtypeV 查询指定数据库数据库中所有表名&#xff0c; SELECT TAB…

【排序算法】归并排序

一、定义&#xff1a; &#x1f449;归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff…

精益求精测径仪颜色也是一种工艺细节

首先&#xff0c;测径仪的颜色可以作为一种视觉标识&#xff0c;提升工作效率。在复杂的生产环境中&#xff0c;不同颜色的测径仪可以迅速区分不同型号、规格或功能的设备&#xff0c;减少误操作的可能性。同时&#xff0c;通过颜色搭配&#xff0c;还可以实现设备布局的美观与…

[数据集][图像分类]煤矿分类数据集351张4类别

数据集类型&#xff1a;图像分类用&#xff0c;不可用于目标检测无标注文件 数据集格式&#xff1a;仅仅包含jpg图片&#xff0c;每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数)&#xff1a;351 分类类别数&#xff1a;4 类别名称:[“Anthracite”,“Bituminous”,“…

Qt应用程序发布

一、静态编译发布 1.0:以Release模式构建工程 1.1:查看当前构建生成路径,并将所生成的.exe单独拷贝出来 1.2:将可执行文件*.exe拷贝至任一目标文件夹:D:\Temporary\QQIF 2:查看安装Qt时发布工具windeployqt.exe所在的目录 windeployqt.exe在Qt开发套件的bin目录下。Qt的每…

一个可以自动生成随机区组试验的excel VBA小程序

在作物品种区域试验时&#xff0c;通常会采用随机区组试验设计&#xff0c;特制作了一个可以自动生成随机区组试验的小程序。excel参数界面如下&#xff1a; 参数含义如下&#xff1a; 1、生成新表的名称&#xff1a;程序将新建表格&#xff0c;用于生成随机区组试验。若此处为…

JavaScript 从入门到精通Object(对象)

文章目录 对象文本和属性方括号计算属性 属性值简写属性名称限制属性存在性测试&#xff0c;“in” 操作符“for…in” 循环像对象一样排序 总结✅任务你好&#xff0c;对象检查空对象对象属性求和将数值属性值都乘以 2 对象引用和复制通过引用来比较克隆与合并&#xff0c;Obj…

消息队列-ActiveMQ

异步技术 企业级应用中广泛使用的三种异步消息传递技术 版权声明&#xff1a;本文为博主原创文章&#xff0c;遵循 CC 4.0 BY-SA 版权协议&#xff0c;转载请附上原文出处链接和本声明。原文链接&#xff1a;https://blog.csdn.net/qq_55917018/article/details/122122218 三…