Browse Source

feat(game): 添加参赛项目选择验证功能

- 新增每人限报项目数和项目限报人数字段- 实现项目选择验证接口和业务逻辑
- 添加验证规则:每人限报项目数、项目报名人数限制
- 提供详细的错误和警告信息返回
- 支持排除自身重复校验逻辑
zhou 2 weeks ago
parent
commit
a709a957d9

+ 9 - 4
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameAthleteController.java

@@ -1,10 +1,7 @@
 package org.dromara.system.controller;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import cn.hutool.json.JSONUtil;
@@ -18,6 +15,7 @@ import org.dromara.common.excel.core.ExcelResult;
 import org.dromara.common.redis.utils.RedisUtils;
 import org.dromara.system.domain.GameAthlete;
 import org.dromara.system.domain.GameTeam;
+import org.dromara.system.domain.bo.ProjectSelectionValidationBo;
 import org.dromara.system.domain.constant.GameEventConstant;
 import org.dromara.system.domain.vo.AthleteScoreVo;
 import org.dromara.system.domain.vo.GameTeamVo;
@@ -166,4 +164,11 @@ public class GameAthleteController extends BaseController {
                           @PathVariable Long[] athleteIds) {
         return toAjax(gameAthleteService.deleteWithValidByIds(List.of(athleteIds), true));
     }
+
+    @PostMapping("/validateProjectSelection")
+    @SaCheckPermission("system:gameAthlete:add")
+    public R<Map<String, Object>> validateProjectSelection(@RequestBody ProjectSelectionValidationBo bo) {
+        Map<String, Object> result = gameAthleteService.validateProjectSelection(bo);
+        return R.ok(result);
+    }
 }

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

