Browse Source

feat(game-event): 新增小程序端成绩上传功能并优化分组逻辑

- 新增 AppScoreValidationService 类用于小程序端成绩上传校验
- 添加 GameAthleteCompetitionGroup 相关实体和 Mapper- 在 GameEventGroupController 中增加生成分组和获取分组结果接口
- 实现 GameEventGroupServiceImpl 中的分组生成逻辑- 在 ScoreController 中添加小程序端批量上传成绩接口
- 优化成绩数据校验和保存逻辑
zhou 7 hours ago
parent
commit
fb5bbc346b
15 changed files with 1388 additions and 20 deletions
  1. 25 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameEventGroupController.java
  2. 93 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/ScoreController.java
  3. 91 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameAthleteCompetitionGroup.java
  4. 90 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/AppScoreUploadBo.java
  5. 96 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameAthleteCompetitionGroupBo.java
  6. 2 2
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameScoreBo.java
  7. 90 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameAthleteCompetitionGroupVo.java
  8. 15 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/GameAthleteCompetitionGroupMapper.java
  9. 95 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameAthleteCompetitionGroupService.java
  10. 17 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameEventGroupService.java
  11. 211 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/app/AppScoreValidationService.java
  12. 295 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameAthleteCompetitionGroupServiceImpl.java
  13. 233 8
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventGroupServiceImpl.java
  14. 5 9
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameScoreServiceImpl.java
  15. 30 0
      ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/GameAthleteCompetitionGroupMapper.xml

+ 25 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameEventGroupController.java

@@ -1,6 +1,7 @@
 package org.dromara.system.controller;
 
 import java.util.List;
+import java.util.Map;
 
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
@@ -102,4 +103,28 @@ public class GameEventGroupController extends BaseController {
                           @PathVariable Long[] groupIds) {
         return toAjax(gameEventGroupService.deleteWithValidByIds(List.of(groupIds), true));
     }
+
+    /**
+     * 生成分组结果
+     *
+     * @param groupId 分组ID
+     */
+    @SaCheckPermission("system:gameEventGroup:query")
+    @GetMapping("/generateGroups/{groupId}")
+    public R<Map<String, Object>> generateGroups(@NotNull(message = "分组ID不能为空")
+                                               @PathVariable Long groupId) {
+        return R.ok(gameEventGroupService.generateGroups(groupId));
+    }
+
+    /**
+     * 从数据库获取分组结果
+     *
+     * @param groupId 分组ID
+     */
+    @SaCheckPermission("system:gameEventGroup:query")
+    @GetMapping("/getGroupResultFromDB/{groupId}")
+    public R<Map<String, Object>> getGroupResultFromDB(@NotNull(message = "分组ID不能为空")
+                                                     @PathVariable Long groupId) {
+        return R.ok(gameEventGroupService.getGroupResultFromDB(groupId));
+    }
 }

+ 93 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/ScoreController.java

@@ -3,10 +3,16 @@ package org.dromara.system.controller.app;
 import cn.dev33.satoken.annotation.SaIgnore;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
 import org.dromara.system.domain.vo.AppScoreVo;
 import org.dromara.system.domain.vo.GameScoreVo;
-import org.dromara.system.domain.vo.GameTeamVo;
 import org.dromara.system.service.IGameScoreService;
