Bladeren bron

feat(game): 添加排名分组层级结构功能

- 在GameRankGroup实体中新增parentId、ancestors和sortNum字段支持层级关系
- 为GameRankGroupBo和GameRankGroupVo添加层级相关属性并增加数据校验
- 修改查询方法将返回TableDataInfo改为返回树形结构的R<List<GameRankGroupVo>>
- 实现buildTree方法构建前端所需的层级树结构展示
- 添加insertByBo和updateByBo中的祖先列表维护逻辑
- 实现hasChildByRgId方法用于删除时的子节点校验
- 在mapper接口添加@Mapper注解并在service中实现递归查询功能
- 更新查询条件支持按parentId进行筛选并调整排序规则为按创建时间升序
zhou 5 dagen geleden
bovenliggende
commit
938b80f8f1

+ 3 - 2
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameRankGroupController.java

@@ -41,8 +41,9 @@ public class GameRankGroupController extends BaseController {
      */
     @SaCheckPermission("system:rankGroup:list")
     @GetMapping("/list")
-    public TableDataInfo<GameRankGroupVo> list(GameRankGroupBo bo, PageQuery pageQuery) {
-        return gameRankGroupService.queryPageList(bo, pageQuery);
+    public R<List<GameRankGroupVo>> list(GameRankGroupBo bo) {
+        List<GameRankGroupVo> list = gameRankGroupService.queryList(bo);
+        return R.ok(gameRankGroupService.buildTree(list));
     }
 
     /**

+ 15 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameRankGroup.java

@@ -32,11 +32,26 @@ public class GameRankGroup extends TenantEntity {
      */
     private Long eventId;
 
+    /**
+     * 父分组ID
+     */
+    private Long parentId;
+
+    /**
+     * 祖级列表
+     */
+    private String ancestors;
+
     /**
      * 分组名
      */
     private String rgName;
 
+    /**
+     * 显示顺序
+     */
+    private Integer sortNum;
+
     /**
      * 状态(0正常 1停用)
      */

+ 16 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameRankGroupBo.java

@@ -30,11 +30,27 @@ public class GameRankGroupBo extends BaseEntity {
      */
     private Long eventId;
 
+    /**
+     * 父分组ID
+     */
+    private Long parentId;
+
+    /**
+     * 祖级列表
+     */
+    private String ancestors;
+
     /**
      * 分组名
      */
+    @NotBlank(message = "分组名不能为空", groups = { AddGroup.class, EditGroup.class })
     private String rgName;
 
+    /**
+     * 显示顺序
+     */
+    private Integer sortNum;
+
     /**
      * 状态(0正常 1停用)
      */

+ 24 - 5
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameRankGroupVo.java

@@ -10,9 +10,9 @@ import lombok.Data;
 
 import java.io.Serial;
 import java.io.Serializable;
+import java.util.ArrayList;
 import java.util.Date;
-
-
+import java.util.List;
 
 /**
  * 排名分组视图对象 game_rank_group
@@ -31,21 +31,41 @@ public class GameRankGroupVo implements Serializable {
     /**
      * 主键
      */
-//    @ExcelProperty(value = "主键")
+    // @ExcelProperty(value = "主键")
     private Long rgId;
 
     /**
      * 赛事ID
      */
-//    @ExcelProperty(value = "赛事ID")
+    // @ExcelProperty(value = "赛事ID")
     private Long eventId;
 
+    /**
+     * 父分组ID
+     */
+    private Long parentId;
+
+    /**
+     * 祖级列表
+     */
+    private String ancestors;
+
     /**
      * 分组名
      */
     @ExcelProperty(value = "分组名")
     private String rgName;
 
+    /**
+     * 显示顺序
+     */
+    private Integer sortNum;
+
+    /**
+     * 子组别
+     */
+    private List<GameRankGroupVo> children = new ArrayList<>();
+
     /**
      * 更新时间
      */
@@ -65,5 +85,4 @@ public class GameRankGroupVo implements Serializable {
     @ExcelProperty(value = "备注")
     private String remark;
 
-
 }

+ 2 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/GameRankGroupMapper.java

@@ -1,5 +1,6 @@
 package org.dromara.system.mapper;
 
+import org.apache.ibatis.annotations.Mapper;
 import org.dromara.system.domain.GameRankGroup;
 import org.dromara.system.domain.vo.GameRankGroupVo;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@@ -10,6 +11,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
  * @author zlt
  * @date 2025-09-19
  */
+@Mapper
 public interface GameRankGroupMapper extends BaseMapperPlus<GameRankGroup, GameRankGroupVo> {
 
 }

+ 16 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameRankGroupService.java

@@ -7,7 +7,6 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 
 /**
  * 排名分组Service接口
@@ -66,4 +65,20 @@ public interface IGameRankGroupService {
      * @return 是否删除成功
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 构建前端所需要树结构
+     *
+     * @param groups 分组列表
+     * @return 树结构列表
+     */
+    List<GameRankGroupVo> buildTree(List<GameRankGroupVo> groups);
+
+    /**
+     * 查询是否存在子节点
+     * 
+     * @param rgId
+     * @return
+     */
+    boolean hasChildByRgId(Long rgId);
 }

+ 119 - 7
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameRankGroupServiceImpl.java

@@ -1,5 +1,6 @@
 package org.dromara.system.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -18,6 +19,7 @@ import org.dromara.system.domain.GameRankGroup;
 import org.dromara.system.mapper.GameRankGroupMapper;
 import org.dromara.system.service.IGameRankGroupService;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Collection;
@@ -43,7 +45,7 @@ public class GameRankGroupServiceImpl implements IGameRankGroupService {
      * @return 排名分组
      */
     @Override
-    public GameRankGroupVo queryById(Long rgId){
+    public GameRankGroupVo queryById(Long rgId) {
         return baseMapper.selectVoById(rgId);
     }
 
@@ -59,7 +61,7 @@ public class GameRankGroupServiceImpl implements IGameRankGroupService {
         if (bo.getEventId() == null) {
             Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
             if (cacheObject instanceof Integer) {
-                bo.setEventId(((Integer) cacheObject).longValue());  // 显式转换为 Long 类型
+                bo.setEventId(((Integer) cacheObject).longValue()); // 显式转换为 Long 类型
             } else if (cacheObject instanceof Long) {
                 bo.setEventId((Long) cacheObject);
             }
@@ -84,8 +86,9 @@ public class GameRankGroupServiceImpl implements IGameRankGroupService {
     private LambdaQueryWrapper<GameRankGroup> buildQueryWrapper(GameRankGroupBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<GameRankGroup> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(GameRankGroup::getRgId);
+        lqw.orderByAsc(GameRankGroup::getCreateTime);
         lqw.eq(bo.getEventId() != null, GameRankGroup::getEventId, bo.getEventId());
+        lqw.eq(bo.getParentId() != null, GameRankGroup::getParentId, bo.getParentId());
         lqw.like(StringUtils.isNotBlank(bo.getRgName()), GameRankGroup::getRgName, bo.getRgName());
         lqw.eq(StringUtils.isNotBlank(bo.getStatus()), GameRankGroup::getStatus, bo.getStatus());
         return lqw;
@@ -100,7 +103,21 @@ public class GameRankGroupServiceImpl implements IGameRankGroupService {
     @Override
     public Boolean insertByBo(GameRankGroupBo bo) {
         GameRankGroup add = MapstructUtils.convert(bo, GameRankGroup.class);
+
         validEntityBeforeSave(add);
+
+        // 设置祖级列表
+        if (add.getParentId() != 0) {
+            GameRankGroup parent = baseMapper.selectById(add.getParentId());
+            if (parent != null) {
+                add.setAncestors(parent.getAncestors() + "," + parent.getRgId());
+            } else {
+                add.setAncestors("0");
+            }
+        } else {
+            add.setAncestors("0");
+        }
+
         boolean flag = baseMapper.insert(add) > 0;
         if (flag) {
             bo.setRgId(add.getRgId());
@@ -118,14 +135,53 @@ public class GameRankGroupServiceImpl implements IGameRankGroupService {
     public Boolean updateByBo(GameRankGroupBo bo) {
         GameRankGroup update = MapstructUtils.convert(bo, GameRankGroup.class);
         validEntityBeforeSave(update);
+
+        GameRankGroup oldEntity = baseMapper.selectById(update.getRgId());
+        // 如果修改了上级,需要更新所有下级的祖级列表
+        if (oldEntity != null && !oldEntity.getParentId().equals(update.getParentId())) {
+            // 设置新的祖级列表
+            if (update.getParentId() != 0) {
+                GameRankGroup parent = baseMapper.selectById(update.getParentId());
+                if (parent != null) {
+                    update.setAncestors(parent.getAncestors() + "," + parent.getRgId());
+                }
+            } else {
+                update.setAncestors("0");
+            }
+            updateChildrenAncestors(update.getRgId(), update.getAncestors(), oldEntity.getAncestors());
+        }
+
         return baseMapper.updateById(update) > 0;
     }
 
+    /**
+     * 更新子元素祖级列表
+     *
+     * @param rgId         当前ID
+     * @param newAncestors 新祖级列表
+     * @param oldAncestors 旧祖级列表
+     */
+    private void updateChildrenAncestors(Long rgId, String newAncestors, String oldAncestors) {
+        List<GameRankGroup> list = baseMapper.selectList(Wrappers.<GameRankGroup>lambdaQuery()
+            .like(GameRankGroup::getAncestors, oldAncestors + "," + rgId));
+        if (CollUtil.isNotEmpty(list)) {
+            for (GameRankGroup child : list) {
+                child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
+            }
+            baseMapper.updateById(list);
+        }
+    }
+
     /**
      * 保存前的数据校验
      */
-    private void validEntityBeforeSave(GameRankGroup entity){
-        //TODO 做一些数据校验,如唯一约束
+    private void validEntityBeforeSave(GameRankGroup entity) {
+        if (entity.getParentId() == null) {
+            entity.setParentId(0L);
+        }
+        if (entity.getRgId() != null && entity.getRgId().equals(entity.getParentId())) {
+            throw new RuntimeException("上级分组不能是自己");
+        }
     }
 
     /**
@@ -137,9 +193,65 @@ public class GameRankGroupServiceImpl implements IGameRankGroupService {
      */
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
-            //TODO 做一些业务上的校验,判断是否需要校验
+        if (isValid) {
+            for (Long id : ids) {
+                if (hasChildByRgId(id)) {
+                    throw new RuntimeException("存在下级分组,不允许删除");
+                }
+            }
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    @Override
+    public List<GameRankGroupVo> buildTree(List<GameRankGroupVo> groups) {
+        List<GameRankGroupVo> returnList = new ArrayList<>();
+        List<Long> tempList = groups.stream().map(GameRankGroupVo::getRgId).toList();
+        for (GameRankGroupVo group : groups) {
+            // 如果是顶级节点, 遍历该父节点的所有子节点
+            if (!tempList.contains(group.getParentId())) {
+                recursionFn(groups, group);
+                returnList.add(group);
+            }
+        }
+        if (returnList.isEmpty()) {
+            returnList = groups;
+        }
+        return returnList;
+    }
+
+    @Override
+    public boolean hasChildByRgId(Long rgId) {
+        return baseMapper.exists(Wrappers.<GameRankGroup>lambdaQuery().eq(GameRankGroup::getParentId, rgId));
+    }
+
+    /**
+     * 递归列表
+     */
+    private void recursionFn(List<GameRankGroupVo> list, GameRankGroupVo t) {
+        // 得到子节点列表
+        List<GameRankGroupVo> childList = getChildList(list, t);
+        t.setChildren(childList);
+        for (GameRankGroupVo tChild : childList) {
+            if (hasChild(list, tChild)) {
+                recursionFn(list, tChild);
+            }
+        }
+    }
+
+    /**
+     * 得到子节点列表
+     */
+    private List<GameRankGroupVo> getChildList(List<GameRankGroupVo> list, GameRankGroupVo t) {
+        return list.stream()
+                .filter(n -> n.getParentId() != null && n.getParentId().equals(t.getRgId()))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 判断是否有子节点
+     */
+    private boolean hasChild(List<GameRankGroupVo> list, GameRankGroupVo t) {
+        return !getChildList(list, t).isEmpty();
+    }
 }