@@ -66,6 +66,11 @@ public class GameEvent extends TenantEntity {
      */
     private Date endTime;
 
+    /**
+     * 每人限报项目数
+     */
+    private Integer limitApplication;
+
     /**
      * 赛事链接
      */

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

@@ -79,6 +79,11 @@ public class GameEventProject extends TenantEntity {
      */
     private Long participateNum;
 
+    /**
+     * 项目限报人数
+     */
+    private Integer limitPerson;
+
     /**
      * 录取名次
      */

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

@@ -68,6 +68,11 @@ public class GameEventBo extends BaseEntity {
     @NotNull(message = "结束时间不能为空", groups = { AddGroup.class, EditGroup.class })
     private Date endTime;
 
+    /**
+     * 每人限报项目数
+     */
+    private Integer limitApplication;
+
     /**
      * 赛事链接
      */

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

@@ -101,6 +101,11 @@ public class GameEventProjectBo extends BaseEntity {
      */
     private Long participateNum;
 
+    /**
+     * 项目限报人数
+     */
+    private Integer limitPerson;
+
     /**
      * 录取名次
      */

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

@@ -0,0 +1,15 @@
+package org.dromara.system.domain.bo;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 参赛项目选择表单验证
+ */
+@Data
+public class ProjectSelectionValidationBo {
+//    private Long eventId;
+    private Long athleteId; // 编辑时传入,新增时为null
+    private List<Long> selectedProjectIds;
+}

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

@@ -115,6 +115,11 @@ public class GameEventProjectVo implements Serializable {
     @ExcelProperty(value = "参赛人数")
     private Long participateNum;
 
+    /**
+     * 项目限报人数
+     */
+    private Integer limitPerson;
+
     /**
      * 录取名次
      */

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

@@ -77,6 +77,11 @@ public class GameEventVo implements Serializable {
     @ExcelProperty(value = "结束时间")
     private Date endTime;
 
+    /**
+     * 每人限报项目数
+     */
+    private Integer limitApplication;
+
     /**
      * 赛事链接
      */

+ 9 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameAthleteService.java

@@ -4,11 +4,13 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.system.domain.GameAthlete;
 import org.dromara.system.domain.bo.GameAthleteBo;
+import org.dromara.system.domain.bo.ProjectSelectionValidationBo;
 import org.dromara.system.domain.vo.AthleteScoreVo;
 import org.dromara.system.domain.vo.GameAthleteVo;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 参赛队员Service接口
@@ -104,4 +106,11 @@ public interface IGameAthleteService {
      * @return 运动员列表
      */
     List<GameAthleteBo> findByProjectIds(Collection<Long> projectIds);
+
+    /**
+     * 验证项目选择
+     * @param bo 验证参数
+     * @return 验证结果
+     */
+    Map<String, Object> validateProjectSelection(ProjectSelectionValidationBo bo);
 }

+ 76 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameAthleteServiceImpl.java

@@ -22,6 +22,7 @@ import org.dromara.system.domain.GameTeam;
 import org.dromara.system.domain.bo.GameAthleteBo;
 import org.dromara.system.domain.bo.GameEventBo;
 import org.dromara.system.domain.bo.GameTeamBo;
+import org.dromara.system.domain.bo.ProjectSelectionValidationBo;
 import org.dromara.system.domain.constant.GameEventConstant;
 import org.dromara.system.domain.vo.*;
 import org.dromara.system.mapper.GameAthleteMapper;
@@ -342,7 +343,8 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
         if (entity.getAthleteCode() != null){
             List<GameAthlete> list = baseMapper.selectList(new LambdaQueryWrapper<GameAthlete>()
                 .eq(GameAthlete::getAthleteCode, entity.getAthleteCode()));
-            if (!list.isEmpty()){
+            //排除自己
+            if (list.size()>1){
                 throw new ServiceException(entity.getName()+"的编号已存在!");
             }
         }
@@ -542,4 +544,77 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
     public List<GameAthleteBo> findByProjectIds(Collection<Long> projectIds) {
         return baseMapper.findByProjectIds(projectIds);
     }
+
+    /**
+     * 项目选择验证
+     * @param bo 验证参数
+     * @return
+     */
+    @Override
+    public Map<String, Object> validateProjectSelection(ProjectSelectionValidationBo bo) {
+        Map<String, Object> result = new HashMap<>();
+        List<String> errors = new ArrayList<>();
+        List<String> warnings = new ArrayList<>();
+
+        // 1. 校验每人限报项目数
+        GameEventVo event = gameEventService.getDefaultEvent();
+        if (event.getLimitApplication() != null && event.getLimitApplication() > 0) {
+            if (bo.getSelectedProjectIds().size() > event.getLimitApplication()) {
+                errors.add(String.format("每人限报项目数为 %d 个,您已选择 %d 个项目",
+                    event.getLimitApplication(), bo.getSelectedProjectIds().size()));
+            }
+        }
+
+        // 2. 校验各项目的限报人数
+        for (Long projectId : bo.getSelectedProjectIds()) {
+            GameEventProjectVo project = gameEventProjectService.queryById(projectId);
+            if (project.getLimitPerson() != null && project.getLimitPerson() > 0) {
+                // 统计当前项目已报名人数
+                int currentCount = countAthletesByProjectId(projectId, bo.getAthleteId());
+
+                if (currentCount >= project.getLimitPerson()) {
+                    errors.add(String.format("项目「%s」报名人数已满(%d/%d),无法添加",
+                        project.getProjectName(), currentCount, project.getLimitPerson()));
+                } else if (currentCount >= project.getLimitPerson() * 0.9) {
+                    warnings.add(String.format("项目「%s」报名人数接近上限(%d/%d)",
+                        project.getProjectName(), currentCount, project.getLimitPerson()));
+                }
+            }
+        }
+
+        result.put("valid", errors.isEmpty());
+        result.put("errors", errors);
+        result.put("warnings", warnings);
+
+        return result;
+    }
+
+    // 统计项目报名人数的方法
+    private int countAthletesByProjectId(Long projectId, Long excludeAthleteId) {
+        LambdaQueryWrapper<GameAthlete> wrapper = new LambdaQueryWrapper<>();
+        wrapper.like(GameAthlete::getProjectValue, projectId.toString());
+
+        if (excludeAthleteId != null) {
+            wrapper.ne(GameAthlete::getAthleteId, excludeAthleteId);
+        }
+
+        List<GameAthlete> athletes = baseMapper.selectList(wrapper);
+
+        // 需要解析JSON格式的projectValue字段
+        int count = 0;
+        for (GameAthlete athlete : athletes) {
+            if (StringUtils.isNotBlank(athlete.getProjectValue())) {
+                try {
+                    List<Long> projectIds = JSONUtil.toList(athlete.getProjectValue(), Long.class);
+                    if (projectIds.contains(projectId)) {
+                        count++;
+                    }
+                } catch (Exception e) {
+                    log.warn("解析运动员项目列表失败: {}", athlete.getProjectValue());
+                }
+            }
+        }
+
+        return count;
+    }
 }

+ 2 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameTeamServiceImpl.java

@@ -272,7 +272,8 @@ public class GameTeamServiceImpl implements IGameTeamService {
                 Wrappers.lambdaQuery(GameTeam.class)
                     .eq(GameTeam::getTeamCode, entity.getTeamCode())
             );
-            if (count > 0) {
+            //排除自己
+            if (count > 1) {
                 throw new ServiceException(entity.getTeamName()+"的编号已存在!");
             }
         }