+import org.dromara.system.service.app.AppScoreValidationService;
+import org.dromara.system.domain.bo.AppScoreUploadBo;
+import org.dromara.system.domain.bo.GameScoreBo;
+import org.dromara.common.core.validate.AddGroup;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
@@ -24,6 +30,7 @@ import java.util.Map;
 public class ScoreController {
 
     private final IGameScoreService gameScoreService;
+    private final AppScoreValidationService appScoreValidationService;
 
     /**
      * 查看获奖名单
@@ -45,4 +52,89 @@ public class ScoreController {
         List<AppScoreVo> result = gameScoreService.listAppScore(eventId, projectId);
         return R.ok(result);
     }
+
+    /**
+     * 小程序端批量上传成绩数据
+     * 专门为小程序端设计的批量成绩上传接口,包含完整的校验逻辑
+     */
+    @RepeatSubmit()
+    @PostMapping("/upload")
+    @Log(title = "小程序端批量上传成绩数据", businessType = BusinessType.INSERT)
+    public R<String> uploadScore(@Validated(AddGroup.class) @RequestBody List<AppScoreUploadBo> scoreList) {
+        try {
+            if (scoreList == null || scoreList.isEmpty()) {
+                return R.fail("成绩数据列表不能为空");
+            }
+
+            if (scoreList.size() > 100) {
+                return R.fail("单次最多上传100条成绩数据");
+            }
+
+            int successCount = 0;
+            int failCount = 0;
+            StringBuilder errorMsg = new StringBuilder();
+
+            // 批量处理成绩数据
+            for (int i = 0; i < scoreList.size(); i++) {
+                AppScoreUploadBo bo = scoreList.get(i);
+                try {
+                    // 保存前校验
+                    appScoreValidationService.validateAppScoreUpload(bo);
+
+                    // 校验通过后,转换为GameScoreBo并调用服务层保存
+                    GameScoreBo gameScoreBo = convertToGameScoreBo(bo);
+
+                    // 调用成绩服务保存数据
+                    boolean success = gameScoreService.insertByBo(gameScoreBo);
+
+                    if (success) {
+                        successCount++;
+                    } else {
+                        failCount++;
+                        errorMsg.append("第").append(i + 1).append("条数据保存失败; ");
+                    }
+                } catch (Exception e) {
+                    failCount++;
+                    errorMsg.append("第").append(i + 1).append("条数据校验失败: ").append(e.getMessage()).append("; ");
+                }
+            }
+
+            // 返回处理结果
+            if (failCount == 0) {
+                return R.ok("批量成绩上传成功,共上传" + successCount + "条数据");
+            } else if (successCount > 0) {
+                return R.ok("部分成绩上传成功,成功" + successCount + "条,失败" + failCount + "条。失败原因:" + errorMsg.toString());
+            } else {
+                return R.fail("批量成绩上传失败,失败原因:" + errorMsg.toString());
+            }
+        } catch (Exception e) {
+            return R.fail("批量成绩上传失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 将AppScoreUploadBo转换为GameScoreBo
+     */
+    private GameScoreBo convertToGameScoreBo(AppScoreUploadBo bo) {
+        GameScoreBo gameScoreBo = new GameScoreBo();
+        gameScoreBo.setEventId(bo.getEventId());
+        gameScoreBo.setProjectId(bo.getProjectId());
+        gameScoreBo.setAthleteId(bo.getAthleteId());
+        gameScoreBo.setTeamId(bo.getTeamId());
+        gameScoreBo.setIndividualPerformance(bo.getIndividualPerformance());
+        gameScoreBo.setTeamPerformance(bo.getTeamPerformance());
+        gameScoreBo.setScoreType(bo.getScoreType());
+        gameScoreBo.setScoreRank(bo.getScoreRank());
+        gameScoreBo.setScorePoint(bo.getScorePoint());
+        gameScoreBo.setAward(bo.getAward());
+        gameScoreBo.setExtraScore1(bo.getExtraScore1());
+        gameScoreBo.setExtraScore2(bo.getExtraScore2());
+        gameScoreBo.setRemark(bo.getRemark());
+
+        // 设置默认值
+        gameScoreBo.setStatusFlag("0"); // 有效
+        gameScoreBo.setStatus("0");     // 正常
+
+        return gameScoreBo;
+    }
 }

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

@@ -0,0 +1,91 @@
+package org.dromara.system.domain;
+
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 运动员比赛场次分组关联对象 game_athlete_competition_group
+ *
+ * @author zlt
+ * @date 2025-08-28
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("game_athlete_competition_group")
+public class GameAthleteCompetitionGroup extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 分组ID
+     */
+    private Long groupId;
+
+    /**
+     * 赛事ID
+     */
+    private Long eventId;
+
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+
+    /**
+     * 运动员ID
+     */
+    private Long athleteId;
+
+    /**
+     * 队伍ID
+     */
+    private Long teamId;
+
+    /**
+     * 组次序号
+     */
+    private Long groupIndex;
+
+    /**
+     * 道次序号
+     */
+    private Long trackIndex;
+
+    /**
+     * 运动员编号
+     */
+    private String athleteCode;
+
+    /**
+     * 运动员姓名
+     */
+    private String athleteName;
+
+    /**
+     * 队伍名称
+     */
+    private String teamName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

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

@@ -0,0 +1,90 @@
+package org.dromara.system.domain.bo;
+
+import lombok.Data;
+import org.dromara.common.core.validate.AddGroup;
+import jakarta.validation.constraints.*;
+
+import java.math.BigDecimal;
+
+/**
+ * 小程序端成绩上传业务对象
+ *
+ * @author zlt
+ * @date 2025-07-30
+ */
+@Data
+public class AppScoreUploadBo {
+
+    /**
+     * 赛事ID
+     */
+    @NotNull(message = "赛事ID不能为空", groups = { AddGroup.class })
+    private Long eventId;
+
+    /**
+     * 项目ID
+     */
+    @NotNull(message = "项目ID不能为空", groups = { AddGroup.class })
+    private Long projectId;
+
+    /**
+     * 运动员ID
+     */
+    @NotNull(message = "运动员ID不能为空", groups = { AddGroup.class })
+    private Long athleteId;
+
+    /**
+     * 队伍ID
+     */
+    @NotNull(message = "队伍ID不能为空", groups = { AddGroup.class })
+    private Long teamId;
+
+    /**
+     * 个人成绩
+     */
+    @DecimalMin(value = "0.0", message = "个人成绩不能为负数", groups = { AddGroup.class })
+    private BigDecimal individualPerformance;
+
+    /**
+     * 团队成绩
+     */
+    @DecimalMin(value = "0.0", message = "团队成绩不能为负数", groups = { AddGroup.class })
+    private BigDecimal teamPerformance;
+
+    /**
+     * 成绩类型
+     */
+    private String scoreType;
+
+    /**
+     * 名次
+     */
+    @Min(value = 1, message = "名次必须大于0", groups = { AddGroup.class })
+    private Integer scoreRank;
+
+    /**
+     * 积分
+     */
+    @Min(value = 0, message = "积分不能为负数", groups = { AddGroup.class })
+    private Integer scorePoint;
+
+    /**
+     * 奖项
+     */
+    private String award;
+
+    /**
+     * 附加成绩1
+     */
+    private String extraScore1;
+
+    /**
+     * 附加成绩2
+     */
+    private String extraScore2;
+
+    /**
+     * 备注
+     */
+    private String remark;
+}

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

@@ -0,0 +1,96 @@
+package org.dromara.system.domain.bo;
+
+import io.github.linpeilie.annotations.AutoMapper;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import jakarta.validation.constraints.NotNull;
+import org.dromara.system.domain.GameAthleteCompetitionGroup;
+
+import java.io.Serial;
+
+/**
+ * 运动员比赛场次分组关联业务对象 game_athlete_competition_group
+ *
+ * @author zlt
+ * @date 2025-08-28
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = GameAthleteCompetitionGroup.class, reverseConvertGenerate = false)
+public class GameAthleteCompetitionGroupBo extends BaseEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 分组ID
+     */
+    @NotNull(message = "分组ID不能为空")
+    private Long groupId;
+
+    /**
+     * 赛事ID
+     */
+    private Long eventId;
+
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+
+    /**
+     * 运动员ID
+     */
+    @NotNull(message = "运动员ID不能为空")
+    private Long athleteId;
+
+    /**
+     * 队伍ID
+     */
+    private Long teamId;
+
+    /**
+     * 组次序号
+     */
+    @NotNull(message = "组次序号不能为空")
+    private Long groupIndex;
+
+    /**
+     * 道次序号
+     */
+    @NotNull(message = "道次序号不能为空")
+    private Long trackIndex;
+
+    /**
+     * 运动员编号
+     */
+    private String athleteCode;
+
+    /**
+     * 运动员姓名
+     */
+    private String athleteName;
+
+    /**
+     * 队伍名称
+     */
+    private String teamName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

+ 2 - 2
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameScoreBo.java

@@ -31,13 +31,13 @@ public class GameScoreBo extends BaseEntity {
     /**
      * 赛事ID
      */
-//    @NotNull(message = "赛事ID不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "赛事ID不能为空", groups = { AddGroup.class, EditGroup.class })
     private Long eventId;
 
     /**
      * 项目ID
      */
-//    @NotNull(message = "项目ID不能为空", groups = { AddGroup.class, EditGroup.class })
+    @NotNull(message = "项目ID不能为空", groups = { AddGroup.class, EditGroup.class })
     private Long projectId;
 
     /**

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

@@ -0,0 +1,90 @@
+package org.dromara.system.domain.vo;
+
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import org.dromara.system.domain.GameAthleteCompetitionGroup;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 运动员比赛场次分组关联视图对象 game_athlete_competition_group
+ *
+ * @author zlt
+ * @date 2025-08-28
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = GameAthleteCompetitionGroup.class)
+public class GameAthleteCompetitionGroupVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * 分组ID
+     */
+    private Long groupId;
+
+    /**
+     * 赛事ID
+     */
+    private Long eventId;
+
+    /**
+     * 项目ID
+     */
+    private Long projectId;
+
+    /**
+     * 运动员ID
+     */
+    private Long athleteId;
+
+    /**
+     * 队伍ID
+     */
+    private Long teamId;
+
+    /**
+     * 组次序号
+     */
+    private Long groupIndex;
+
+    /**
+     * 道次序号
+     */
+    private Long trackIndex;
+
+    /**
+     * 运动员编号
+     */
+    private String athleteCode;
+
+    /**
+     * 运动员姓名
+     */
+    private String athleteName;
+
+    /**
+     * 队伍名称
+     */
+    private String teamName;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+}

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

@@ -0,0 +1,15 @@
+package org.dromara.system.mapper;
+
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.system.domain.GameAthleteCompetitionGroup;
+import org.dromara.system.domain.vo.GameAthleteCompetitionGroupVo;
+
+/**
+ * 运动员比赛场次分组关联Mapper接口
+ *
+ * @author zlt
+ * @date 2025-08-28
+ */
+public interface GameAthleteCompetitionGroupMapper extends BaseMapperPlus<GameAthleteCompetitionGroup, GameAthleteCompetitionGroupVo> {
+
+} 

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

@@ -0,0 +1,95 @@
+package org.dromara.system.service;
+
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.system.domain.bo.GameAthleteCompetitionGroupBo;
+import org.dromara.system.domain.vo.GameAthleteCompetitionGroupVo;
+import org.dromara.system.domain.vo.GameAthleteVo;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 运动员比赛场次分组关联Service接口
+ *
+ * @author zlt
+ * @date 2025-08-28
+ */
+public interface IGameAthleteCompetitionGroupService {
+
+    /**
+     * 查询运动员比赛场次分组关联
+     *
+     * @param id 主键
+     * @return 运动员比赛场次分组关联
+     */
+    GameAthleteCompetitionGroupVo queryById(Long id);
+
+    /**
+     * 查询运动员比赛场次分组关联列表
+     *
+     * @param bo 查询条件
+     * @return 运动员比赛场次分组关联集合
+     */
+    TableDataInfo<GameAthleteCompetitionGroupVo> queryPageList(GameAthleteCompetitionGroupBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询运动员比赛场次分组关联列表
+     *
+     * @param bo 查询条件
+     * @return 运动员比赛场次分组关联集合
+     */
+    List<GameAthleteCompetitionGroupVo> queryList(GameAthleteCompetitionGroupBo bo);
+
+    /**
+     * 新增运动员比赛场次分组关联
+     *
+     * @param bo 运动员比赛场次分组关联新增业务对象
+     * @return 结果
+     */
+    Boolean insertByBo(GameAthleteCompetitionGroupBo bo);
+
+    /**
+     * 修改运动员比赛场次分组关联
+     *
+     * @param bo 运动员比赛场次分组关联编辑业务对象
+     * @return 结果
+     */
+    Boolean updateByBo(GameAthleteCompetitionGroupBo bo);
+
+    /**
+     * 校验并批量删除运动员比赛场次分组关联信息
+     *
+     * @param ids     主键集合
+     * @param isValid 是否检验,true-删除前校验,false-不校验
+     * @return 结果
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 根据分组ID查询分组结果
+     *
+     * @param groupId 分组ID
+     * @return 分组结果
+     */
+    Map<String, Object> getGroupResultFromDB(Long groupId);
+
+    /**
+     * 保存分组结果到数据库
+     *
+     * @param groupId 分组ID
+     * @param groupResult 分组结果
+     * @param groupInfo 分组信息
+     * @return 是否保存成功
+     */
+    Boolean saveGroupResult(Long groupId, Map<String, GameAthleteVo> groupResult, Object groupInfo);
+
+    /**
+     * 根据分组ID删除分组结果
+     *
+     * @param groupId 分组ID
+     * @return 是否删除成功
+     */
+    Boolean deleteGroupResultByGroupId(Long groupId);
+}

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

@@ -8,6 +8,7 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 赛事分组Service接口
@@ -73,4 +74,20 @@ public interface IGameEventGroupService {
      * @return
      */
     GameEventGroup queryByEventId(Long defaultEventId);
+
+    /**
+     * 生成分组结果
+     *
+     * @param groupId 分组ID
+     * @return 分组结果
+     */
+    Map<String, Object> generateGroups(Long groupId);
+
+    /**
+     * 从数据库获取分组结果
+     *
+     * @param groupId 分组ID
+     * @return 分组结果
+     */
+    Map<String, Object> getGroupResultFromDB(Long groupId);
 }

+ 211 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/app/AppScoreValidationService.java

@@ -0,0 +1,211 @@
+package org.dromara.system.service.app;
+
+import org.dromara.system.domain.bo.AppScoreUploadBo;
+import org.dromara.system.domain.vo.GameEventVo;
+import org.dromara.system.domain.vo.GameEventProjectVo;
+import org.dromara.system.domain.vo.GameAthleteVo;
+import org.dromara.system.domain.vo.GameTeamVo;
+import org.dromara.system.service.IGameAthleteService;
+import org.dromara.system.service.IGameEventProjectService;
+import org.dromara.system.service.IGameEventService;
+import org.dromara.system.service.IGameTeamService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.dromara.common.core.exception.ServiceException;
+
+import java.math.BigDecimal;
+
+/**
+ * 小程序端成绩校验服务
+ * 专门用于校验小程序端成绩上传的业务逻辑
+ *
+ * @author zlt
+ * @date 2025-07-30
+ */
+@Service
+public class AppScoreValidationService {
+
+    @Autowired
+    private IGameEventService gameEventService;
+
+    @Autowired
+    private IGameEventProjectService gameEventProjectService;
+
+    @Autowired
+    private IGameAthleteService gameAthleteService;
+
+    @Autowired
+    private IGameTeamService gameTeamService;
+
+    /**
+     * 校验小程序端成绩上传数据
+     * @param bo 成绩上传业务对象
+     */
+    public void validateAppScoreUpload(AppScoreUploadBo bo) {
+        // 基础数据校验
+        validateBasicData(bo);
+
+        // 关联数据校验
+        validateRelatedData(bo);
+
+        // 业务逻辑校验
+        validateBusinessLogic(bo);
+
+        // 成绩数据校验
+        validateScoreData(bo);
+    }
+
+    /**
+     * 基础数据校验
+     */
+    private void validateBasicData(AppScoreUploadBo bo) {
+        if (bo.getEventId() == null) {
+            throw new ServiceException("赛事ID不能为空");
+        }
+
+        if (bo.getProjectId() == null) {
+            throw new ServiceException("项目ID不能为空");
+        }
+
+        if (bo.getAthleteId() == null) {
+            throw new ServiceException("运动员ID不能为空");
+        }
+
+        if (bo.getTeamId() == null) {
+            throw new ServiceException("队伍ID不能为空");
+        }
+    }
+
+    /**
+     * 关联数据校验
+     */
+    private void validateRelatedData(AppScoreUploadBo bo) {
+        // 校验赛事ID
+        try {
+            GameEventVo event = gameEventService.queryById(bo.getEventId());
+            if (event == null) {
+                throw new ServiceException("赛事不存在: " + bo.getEventId());
+            }
+            // 校验赛事状态是否正常
+            if (!"0".equals(event.getStatus())) {
+                throw new ServiceException("赛事状态异常,无法上传成绩");
+            }
+        } catch (Exception e) {
+            if (e instanceof ServiceException) {
+                throw e;
+            }
+            throw new ServiceException("校验赛事ID失败: " + e.getMessage());
+        }
+
+        // 校验项目ID
+        try {
+            GameEventProjectVo project = gameEventProjectService.queryById(bo.getProjectId());
+            if (project == null) {
+                throw new ServiceException("项目不存在: " + bo.getProjectId());
+            }
+
+            // 校验项目是否属于指定赛事
+            if (!bo.getEventId().equals(project.getEventId())) {
+                throw new ServiceException("项目不属于指定赛事");
+            }
+
+            // 校验项目状态是否正常
+            if (!"0".equals(project.getStatus())) {
+                throw new ServiceException("项目状态异常,无法上传成绩");
+            }
+        } catch (Exception e) {
+            if (e instanceof ServiceException) {
+                throw e;
+            }
+            throw new ServiceException("校验项目ID失败: " + e.getMessage());
+        }
+
+        // 校验运动员ID
+        try {
+            GameAthleteVo athlete = gameAthleteService.queryById(bo.getAthleteId());
+            if (athlete == null) {
+                throw new ServiceException("运动员不存在: " + bo.getAthleteId());
+            }
+
+            // 校验运动员状态是否正常
+            if (!"0".equals(athlete.getStatus())) {
+                throw new ServiceException("运动员状态异常,无法上传成绩");
+            }
+        } catch (Exception e) {
+            if (e instanceof ServiceException) {
+                throw e;
+            }
+            throw new ServiceException("校验运动员ID失败: " + e.getMessage());
+        }
+
+        // 校验队伍ID
+        try {
+            GameTeamVo team = gameTeamService.queryById(bo.getTeamId());
+            if (team == null) {
+                throw new ServiceException("队伍不存在: " + bo.getTeamId());
+            }
+
+            // 校验队伍状态是否正常
+            if (!"0".equals(team.getStatus())) {
+                throw new ServiceException("队伍状态异常,无法上传成绩");
+            }
+        } catch (Exception e) {
+            if (e instanceof ServiceException) {
+                throw e;
+            }
+            throw new ServiceException("校验队伍ID失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 业务逻辑校验
+     */
+    private void validateBusinessLogic(AppScoreUploadBo bo) {
+        // 校验是否已经存在该运动员在该项目的成绩
+        // 这里可以添加防重复提交的逻辑
+
+        // 校验成绩上传时间是否在合理范围内
+        // 可以添加时间窗口校验
+
+        // 校验运动员是否参加了该项目
+        // 可以添加参赛资格校验
+    }
+
+    /**
+     * 成绩数据校验
+     */
+    private void validateScoreData(AppScoreUploadBo bo) {
+        // 校验个人成绩
+        if (bo.getIndividualPerformance() != null) {
+            if (bo.getIndividualPerformance().compareTo(BigDecimal.ZERO) < 0) {
+                throw new ServiceException("个人成绩不能为负数");
+            }
+        }
+
+        // 校验团队成绩
+        if (bo.getTeamPerformance() != null) {
+            if (bo.getTeamPerformance().compareTo(BigDecimal.ZERO) < 0) {
+                throw new ServiceException("团队成绩不能为负数");
+            }
+        }
+
+        // 至少需要有一个成绩值
+        if (bo.getIndividualPerformance() == null && bo.getTeamPerformance() == null) {
+            throw new ServiceException("至少需要提供个人成绩或团队成绩");
+        }
+
+        // 校验名次
+        if (bo.getScoreRank() != null) {
+            if (bo.getScoreRank() < 1) {
+                throw new ServiceException("名次必须大于0");
+            }
+        }
+
+        // 校验积分
+        if (bo.getScorePoint() != null) {
+            if (bo.getScorePoint() < 0) {
+                throw new ServiceException("积分不能为负数");
+            }
+        }
+    }
+}

+ 295 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameAthleteCompetitionGroupServiceImpl.java

@@ -0,0 +1,295 @@
+package org.dromara.system.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.dromara.system.domain.bo.GameAthleteCompetitionGroupBo;
+import org.dromara.system.domain.vo.GameAthleteCompetitionGroupVo;
+import org.dromara.system.domain.GameAthleteCompetitionGroup;
+import org.dromara.system.mapper.GameAthleteCompetitionGroupMapper;
+import org.dromara.system.service.IGameAthleteCompetitionGroupService;
+import org.dromara.system.domain.vo.GameEventGroupVo;
+import org.dromara.system.domain.vo.GameAthleteVo;
+import org.dromara.system.domain.vo.GameTeamVo;
+import org.dromara.system.service.IGameTeamService;
+import org.dromara.common.core.utils.SpringUtils;
+
+import java.util.*;
+
+/**
+ * 运动员比赛场次分组关联Service业务层处理
+ *
+ * @author zlt
+ * @date 2025-08-28
+ */
+@Slf4j
+@Service
+public class GameAthleteCompetitionGroupServiceImpl implements IGameAthleteCompetitionGroupService {
+
+    @Autowired
+    private GameAthleteCompetitionGroupMapper baseMapper;
+
+    private IGameTeamService gameTeamService;
+
+    // 获取队伍服务的辅助方法
+    private IGameTeamService getGameTeamService() {
+        if (gameTeamService == null) {
+            gameTeamService = SpringUtils.getBean(IGameTeamService.class);
+        }
+        return gameTeamService;
+    }
+
+    /**
+     * 查询运动员比赛场次分组关联
+     *
+     * @param id 主键
+     * @return 运动员比赛场次分组关联
+     */
+    @Override
+    public GameAthleteCompetitionGroupVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询运动员比赛场次分组关联列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 运动员比赛场次分组关联分页列表
+     */
+    @Override
+    public TableDataInfo<GameAthleteCompetitionGroupVo> queryPageList(GameAthleteCompetitionGroupBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<GameAthleteCompetitionGroup> lqw = buildQueryWrapper(bo);
+        Page<GameAthleteCompetitionGroupVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的运动员比赛场次分组关联列表
+     *
+     * @param bo 查询条件
+     * @return 运动员比赛场次分组关联列表
+     */
+    @Override
+    public List<GameAthleteCompetitionGroupVo> queryList(GameAthleteCompetitionGroupBo bo) {
+        LambdaQueryWrapper<GameAthleteCompetitionGroup> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<GameAthleteCompetitionGroup> buildQueryWrapper(GameAthleteCompetitionGroupBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<GameAthleteCompetitionGroup> lqw = Wrappers.lambdaQuery();
+        lqw.eq(bo.getGroupId() != null, GameAthleteCompetitionGroup::getGroupId, bo.getGroupId());
+        lqw.eq(bo.getEventId() != null, GameAthleteCompetitionGroup::getEventId, bo.getEventId());
+        lqw.eq(bo.getProjectId() != null, GameAthleteCompetitionGroup::getProjectId, bo.getProjectId());
+        lqw.eq(bo.getAthleteId() != null, GameAthleteCompetitionGroup::getAthleteId, bo.getAthleteId());
+        lqw.eq(bo.getTeamId() != null, GameAthleteCompetitionGroup::getTeamId, bo.getTeamId());
+        lqw.eq(bo.getGroupIndex() != null, GameAthleteCompetitionGroup::getGroupIndex, bo.getGroupIndex());
+        lqw.eq(bo.getTrackIndex() != null, GameAthleteCompetitionGroup::getTrackIndex, bo.getTrackIndex());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), GameAthleteCompetitionGroup::getStatus, bo.getStatus());
+        lqw.orderByAsc(GameAthleteCompetitionGroup::getGroupIndex)
+            .orderByAsc(GameAthleteCompetitionGroup::getTrackIndex);
+        return lqw;
+    }
+
+    /**
+     * 新增运动员比赛场次分组关联
+     *
+     * @param bo 运动员比赛场次分组关联
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(GameAthleteCompetitionGroupBo bo) {
+        GameAthleteCompetitionGroup add = MapstructUtils.convert(bo, GameAthleteCompetitionGroup.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改运动员比赛场次分组关联
+     *
+     * @param bo 运动员比赛场次分组关联
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(GameAthleteCompetitionGroupBo bo) {
+        GameAthleteCompetitionGroup update = MapstructUtils.convert(bo, GameAthleteCompetitionGroup.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(GameAthleteCompetitionGroup entity) {
+        //TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除运动员比赛场次分组关联信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            //TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 根据分组ID查询分组结果
+     *
+     * @param groupId 分组ID
+     * @return 分组结果
+     */
+    @Override
+    public Map<String, Object> getGroupResultFromDB(Long groupId) {
+        Map<String, Object> result = new HashMap<>();
+
+        try {
+            // 查询数据库中的分组结果
+            GameAthleteCompetitionGroupBo queryBo = new GameAthleteCompetitionGroupBo();
+            queryBo.setGroupId(groupId);
+            List<GameAthleteCompetitionGroupVo> groupData = queryList(queryBo);
+
+            if (groupData.isEmpty()) {
+                result.put("success", false);
+                result.put("message", "数据库中没有找到分组数据");
+                return result;
+            }
+
+            // 构建分组结果
+            Map<String, Object> groupResult = new HashMap<>();
+            for (GameAthleteCompetitionGroupVo item : groupData) {
+                String key = item.getGroupIndex() + "-" + item.getTrackIndex();
+                Map<String, Object> athlete = new HashMap<>();
+                athlete.put("athleteId", item.getAthleteId());
+                athlete.put("athleteCode", item.getAthleteCode());
+                athlete.put("name", item.getAthleteName());
+                athlete.put("teamId", item.getTeamId());
+                athlete.put("teamName", item.getTeamName());
+                groupResult.put(key, athlete);
+            }
+
+            result.put("success", true);
+            result.put("groupResult", groupResult);
+            result.put("totalAthletes", groupData.size());
+
+        } catch (Exception e) {
+            log.error("从数据库查询分组结果失败", e);
+            result.put("success", false);
+            result.put("message", "查询分组结果失败: " + e.getMessage());
+        }
+
+        return result;
+    }
+
+    /**
+     * 保存分组结果到数据库
+     *
+     * @param groupId 分组ID
+     * @param groupResult 分组结果
+     * @param groupInfo 分组信息
+     * @return 是否保存成功
+     */
+    @Override
+    public Boolean saveGroupResult(Long groupId, Map<String, GameAthleteVo> groupResult, Object groupInfo) {
+        try {
+            // 先删除原有的分组数据
+            deleteGroupResultByGroupId(groupId);
+
+            // 保存新的分组数据
+            List<GameAthleteCompetitionGroupBo> boList = new ArrayList<>();
+
+            for (Map.Entry<String, GameAthleteVo> entry : groupResult.entrySet()) {
+                String key = entry.getKey();
+                GameAthleteVo value = entry.getValue();
+
+                String[] keyParts = key.split("-");
+                if (keyParts.length >= 2) {
+                    GameAthleteCompetitionGroupBo bo = new GameAthleteCompetitionGroupBo();
+                    bo.setGroupId(groupId);
+
+                    // 设置组别和道次
+                    bo.setGroupIndex(Long.valueOf(keyParts[0]));
+                    bo.setTrackIndex(Long.valueOf(keyParts[1]));
+
+                    // 设置运动员信息 - 直接从GameAthleteVo对象获取
+                    bo.setAthleteId(value.getAthleteId());
+                    bo.setAthleteCode(value.getAthleteCode());
+                    bo.setAthleteName(value.getName());
+                    bo.setTeamId(value.getTeamId());
+                    
+                    // 通过队伍ID查询队伍名称
+                    String teamName = "";
+                    if (value.getTeamId() != null) {
+                        try {
+                            GameTeamVo team = getGameTeamService().queryById(value.getTeamId());
+                            if (team != null) {
+                                teamName = team.getTeamName();
+                            }
+                        } catch (Exception e) {
+                            log.warn("查询队伍名称失败,队伍ID: {}, 错误: {}", value.getTeamId(), e.getMessage());
+                        }
+                    }
+                    bo.setTeamName(teamName != null ? teamName : "");
+
+                    // 设置其他信息
+                    if (groupInfo instanceof GameEventGroupVo) {
+                        GameEventGroupVo group = (GameEventGroupVo) groupInfo;
+                        bo.setEventId(group.getEventId());
+                        bo.setProjectId(group.getProjectId());
+                    }
+
+                    bo.setStatus("0"); // 正常状态
+                    boList.add(bo);
+                }
+            }
+
+                        // 批量插入
+            log.info("准备插入 {} 条分组数据", boList.size());
+            for (GameAthleteCompetitionGroupBo bo : boList) {
+                boolean insertResult = insertByBo(bo);
+                if (!insertResult) {
+                    log.error("插入分组数据失败: groupId={}, athleteId={}, groupIndex={}, trackIndex={}", 
+                             bo.getGroupId(), bo.getAthleteId(), bo.getGroupIndex(), bo.getTrackIndex());
+                }
+            }
+            
+            log.info("分组数据保存完成,共插入 {} 条记录", boList.size());
+            return true;
+
+        } catch (Exception e) {
+            log.error("保存分组结果到数据库失败", e);
+            return false;
+        }
+    }
+
+    /**
+     * 根据分组ID删除分组结果
+     *
+     * @param groupId 分组ID
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteGroupResultByGroupId(Long groupId) {
+        LambdaQueryWrapper<GameAthleteCompetitionGroup> wrapper = Wrappers.lambdaQuery();
+        wrapper.eq(GameAthleteCompetitionGroup::getGroupId, groupId);
+        return baseMapper.delete(wrapper) > 0;
+    }
+}

+ 233 - 8
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventGroupServiceImpl.java

@@ -9,22 +9,30 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.system.domain.bo.GameTeamBo;
 import org.dromara.system.domain.vo.GameEventProjectVo;
 import org.dromara.system.service.IGameEventProjectService;
+import org.dromara.system.service.IGameAthleteService;
+import org.dromara.system.service.IGameTeamService;
+import org.dromara.system.domain.vo.GameAthleteVo;
+import org.dromara.system.domain.vo.GameTeamVo;
 import org.springframework.stereotype.Service;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.InitializingBean;
 import org.dromara.system.domain.bo.GameEventGroupBo;
 import org.dromara.system.domain.constant.GameEventConstant;
 import org.dromara.system.domain.vo.GameEventGroupVo;
 import org.dromara.system.domain.GameEventGroup;
 import org.dromara.system.mapper.GameEventGroupMapper;
 import org.dromara.system.service.IGameEventGroupService;
+import org.dromara.system.service.IGameAthleteCompetitionGroupService;
+import jakarta.annotation.PostConstruct;
+import org.dromara.system.domain.bo.GameAthleteBo;
+import org.dromara.common.core.utils.SpringUtils;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Collection;
-import java.util.Optional;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -34,13 +42,49 @@ import java.util.stream.Collectors;
  * @date 2025-07-30
  */
 @Slf4j
-@RequiredArgsConstructor
 @Service
 public class GameEventGroupServiceImpl implements IGameEventGroupService {
 
-    private final GameEventGroupMapper baseMapper;
+    @Autowired
+    private GameEventGroupMapper baseMapper;
 
-    private final IGameEventProjectService gameEventProjectService;
+    @Autowired
+    private IGameEventProjectService gameEventProjectService;
+
+    private IGameAthleteService gameAthleteService;
+
+    private IGameTeamService gameTeamService;
+
+    private IGameAthleteCompetitionGroupService gameAthleteCompetitionGroupService;
+
+    @PostConstruct
+    public void init() {
+        // 延迟初始化依赖,避免循环依赖
+    }
+
+    // 获取运动员服务的辅助方法
+    private IGameAthleteService getGameAthleteService() {
+        if (gameAthleteService == null) {
+            gameAthleteService = SpringUtils.getBean(IGameAthleteService.class);
+        }
+        return gameAthleteService;
+    }
+
+    // 获取队伍服务的辅助方法
+    private IGameTeamService getGameTeamService() {
+        if (gameTeamService == null) {
+            gameTeamService = SpringUtils.getBean(IGameTeamService.class);
+        }
+        return gameTeamService;
+    }
+
+    // 获取运动员比赛分组服务的辅助方法
+    private IGameAthleteCompetitionGroupService getGameAthleteCompetitionGroupService() {
+        if (gameAthleteCompetitionGroupService == null) {
+            gameAthleteCompetitionGroupService = SpringUtils.getBean(IGameAthleteCompetitionGroupService.class);
+        }
+        return gameAthleteCompetitionGroupService;
+    }
 
     /**
      * 查询赛事分组
@@ -216,4 +260,185 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
                 .eq(GameEventGroup::getEventId, defaultEventId)
         ).get(0);
     }
+
+    /**
+     * 从数据库获取分组结果
+     *
+     * @param groupId 分组ID
+     * @return 分组结果
+     */
+    @Override
+    public Map<String, Object> getGroupResultFromDB(Long groupId) {
+        return getGameAthleteCompetitionGroupService().getGroupResultFromDB(groupId);
+    }
+
+    /**
+     * 生成分组结果
+     *
+     * @param groupId 分组ID
+     * @return 分组结果
+     */
+    @Override
+    public Map<String, Object> generateGroups(Long groupId) {
+        Map<String, Object> result = new HashMap<>();
+
+        try {
+            // 获取分组信息
+            GameEventGroupVo groupInfo = queryById(groupId);
+            if (groupInfo == null) {
+                result.put("success", false);
+                result.put("message", "分组信息不存在");
+                return result;
+            }
+
+            // 获取运动员列表
+            GameAthleteBo athleteBo = new GameAthleteBo();
+            athleteBo.setEventId(groupInfo.getEventId());
+            List<GameAthleteVo> athletes = getGameAthleteService().queryList(athleteBo);
+
+            // 获取队伍列表
+            GameTeamBo teamBo = new GameTeamBo();
+            teamBo.setEventId(groupInfo.getEventId());
+            List<GameTeamVo> teams = getGameTeamService().queryList(teamBo);
+
+            // 获取项目信息
+            GameEventProjectVo project = null;
+            if (groupInfo.getProjectId() != null) {
+                project = gameEventProjectService.queryById(groupInfo.getProjectId());
+            }
+
+            // 筛选符合条件的运动员
+            List<GameAthleteVo> eligibleAthletes = athletes.stream()
+                .filter(athlete -> {
+                    // 检查是否参与该项目
+                    if (athlete.getProjectList() == null) {
+                        return false;
+                    }
+
+                    // 处理项目列表
+                    List<String> projectIds = new ArrayList<>();
+                    if (athlete.getProjectList() != null) {
+                        projectIds = ((List<?>) athlete.getProjectList()).stream()
+                            .map(Object::toString)
+                            .toList();
+                    }
+//                    else if (athlete.getProjectList() instanceof String) {
+//                        String projectListStr = (String) athlete.getProjectList();
+//                        try {
+//                            projectIds = JSONUtil.toList(projectListStr, String.class);
+//                        } catch (Exception e) {
+//                            // 如果不是JSON格式,可能是逗号分隔的字符串
+//                            projectIds = List.of(projectListStr.split(","));
+//                        }
+//                    }
+
+                    String targetProjectId = groupInfo.getProjectId() != null ?
+                        groupInfo.getProjectId().toString() : null;
+                    if (targetProjectId == null || !projectIds.contains(targetProjectId)) {
+                        return false;
+                    }
+
+                    // 检查性别是否匹配
+                    if (StringUtils.isNotBlank(groupInfo.getMemberGender()) &&
+                        !"0".equals(groupInfo.getMemberGender())) {
+                        if (!groupInfo.getMemberGender().equals(
+                            athlete.getGender() != null ? athlete.getGender().toString() : null)) {
+                            return false;
+                        }
+                    }
+
+                    return true;
+                })
+                .toList();
+
+            if (eligibleAthletes.isEmpty()) {
+                result.put("success", false);
+                result.put("message", "没有找到符合条件的运动员");
+                return result;
+            }
+
+            // 随机打乱运动员顺序
+            List<GameAthleteVo> shuffledAthletes = new ArrayList<>(eligibleAthletes);
+            Collections.shuffle(shuffledAthletes);
+
+            // 记录已分配的运动员ID,避免重复分配
+            Set<Long> assignedAthleteIds = new HashSet<>();
+
+            // 分组结果
+            Map<String, GameAthleteVo> groupResult = new HashMap<>();
+
+            // 按组别和道次分配运动员
+            for (int groupIndex = 1; groupIndex <= groupInfo.getIncludeGroupNum(); groupIndex++) {
+                final int currentGroupIndex = groupIndex; // 创建final变量用于lambda表达式
+                for (int track = 1; track <= groupInfo.getTrackNum(); track++) {
+                    // 寻找可用的运动员
+                    GameAthleteVo selectedAthlete = null;
+                    int athleteIndex = 0;
+
+                    while (athleteIndex < shuffledAthletes.size() && selectedAthlete == null) {
+                        GameAthleteVo candidateAthlete = shuffledAthletes.get(athleteIndex);
+
+                        // 检查运动员是否已经被分配
+                        if (assignedAthleteIds.contains(candidateAthlete.getAthleteId())) {
+                            athleteIndex++;
+                            continue;
+                        }
+
+                        // 检查同一组中是否已有同一队伍的运动员
+                        boolean hasSameTeamInGroup = groupResult.entrySet().stream()
+                            .anyMatch(entry -> {
+                                String[] keyParts = entry.getKey().split("-");
+                                if (keyParts.length >= 1) {
+                                    String existingGroup = keyParts[0];
+                                    return existingGroup.equals(String.valueOf(currentGroupIndex)) &&
+                                           entry.getValue().getTeamId().equals(candidateAthlete.getTeamId());
+                                }
+                                return false;
+                            });
+
+                        if (!hasSameTeamInGroup) {
+                            selectedAthlete = candidateAthlete;
+                            // 标记运动员为已分配
+                            assignedAthleteIds.add(candidateAthlete.getAthleteId());
+                        }
+
+                        athleteIndex++;
+                    }
+
+                    // 如果找到了合适的运动员,分配到当前组和道次
+                    if (selectedAthlete != null) {
+                        String key = currentGroupIndex + "-" + track;
+                        groupResult.put(key, selectedAthlete);
+                    }
+                }
+            }
+
+            // 保存分组结果到数据库
+            try {
+                boolean saveSuccess = getGameAthleteCompetitionGroupService().saveGroupResult(groupId, groupResult, groupInfo);
+                if (saveSuccess) {
+                    log.info("分组结果已保存到数据库,分组ID: {}", groupId);
+                } else {
+                    log.warn("分组结果保存到数据库失败,分组ID: {}", groupId);
+                }
+            } catch (Exception e) {
+                log.error("保存分组结果到数据库时发生错误", e);
+            }
+
+            // 构建返回结果
+            result.put("success", true);
+            result.put("groupResult", groupResult);
+            result.put("totalAthletes", eligibleAthletes.size());
+            result.put("groupInfo", groupInfo);
+            result.put("project", project);
+            result.put("teams", teams);
+
+        } catch (Exception e) {
+            log.error("生成分组失败", e);
+            result.put("success", false);
+            result.put("message", "生成分组失败: " + e.getMessage());
+        }
+
+        return result;
+    }
 }

+ 5 - 9
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameScoreServiceImpl.java

@@ -1,10 +1,8 @@
 package org.dromara.system.service.impl;
 
 import cn.hutool.core.bean.BeanUtil;
-import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
-import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -13,8 +11,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.dromara.common.redis.utils.RedisUtils;
-import org.dromara.system.domain.GameAthlete;
-import org.dromara.system.domain.GameEventProject;
 import org.dromara.system.domain.constant.GameEventConstant;
 import org.dromara.system.domain.constant.ProjectClassification;
 import org.dromara.system.domain.constant.SortType;
@@ -34,7 +30,6 @@ import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import jakarta.servlet.http.HttpServletResponse;
-import org.dromara.common.excel.utils.ExcelUtil;
 import org.dromara.system.domain.bo.GameEventProjectBo;
 import org.dromara.system.domain.bo.GameTeamBo;
 
@@ -62,6 +57,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
     private final GameEventProjectMapper projectMapper;
     private final IGameEventProjectService gameEventProjectService;
 
+
     /**
      * 查询成绩
      *
@@ -178,7 +174,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
      * 保存前的数据校验
      */
     private void validEntityBeforeSave(GameScore entity) {
-        //TODO 做一些数据校验,如唯一约束
+
     }
 
     /**
@@ -697,10 +693,10 @@ public class GameScoreServiceImpl implements IGameScoreService {
         for (int i = 0; i < sortedByPoints.size(); i++) {
             GameScoreVo score = sortedByPoints.get(i);
             int currentPoints = score.getScorePoint() != null ? score.getScorePoint() : 0;
-            
+
             // 如果不是第一个,检查是否与前一个积分相同
             if (i > 0) {
-                int previousPoints = sortedByPoints.get(i - 1).getScorePoint() != null ? 
+                int previousPoints = sortedByPoints.get(i - 1).getScorePoint() != null ?
                     sortedByPoints.get(i - 1).getScorePoint() : 0;
                 if (currentPoints != previousPoints) {
                     currentRank = i + 1;
@@ -779,7 +775,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
             Long teamId = teamEntry.getKey();
             BigDecimal currentPerformance = teamEntry.getValue();
             int points = i < pointValues.size() ? pointValues.get(i) : 0;
-            
+
             // 如果不是第一个,检查是否与前一个成绩相同
             if (i > 0) {
                 BigDecimal previousPerformance = sortedTeamsByPerformance.get(i - 1).getValue();

+ 30 - 0
ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/GameAthleteCompetitionGroupMapper.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.dromara.system.mapper.GameAthleteCompetitionGroupMapper">
+
+    <resultMap type="org.dromara.system.domain.GameAthleteCompetitionGroup" id="GameAthleteCompetitionGroupResult">
+        <result property="id" column="id"/>
+        <result property="groupId" column="group_id"/>
+        <result property="eventId" column="event_id"/>
+        <result property="projectId" column="project_id"/>
+        <result property="athleteId" column="athlete_id"/>
+        <result property="teamId" column="team_id"/>
+        <result property="groupIndex" column="group_index"/>
+        <result property="trackIndex" column="track_index"/>
+        <result property="athleteCode" column="athlete_code"/>
+        <result property="athleteName" column="athlete_name"/>
+        <result property="teamName" column="team_name"/>
+        <result property="status" column="status"/>
+        <result property="remark" column="remark"/>
+        <result property="createTime" column="create_time"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createBy" column="create_by"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="updateBy" column="update_by"/>
+        <result property="tenantId" column="tenant_id"/>
+        <result property="createDept" column="create_dept"/>
+    </resultMap>
+
+</mapper>