28 Комити 5e98545d50 ... 54b867c132

Аутор SHA1 Порука Датум
  wenkai 54b867c132 feat:首页背景图 пре 4 недеља
  zhou 0d5d1117a0 feat(game-group): 解决分组模块时间格式bug пре 4 недеља
  zhou 1c41d7ac7b feat(game-advice): 解决建议模块bug пре 1 месец
  zhou 9fbd433926 feat(game-event): 解决队伍导入bug пре 1 месец
  zhou 8cf8e49b41 feat(game-event): 查询成绩接口更新 пре 1 месец
  zhou 7e9c0cec1a feat(game-event): 分组bug修改 пре 1 месец
  zhou c08bf3e4c6 feat(game-event): 增加赛事项目库功能并优化相关接口 пре 1 месец
  zhou 446af1cefc refactor(game-score): 添加加分数据相关API пре 1 месец
  zhou 1c6a455832 refactor(game-event): 重构裁判二维码生成逻辑 пре 1 месец
  zhou 4aa5aadf7d feat(system): 优化数据结构和实现裁判二维码生成 пре 1 месец
  zhou 5d3ac9c0e3 app端接口-查看获奖名单bug修复2 пре 1 месец
  zhou 262abff9f1 app端接口bug修复 пре 1 месец
  zhou 360d3c5123 视力名称修改 пре 1 месец
  zhou 9f9886f52d refactor(游戏赛事模块):优化数据导出和字典格式 пре 1 месец
  zhou 78b2b15a85 refactor(athlete): 优化运动员数据处理逻辑 пре 1 месец
  zhou 14ed197b40 feat(game-event): 添加体质测试和设备成绩数据上传接口 пре 1 месец
  zhou 185fe19450 feat(game-event): 添加澳亚特设备成绩上传接口 пре 1 месец
  zhou 3140d21baf Merge branch 'main' into dev пре 1 месец
  zhou b873080166 Merge branch 'dev_zlt' into dev пре 1 месец
  zhou 2d2e0008ba refactor(game-event): 修复新增赛事的bug пре 1 месец
  zhou 1b644fb1dc Merge remote-tracking branch 'gogs/main' пре 1 месец
  wenkai e5cd03aa84 Merge branch 'wk-dev-8-13' into dev пре 1 месец
  wenkai 476591eac3 fix:增加校验 пре 1 месец
  zhou 919243da45 Merge branch 'dev_zlt' into dev пре 1 месец
  zhou fb5bbc346b feat(game-event): 新增小程序端成绩上传功能并优化分组逻辑 пре 1 месец
  wenkai 74cca057ba fix:增加校验 пре 1 месец
  zhou 8817c085e1 feat(game-event): 实现微信小程序用户登录和赛事信息获取功能- 新增微信小程序配置项- 实现用户登录接口,支持微信小程序登录 пре 1 месец
  wenkai bc94d2e21f 线上 пре 1 месец
100 измењених фајлова са 4814 додато и 308 уклоњено
  1. 8 4
      pom.xml
  2. 0 6
      ruoyi-admin/pom.xml
  3. 1 1
      ruoyi-admin/src/main/resources/application-dev.yml
  4. 5 5
      ruoyi-admin/src/main/resources/application-prod.yml
  5. 9 1
      ruoyi-admin/src/main/resources/application.yml
  6. BIN
      ruoyi-admin/src/main/resources/fonts/simhei.ttf
  7. BIN
      ruoyi-admin/src/main/resources/fonts/simsun.ttf
  8. BIN
      ruoyi-admin/src/main/resources/fonts/yahei.ttf
  9. 0 1
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java
  10. 0 2
      ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java
  11. 6 0
      ruoyi-modules/ruoyi-game-event/pom.xml
  12. 50 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/config/WebClientConfig.java
  13. 1 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameEventController.java
  14. 27 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameEventGroupController.java
  15. 23 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameEventProjectController.java
  16. 13 13
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameNavigatorController.java
  17. 30 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameRefereeController.java
  18. 53 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameScoreController.java
  19. 11 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameTeamController.java
  20. 2 11
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/AppIndexController.java
  21. 86 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/ElectrometerController.java
  22. 129 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/PhysicalController.java
  23. 97 4
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/ScoreController.java
  24. 53 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/UserEventController.java
  25. 91 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameAthleteCompetitionGroup.java
  26. 6 6
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameEvent.java
  27. 3 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameEventGroup.java
  28. 5 5
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameEventProject.java
  29. 4 4
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameScore.java
  30. 12 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/app/AppIndexVo.java
  31. 86 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/app/GameUser.java
  32. 27 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/app/QRCodeReferee.java
  33. 15 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/app/WxLoginResult.java
  34. 4 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/AdviceBo.java
  35. 90 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/AppScoreUploadBo.java
  36. 1 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameAthleteBo.java
  37. 96 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameAthleteCompetitionGroupBo.java
  38. 10 11
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameEventBo.java
  39. 3 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameEventGroupBo.java
  40. 7 7
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameEventProjectBo.java
  41. 1 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameRefereeBo.java
  42. 6 6
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameScoreBo.java
  43. 1 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/GameTeamBo.java
  44. 1 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/AdviceVo.java
  45. 35 3
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/AppScoreVo.java
  46. 90 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameAthleteCompetitionGroupVo.java
  47. 1 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameAthleteVo.java
  48. 1 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameEventGroupVo.java
  49. 13 9
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameEventProjectVo.java
  50. 8 9
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameEventVo.java
  51. 1 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameRefereeVo.java
  52. 9 3
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameScoreSummaryExportVo.java
  53. 6 6
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameScoreVo.java
  54. 5 5
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameTeamVo.java
  55. 48 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/DistanceScoreUploadVo.java
  56. 86 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/ElectrometerDataItemVo.java
  57. 62 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/ElectrometerScoreUploadVo.java
  58. 96 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/GameUserVo.java
  59. 29 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/PhysicalDeviceVo.java
  60. 24 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/PhysicalTestVo.java
  61. 34 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/UserEventInfoVo.java
  62. 52 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/UserLoginVo.java
  63. 15 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/GameAthleteCompetitionGroupMapper.java
  64. 9 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/GameAthleteMapper.java
  65. 3 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/GameRefereeMapper.java
  66. 12 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/GameScoreMapper.java
  67. 14 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/app/ElectrometerMapper.java
  68. 41 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/app/GameUserMapper.java
  69. 95 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameAthleteCompetitionGroupService.java
  70. 2 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameEventConfigService.java
  71. 17 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameEventGroupService.java
  72. 19 2
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameEventProjectService.java
  73. 2 2
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameNavigatorService.java
  74. 16 2
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameScoreService.java
  75. 8 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameTeamService.java
  76. 211 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/app/AppScoreValidationService.java
  77. 43 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/app/IElectrometerService.java
  78. 33 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/app/IPhysicalTestService.java
  79. 12 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/app/IUserEventService.java
  80. 1 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/dbbackup/DatabaseBackupRestoreService.java
  81. 11 12
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/AdviceServiceImpl.java
  82. 295 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameAthleteCompetitionGroupServiceImpl.java
  83. 6 6
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameAthleteServiceImpl.java
  84. 13 3
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventConfigServiceImpl.java
  85. 234 9
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventGroupServiceImpl.java
  86. 110 5
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventProjectServiceImpl.java
  87. 11 32
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventServiceImpl.java
  88. 160 12
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameRefereeServiceImpl.java
  89. 357 75
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameScoreServiceImpl.java
  90. 26 4
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameTeamServiceImpl.java
  91. 0 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/IEnrollServiceImpl.java
  92. 20 4
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/IGameNavigatorServiceImpl.java
  93. 409 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/app/ElectrometerServiceImpl.java
  94. 386 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/app/PhysicalTestServiceImpl.java
  95. 293 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/app/UserEventServiceImpl.java
  96. 1 1
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/utils/FilteredCommentInputStream.java
  97. 194 0
      ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/utils/QRCodeUtils.java
  98. 30 0
      ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/GameAthleteCompetitionGroupMapper.xml
  99. 25 5
      ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/GameScoreMapper.xml
  100. 7 0
      ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/app/GameUserMapper.xml

+ 8 - 4
pom.xml

@@ -80,6 +80,7 @@
                 <monitor.username>ruoyi</monitor.username>
                 <monitor.password>123456</monitor.password>
             </properties>
+
         </profile>
         <profile>
             <id>test</id>
@@ -90,10 +91,8 @@
                 <monitor.username>ruoyi</monitor.username>
                 <monitor.password>123456</monitor.password>
             </properties>
-            <activation>
-                <!-- 默认环境 -->
-                <activeByDefault>true</activeByDefault>
-            </activation>
+
+
         </profile>
         <profile>
             <id>prod</id>
@@ -103,6 +102,11 @@
                 <monitor.username>ruoyi</monitor.username>
                 <monitor.password>123456</monitor.password>
             </properties>
+            <activation>
+                <!-- 默认环境 -->
+                <activeByDefault>true</activeByDefault>
+            </activation>
+
         </profile>
     </profiles>
 

+ 0 - 6
ruoyi-admin/pom.xml

@@ -75,12 +75,6 @@
             <artifactId>ruoyi-job</artifactId>
         </dependency>
 
-        <dependency>
-            <groupId>org.dromara</groupId>
-            <artifactId>ruoyi-common-caffeine</artifactId>
-            <version>5.4.1</version>
-        </dependency>
-
         <!-- 代码生成-->
         <dependency>
             <groupId>org.dromara</groupId>

+ 1 - 1
ruoyi-admin/src/main/resources/application-dev.yml

@@ -50,7 +50,7 @@ spring:
           # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
           # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
 #          url: jdbc:mysql://192.168.1.146:3306/game_event?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
-          url: jdbc:mysql://localhost:3306/game_event?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username: root
 #          password: P@ssw0rd
           password: 123456

+ 5 - 5
ruoyi-admin/src/main/resources/application-prod.yml

@@ -4,7 +4,7 @@ spring.servlet.multipart.location: /ruoyi/server/temp
 --- # 监控中心配置
 spring.boot.admin.client:
   # 增加客户端开关
-  enabled: true
+  enabled: false
   url: http://182.92.79.54:9090/admin
   instance:
     service-host-type: IP
@@ -16,7 +16,7 @@ spring.boot.admin.client:
 
 --- # snail-job 配置
 snail-job:
-  enabled: true
+  enabled: false
   # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
   group: "ruoyi_group"
   # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
@@ -52,7 +52,7 @@ spring:
           driverClassName: com.mysql.cj.jdbc.Driver
           # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
           # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
-          url: jdbc:mysql://localhost:3306/game_event?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
+          url: jdbc:mysql://182.92.79.54:3306/game_event?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
           username: game_event
           password: TxtYJXEtnC6ecmBw
 #        # 从库数据源
@@ -101,13 +101,13 @@ spring:
 spring.data:
   redis:
     # 地址
-    host: 127.0.0.1
+    host: 182.92.79.54
     # 端口,默认为6379
     port: 6379
     # 数据库索引
     database: 0
     # redis 密码必须配置
-#    password: game_event
+    password: game_event
     # 连接超时时间
     timeout: 10s
     # 是否开启ssl

+ 9 - 1
ruoyi-admin/src/main/resources/application.yml

@@ -73,6 +73,8 @@ spring:
   mvc:
     # 设置静态资源路径 防止所有请求都去查静态资源
     static-path-pattern: /static/**
+    async:
+      request-timeout: 300000  # 5分钟
     format:
       date-time: yyyy-MM-dd HH:mm:ss
   jackson:
@@ -269,7 +271,7 @@ websocket:
 --- # warm-flow工作流配置
 warm-flow:
   # 是否开启工作流,默认true
-  enabled: true
+  enabled: false
   # 是否开启设计器ui
   ui: true
   # 默认Authorization,如果有多个token,用逗号分隔
@@ -282,3 +284,9 @@ warm-flow:
     - 255,205,23
     ## 已办理
     - 157,255,0
+
+# 微信小程序配置
+wechat:
+  miniapp:
+    appid: wx017241c84de43b7a
+    secret: 91ee2725605ba0ae73829cf4538395ac

BIN
ruoyi-admin/src/main/resources/fonts/simhei.ttf


BIN
ruoyi-admin/src/main/resources/fonts/simsun.ttf


BIN
ruoyi-admin/src/main/resources/fonts/yahei.ttf


+ 0 - 1
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java

@@ -94,7 +94,6 @@ public class TestDemoServiceImpl implements ITestDemoService {
      * @param entity 实体类数据
      */
     private void validEntityBeforeSave(TestDemo entity) {
-        //TODO 做一些数据校验,如唯一约束
     }
 
     @Override

+ 0 - 2
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java

@@ -75,13 +75,11 @@ public class TestTreeServiceImpl implements ITestTreeService {
      * @param entity 实体类数据
      */
     private void validEntityBeforeSave(TestTree entity) {
-        //TODO 做一些数据校验,如唯一约束
     }
 
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
         if (isValid) {
-            //TODO 做一些业务上的校验,判断是否需要校验
         }
         return baseMapper.deleteByIds(ids) > 0;
     }

+ 6 - 0
ruoyi-modules/ruoyi-game-event/pom.xml

@@ -79,6 +79,12 @@
             <artifactId>ruoyi-common-web</artifactId>
         </dependency>
 
+        <!-- WebFlux依赖,用于WebClient -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-webflux</artifactId>
+        </dependency>
+
         <dependency>
             <groupId>org.dromara</groupId>
             <artifactId>ruoyi-common-idempotent</artifactId>

+ 50 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/config/WebClientConfig.java

@@ -0,0 +1,50 @@
+package org.dromara.system.config;
+
+import io.netty.channel.ChannelOption;
+import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.handler.timeout.WriteTimeoutHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.netty.http.client.HttpClient;
+import reactor.netty.resources.ConnectionProvider;
+
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * WebClient配置类
+ * 用于微信API调用,替代已弃用的RestTemplate
+ *
+ * @author zlt
+ */
+@Configuration
+public class WebClientConfig {
+
+    @Bean
+    public WebClient webClient() {
+        // 连接池配置
+        ConnectionProvider connectionProvider = ConnectionProvider.builder("custom")
+            .maxConnections(100)
+            .maxIdleTime(Duration.ofSeconds(20))
+            .maxLifeTime(Duration.ofSeconds(60))
+            .pendingAcquireTimeout(Duration.ofSeconds(60))
+            .evictInBackground(Duration.ofSeconds(120))
+            .build();
+
+        // HTTP客户端配置
+        HttpClient httpClient = HttpClient.create(connectionProvider)
+            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000) // 连接超时10秒
+            .responseTimeout(Duration.ofSeconds(30)) // 响应超时30秒
+            .doOnConnected(conn -> conn
+                .addHandlerLast(new ReadTimeoutHandler(30, TimeUnit.SECONDS)) // 读取超时30秒
+                .addHandlerLast(new WriteTimeoutHandler(30, TimeUnit.SECONDS)) // 写入超时30秒
+            );
+
+        return WebClient.builder()
+            .clientConnector(new ReactorClientHttpConnector(httpClient))
+            .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024)) // 2MB
+            .build();
+    }
+} 

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

@@ -131,6 +131,7 @@ public class GameEventController extends BaseController {
      */
     @SaCheckPermission("system:gameEvent:edit")
     @Log(title = "赛事默认状态修改", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
     @PutMapping("/changeEventDefault")
     public R<Void> changeEventDefault(@RequestBody GameEventBo bo) {
         // 如果修改的对象 原本是默认并且准备取消则禁止

+ 27 - 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,30 @@ public class GameEventGroupController extends BaseController {
                           @PathVariable Long[] groupIds) {
         return toAjax(gameEventGroupService.deleteWithValidByIds(List.of(groupIds), true));
     }
+
+    /**
+     * 生成分组结果
+     *
+     * @param groupId 分组ID
+     */
+    @SaCheckPermission("system:gameEventGroup:query")
+    @Log(title = "赛事生成分组结果", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @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));
+    }
 }

+ 23 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameEventProjectController.java

@@ -1,11 +1,13 @@
 package org.dromara.system.controller;
 
+import java.util.Collection;
 import java.util.List;
 
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.system.domain.GameEventProject;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -37,7 +39,7 @@ public class GameEventProjectController extends BaseController {
     private final IGameEventProjectService gameEventProjectService;
 
     /**
-     * 查询赛事项目列表
+     * 查询默认赛事项目列表
      */
     @SaCheckPermission("system:gameEventProject:list")
     @GetMapping("/list")
@@ -45,6 +47,15 @@ public class GameEventProjectController extends BaseController {
         return gameEventProjectService.queryPageList(bo, pageQuery);
     }
 
+    /**
+     * 查询所有赛事项目列表
+     */
+    @SaCheckPermission("system:gameEventProject:list")
+    @GetMapping("/listAll")
+    public TableDataInfo<GameEventProjectVo> listAll(GameEventProjectBo bo, PageQuery pageQuery) {
+        return gameEventProjectService.queryAllProjectList(bo, pageQuery);
+    }
+
     /**
      * 导出赛事项目列表
      */
@@ -79,6 +90,17 @@ public class GameEventProjectController extends BaseController {
         return toAjax(gameEventProjectService.insertByBo(bo));
     }
 
+    /**
+     * 批量新增赛事项目
+     */
+    @SaCheckPermission("system:gameEventProject:BatchAdd")
+    @Log(title = "赛事项目", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping("/BatchAdd")
+    public R<Void> BatchAdd(@Validated(AddGroup.class) @RequestBody Collection<GameEventProject> projectList) {
+        return toAjax(gameEventProjectService.BatchInsertByProjectList(projectList));
+    }
+
     /**
      * 修改赛事项目
      */

+ 13 - 13
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameNavigatorController.java

@@ -30,7 +30,7 @@ import java.io.IOException;
 import java.util.List;
 
 /**
- * 赛事主导航表管理
+ * app端菜单管理
  *
  * @author system
  * @date 2025-01-20
@@ -46,7 +46,7 @@ public class GameNavigatorController extends BaseController {
     private final IEventMenuService eventMenuService;
 
     /**
-     * 查询底部主导航表列表
+     * 查询app端菜单列表
      */
     @SaCheckPermission("scenic:navigator:list")
     @GetMapping("/list")
@@ -55,15 +55,15 @@ public class GameNavigatorController extends BaseController {
     }
 
     /**
-     * 导出底部主导航表列表
+     * 导出app端菜单列表
      */
     @SaCheckPermission("scenic:navigator:export")
-    @Log(title = "底部主导航表", businessType = BusinessType.EXPORT)
+    @Log(title = "导出app端菜单列表", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     public void export(GameNavigatorBo bo, HttpServletResponse response) {
         try {
             List<GameNavigatorVo> list = gameNavigatorService.queryList(bo);
-            ExcelUtil.exportExcel(list, "底部主导航表", GameNavigatorVo.class, response);
+            ExcelUtil.exportExcel(list, "app端菜单列表", GameNavigatorVo.class, response);
         } catch (Exception e) {
             // 如果导出过程中出现异常,重置Content-Type并返回错误信息
             response.reset();
@@ -78,7 +78,7 @@ public class GameNavigatorController extends BaseController {
     }
 
     /**
-     * 获取底部主导航表详细信息
+     * 获取app端菜单详细信息
      *
      * @param navId 主键
      */
@@ -90,10 +90,10 @@ public class GameNavigatorController extends BaseController {
     }
 
     /**
-     * 新增底部主导航表
+     * 新增app端菜单
      */
     @SaCheckPermission("scenic:navigator:add")
-    @Log(title = "底部主导航表", businessType = BusinessType.INSERT)
+    @Log(title = "新增app端菜单", businessType = BusinessType.INSERT)
     @RepeatSubmit()
     @PostMapping()
     public R<Void> add(@Validated(AddGroup.class) @RequestBody GameNavigatorBo bo) {
@@ -101,10 +101,10 @@ public class GameNavigatorController extends BaseController {
     }
 
     /**
-     * 修改底部主导航表
+     * 修改app端菜单
      */
     @SaCheckPermission("scenic:navigator:edit")
-    @Log(title = "底部主导航表", businessType = BusinessType.UPDATE)
+    @Log(title = "修改app端菜单", businessType = BusinessType.UPDATE)
     @RepeatSubmit()
     @PutMapping()
     public R<Void> edit(@Validated(EditGroup.class) @RequestBody GameNavigatorBo bo) {
@@ -112,12 +112,12 @@ public class GameNavigatorController extends BaseController {
     }
 
     /**
-     * 删除底部主导航表
+     * 删除app端菜单
      *
      * @param ids 主键串
      */
     @SaCheckPermission("scenic:navigator:remove")
-    @Log(title = "底部主导航表", businessType = BusinessType.DELETE)
+    @Log(title = "删除app端菜单", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
     public R<Void> remove(@NotEmpty(message = "主键不能为空")
                           @PathVariable Long[] ids) {
@@ -125,7 +125,7 @@ public class GameNavigatorController extends BaseController {
     }
 
     /**
-     * 获取启用的底部主导航列表
+     * 获取启用的app端菜单
      */
     @GetMapping("/enabled")
     public R<List<GameNavigatorVo>> getEnabledNavList() {

+ 30 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/GameRefereeController.java

@@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.*;
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.dromara.system.domain.app.QRCodeReferee;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.validation.annotation.Validated;
 import org.dromara.common.idempotent.annotation.RepeatSubmit;
@@ -21,6 +22,8 @@ import org.dromara.system.domain.vo.GameRefereeVo;
 import org.dromara.system.domain.bo.GameRefereeBo;
 import org.dromara.system.service.IGameRefereeService;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.system.utils.QRCodeUtils;
+import com.fasterxml.jackson.databind.ObjectMapper;
 
 /**
  * 裁判
@@ -35,6 +38,8 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
 public class GameRefereeController extends BaseController {
 
     private final IGameRefereeService gameRefereeService;
+    private final QRCodeUtils qrCodeUtils;
+    private final ObjectMapper objectMapper;
 
     /**
      * 查询裁判列表
@@ -103,7 +108,6 @@ public class GameRefereeController extends BaseController {
         return toAjax(gameRefereeService.deleteWithValidByIds(List.of(refereeIds), true));
     }
 
-
     /**
      * 查询裁判数量
      * @return
@@ -113,4 +117,29 @@ public class GameRefereeController extends BaseController {
         return R.ok(gameRefereeService.countReferee());
     }
 
+    /**
+     * 生成裁判二维码
+     *
+     * @param refereeId 裁判ID
+     * @return 二维码Base64数据
+     */
+    @SaCheckPermission("system:gameReferee:edit")
+    @GetMapping("/qrcode/{refereeId}")
+    public R<String> generateQRCode(@NotNull(message = "裁判ID不能为空") @PathVariable Long refereeId) {
+        try {
+            // 获取裁判信息
+            GameRefereeVo referee = gameRefereeService.queryById(refereeId);
+            if (referee == null) {
+                return R.fail("裁判信息不存在");
+            }
+            // 调用工具类封装的裁判二维码生成逻辑
+//            String qrCodeBase64 = qrCodeUtils.generateRefereeQRCodeBase64(referee);
+            String qrCodeBase64 = qrCodeUtils.buildRefereeData2(referee);
+
+            return R.ok("二维码生成成功", qrCodeBase64);
+        } catch (Exception e) {
+            return R.fail("生成二维码失败: " + e.getMessage());
+        }
+    }
+
 }

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

@@ -22,6 +22,10 @@ import org.dromara.system.domain.vo.GameScoreVo;
 import org.dromara.system.domain.bo.GameScoreBo;
 import org.dromara.system.service.IGameScoreService;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.HashMap;
+
 
 /**
  * 成绩
@@ -161,4 +165,53 @@ public class GameScoreController extends BaseController {
     public void exportScoresSummary(@RequestParam("eventId") Long eventId, HttpServletResponse response) {
         gameScoreService.exportScoresSummary(eventId, response);
     }
+
+    /**
+     * 获取加分数据
+     */
+    @SaCheckPermission("system:gameScore:list")
+    @GetMapping("/getBonusData")
+    public R<Map<String, Object>> getBonusData(@RequestParam("eventId") Long eventId) {
+        return R.ok(gameScoreService.getBonusData(eventId));
+    }
+
+    /**
+     * 更新加分数据
+     */
+    @SaCheckPermission("system:gameScore:edit")
+    @Log(title = "更新加分数据", businessType = BusinessType.UPDATE)
+    @PutMapping("/updateBonusData")
+    public R<Void> updateBonusData(@RequestBody Map<String, Object> data) {
+        return toAjax(gameScoreService.updateBonusData(data));
+    }
+
+    /**
+     * 导出加分Excel
+     */
+    @SaCheckPermission("system:gameScore:export")
+    @Log(title = "导出加分Excel", businessType = BusinessType.EXPORT)
+    @PostMapping("/exportBonusExcel")
+    public void exportBonusExcel(
+            @RequestParam("eventId") Long eventId,
+            @RequestParam("data") String dataJson,
+            @RequestParam("projects") String projectsJson,
+            HttpServletResponse response) {
+
+        try {
+            // 解析JSON字符串
+            ObjectMapper objectMapper = new ObjectMapper();
+            List<Map<String, Object>> bonusData = objectMapper.readValue(dataJson,
+                new TypeReference<List<Map<String, Object>>>() {});
+            List<Map<String, Object>> projects = objectMapper.readValue(projectsJson,
+                new TypeReference<List<Map<String, Object>>>() {});
+
+            Map<String, Object> data = new HashMap<>();
+            data.put("data", bonusData);
+            data.put("projects", projects);
+
+            gameScoreService.exportBonusExcel(data, response);
+        } catch (Exception e) {
+            throw new RuntimeException("参数解析失败:" + e.getMessage());
+        }
+    }
 }

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

@@ -22,6 +22,7 @@ import org.dromara.system.domain.GameTeam;
 import org.dromara.system.domain.bo.GameTeamBo;
 import org.dromara.system.domain.request.UpdateTeamAthletesRequest;
 import org.dromara.system.domain.vo.GameTeamVo;
+import org.dromara.system.service.IGameEventService;
 import org.dromara.system.service.IGameTeamService;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -30,6 +31,7 @@ import org.springframework.web.multipart.MultipartFile;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 参赛队伍
@@ -45,6 +47,7 @@ import java.util.List;
 public class GameTeamController extends BaseController {
 
     private final IGameTeamService gameTeamService;
+    private final IGameEventService gameEventService;
 
     /**
      * 查询参赛队伍列表
@@ -65,6 +68,13 @@ public class GameTeamController extends BaseController {
         ExcelResult<GameTeamVo> excelResult = null;
         try {
             excelResult = ExcelUtil.importExcel(file.getInputStream(), GameTeamVo.class, true);
+            Map<String, Long> eventIdNameMap = gameEventService.getEventIdNameMap();
+            excelResult.getList().stream().forEach(item -> {
+                //根据导入的赛事名称查询对应的赛事ID
+                if (eventIdNameMap.containsKey(item.getEventName())){
+                    item.setEventId(eventIdNameMap.get(item.getEventName()));
+                }
+            });
             List<GameTeam> list = MapstructUtils.convert(excelResult.getList(), GameTeam.class);
             gameTeamService.saveBatch(list);
         } catch (IOException e) {
@@ -156,6 +166,7 @@ public class GameTeamController extends BaseController {
      */
     @SaCheckPermission("system:gameTeam:edit")
     @Log(title = "参赛队伍", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
     @PutMapping("/updateAthletes")
     public R<Void> updateTeamAthletes(@RequestBody UpdateTeamAthletesRequest request) {
         return toAjax(gameTeamService.updateTeamAthletes(request.getTeamId(), request.getAthleteIds()));

+ 2 - 11
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/AppIndexController.java

@@ -1,25 +1,16 @@
 package org.dromara.system.controller.app;
 
-import cn.dev33.satoken.annotation.SaCheckPermission;
 import cn.dev33.satoken.annotation.SaIgnore;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
-import org.dromara.common.core.domain.R;
-import org.dromara.common.mybatis.core.page.PageQuery;
-import org.dromara.common.mybatis.core.page.TableDataInfo;
-import org.dromara.system.domain.bo.EventMenuBo;
-import org.dromara.system.domain.vo.GameNavigatorVo;
-import org.dromara.system.domain.vo.VsNavBottomMainVo;
+import org.dromara.system.domain.app.AppIndexVo;
 import org.dromara.system.service.IGameNavigatorService;
-import org.dromara.system.service.IVsNavBottomMainService;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-import java.util.List;
-
 /**
  * app-首页菜单
  */
@@ -38,7 +29,7 @@ public class AppIndexController {
      * 获取赛事导航关联菜单列表
      */
     @GetMapping("/{eventId}")
-    public List<GameNavigatorVo> listRelateMenu(@PathVariable Long eventId) {
+    public AppIndexVo listRelateMenu(@PathVariable Long eventId) {
         return gameNavigatorService.listAppNavByEventId(eventId);
     }
 }

+ 86 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/ElectrometerController.java

@@ -0,0 +1,86 @@
+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.system.domain.vo.app.DistanceScoreUploadVo;
+import org.dromara.system.domain.vo.app.ElectrometerDataItemVo;
+import org.dromara.system.domain.vo.app.ElectrometerScoreUploadVo;
+import org.dromara.system.service.app.IElectrometerService;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 澳亚特设备API
+ *
+ * @author zlt
+ * @date 2025-09-01
+ */
+@SaIgnore
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/api/electrometer")
+public class ElectrometerController {
+
+    private final IElectrometerService electrometerService;
+
+    /**
+     * 下载数据
+     *
+     * @param tjsdm 项目类型(1-径赛, 2-田赛)
+     * @param eventId 赛事ID (通过请求头 X-Dbname 传递)
+     * @return R<List<ElectrometerDataItemVo>>
+     */
+    @GetMapping("/aytmatchroadinfo")
+    public R<List<ElectrometerDataItemVo>> getMatchRoadInfo(
+            @RequestParam(value = "tjsdm", defaultValue = "1") String tjsdm,
+            @RequestHeader(value = "X-Dbname") Long eventId) {
+        try {
+            List<ElectrometerDataItemVo> result = electrometerService.getMatchRoadInfo(eventId, tjsdm);
+            return R.ok(result);
+        } catch (Exception e) {
+            return R.fail("下载数据失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 计时类项目成绩上传(径赛)
+     *
+     * @param scoreUpload 成绩上传数据
+     * @param eventId 赛事ID (通过请求头 X-Dbname 传递)
+     * @return R<String> 上传结果
+     */
+    @PostMapping("/aytuploadscore/timer")
+    public R<String> uploadScore(
+        @RequestBody List<ElectrometerScoreUploadVo> scoreUpload,
+        @RequestHeader(value = "X-Dbname") Long eventId) {
+        try {
+            String result = electrometerService.uploadScore(eventId, scoreUpload);
+            return R.ok(result);
+        } catch (Exception e) {
+            return R.fail("计时类项目成绩上传失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 远度距离类项目成绩上传
+     *
+     * @param scoreUpload 成绩上传数据
+     * @param eventId 赛事ID (通过请求头 X-Dbname 传递)
+     * @return R<String> 上传结果
+     */
+    @PostMapping("/aytuploadscore/distance")
+    public R<String> uploadScore(
+        @RequestBody DistanceScoreUploadVo scoreUpload,
+        @RequestHeader(value = "X-Dbname") Long eventId) {
+        try {
+            String result = electrometerService.uploadDistanceScore(eventId, scoreUpload);
+            return R.ok(result);
+        } catch (Exception e) {
+            return R.fail("远度距离类项目成绩上传失败:" + e.getMessage());
+        }
+    }
+}

+ 129 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/PhysicalController.java

@@ -0,0 +1,129 @@
+package org.dromara.system.controller.app;
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.system.domain.vo.app.PhysicalDeviceVo;
+import org.dromara.system.domain.vo.app.PhysicalTestVo;
+import org.dromara.system.service.app.IPhysicalTestService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 体质测试成绩数据上传接口
+ */
+@SaIgnore
+@Slf4j
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/api/physical")
+public class PhysicalController {
+
+    private final IPhysicalTestService physicalTestService;
+
+    // 默认关联的赛事ID
+    private static final Long DEFAULT_EVENT_ID = 1950743780869537804L;
+
+    @PostMapping("/upload")
+    public ResponseEntity<Map<String, Object>> uploadTestData(@RequestBody List<PhysicalTestVo> results) {
+        try {
+            log.info("收到体质测试数据上传请求,数据条数: {}", results.size());
+
+            // 1. 校验必填字段
+            for (PhysicalTestVo result : results) {
+                if (result.getTestKey() == null || result.getUserId() == null ||
+                    result.getTestTime() == null || result.getResultData() == null) {
+                    log.warn("数据校验失败,缺少必填字段: {}", result);
+                    return ResponseEntity.ok(Map.of("success", false, "msg", "缺少必填字段"));
+                }
+            }
+
+            // 2. 处理数据(存入数据库、日志等)
+            log.info("开始处理体质测试数据,关联赛事ID: {}", DEFAULT_EVENT_ID);
+            boolean success = physicalTestService.processPhysicalTestData(results, DEFAULT_EVENT_ID);
+
+            if (!success) {
+                log.error("体质测试数据处理失败");
+                return ResponseEntity.ok(Map.of("success", false, "msg", "数据处理失败"));
+            }
+
+            // 3. 返回成功
+            log.info("体质测试数据处理成功,共处理 {} 条数据", results.size());
+            return ResponseEntity.ok(Map.of("success", true, "msg", "数据上传成功"));
+        } catch (Exception e) {
+            log.error("体质测试数据上传异常", e);
+            return ResponseEntity.ok(Map.of("success", false, "msg", e.getMessage()));
+        }
+    }
+
+    @PostMapping("/SendResults")
+    public ResponseEntity<Map<String, Object>> handleSendResults(
+        @RequestParam String testName,
+        @RequestParam int recordCount,
+        @RequestParam String resultInfo) {
+        try {
+            // 2. 调用解析方法,根据 TestName 不同采用不同解析策略
+            PhysicalDeviceVo deviceVo = parseAndSaveResults(testName, recordCount, resultInfo);
+            boolean res = physicalTestService.saveDeviceData(deviceVo, DEFAULT_EVENT_ID);
+            // 3. 返回成功
+            if (res){
+                log.info("设备体质测试数据处理成功,共处理 {} 条数据", recordCount);
+                return ResponseEntity.ok(Map.of("success", 1, "msg", "数据上传成功"));
+            }else {
+                log.error("设备体质测试数据处理失败");
+                return ResponseEntity.ok(Map.of("success", 0, "msg", "数据处理失败"));
+            }
+        } catch (Exception e) {
+            log.error("设备体质测试数据上传异常", e);
+            return ResponseEntity.ok(Map.of("success", 0, "msg", e.getMessage()));
+        }
+    }
+
+    /**
+     * 根据项目名称处理不同的数据格式
+     */
+    private PhysicalDeviceVo parseAndSaveResults(String testName, int recordCount, String resultInfo) throws Exception {
+        // 按分号 ';' 分割每条记录
+        String[] allRecords = resultInfo.split(";");
+
+        PhysicalDeviceVo deviceVo = new PhysicalDeviceVo();
+        deviceVo.setTestName(testName);
+        deviceVo.setRecordCount(recordCount);
+
+        // 遍历每条记录
+        for (String singleRecord : allRecords) {
+            // 为每条记录创建新的Record对象
+            PhysicalDeviceVo.Record record = new PhysicalDeviceVo.Record();
+
+            // 按逗号 ',' 分割记录中的字段
+            String[] fields = singleRecord.split(",");
+
+            record.setStuNo(fields[0]);
+            record.setName(fields[1]);
+            // 3. 关键:根据不同的测试项目,分配不同的解析逻辑
+            int index = 2;
+            if (fields.length>5){
+                if (fields.length == 6 && testName.equals("身高体重") || testName.equals("左右眼视力")){
+                    record.setResultData(fields[2]+","+fields[3]);
+                    index = 3;
+                }else if (fields.length == 12 && testName.equals("左右眼视力")){
+                    record.setResultData(fields[2]+","+fields[3]+","+fields[4]+","+fields[5]+","+fields[6]+","+fields[7]+","+fields[8]+","+fields[9]);
+                    index = 9;
+                }
+            }else{
+                record.setResultData(fields[index]);
+            }
+            record.setReferee(fields[index+1]);
+            record.setTestTime(fields[index+2]);
+            deviceVo.getResultInfo().add(record);
+        }
+
+        return deviceVo;
+    }
+
+}

+ 97 - 4
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;
 
     /**
      * 查看获奖名单
@@ -37,12 +44,98 @@ public class ScoreController {
 
     /**
      * 查询成绩
+     * @param eventId 赛事ID
+     * @response {@link R<List<AppScoreVo>>}
      */
     @GetMapping("/list")
     public R<List<AppScoreVo>> listScore(
-        @RequestParam("eventId") Long eventId,
-        @RequestParam("projectId") Long projectId){
-        List<AppScoreVo> result = gameScoreService.listAppScore(eventId, projectId);
+        @RequestParam("eventId") Long eventId){
+        List<AppScoreVo> result = gameScoreService.listAppScore(eventId);
         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.setLeaderPoint(bo.getLeaderPoint());
+        gameScoreBo.setExtraPoint(bo.getExtraPoint());
+        gameScoreBo.setRemark(bo.getRemark());
+
+        // 设置默认值
+        gameScoreBo.setStatusFlag("0"); // 有效
+        gameScoreBo.setStatus("0");     // 正常
+
+        return gameScoreBo;
+    }
 }

+ 53 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/controller/app/UserEventController.java

@@ -0,0 +1,53 @@
+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.log.annotation.Log;
+import org.dromara.common.log.enums.BusinessType;
+import org.dromara.system.domain.vo.app.UserEventInfoVo;
+import org.dromara.system.domain.vo.app.UserLoginVo;
+import org.dromara.system.service.app.IUserEventService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * app-我的赛事
+ */
+@SaIgnore
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/app/user")
+public class UserEventController {
+    @Autowired
+    private IUserEventService userEventService;
+
+    @Log(title = "我的赛事-微信小程序登录", businessType = BusinessType.OTHER)
+    @PostMapping("/login")
+    public R<UserEventInfoVo> login(@RequestBody UserLoginVo loginVo) {
+        try {
+            // 验证必要参数
+            if (loginVo.getCode() == null || loginVo.getCode().trim().isEmpty()) {
+                return R.fail("微信登录凭证不能为空");
+            }
+
+            UserEventInfoVo result = userEventService.login(loginVo);
+            return R.ok(result);
+        } catch (Exception e) {
+            return R.fail("登录失败:" + e.getMessage());
+        }
+    }
+
+    @GetMapping("/eventInfo/{userId}")
+    public R<UserEventInfoVo> getUserEventInfo(@PathVariable Long userId) {
+        try {
+            UserEventInfoVo result = userEventService.getUserEventInfo(userId);
+            return R.ok(result);
+        } catch (Exception e) {
+            return R.fail("获取用户赛事信息失败:" + e.getMessage());
+        }
+    }
+}

+ 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;
+
+
+}

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

@@ -1,15 +1,15 @@
 package org.dromara.system.domain;
 
-import org.dromara.common.tenant.core.TenantEntity;
-import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import org.dromara.common.translation.annotation.Translation;
-import java.util.Date;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.common.tenant.core.TenantEntity;
 
 import java.io.Serial;
+import java.util.Date;
 
 /**
  * 赛事基本信息对象 game_event

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

@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode;
 
 import java.io.Serial;
 import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
 
 /**
  * 赛事分组对象 game_event_group
@@ -61,11 +62,13 @@ public class GameEventGroup extends TenantEntity {
     /**
      * 组别比赛开始时间
      */
+    @JsonFormat(pattern = "HH:mm")
     private Date beginTime;
 
     /**
      * 组别比赛结束时间
      */
+    @JsonFormat(pattern = "HH:mm")
     private Date endTime;
 
     /**

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

@@ -80,7 +80,7 @@ public class GameEventProject extends TenantEntity {
     private Long participateNum;
 
     /**
-     * 
+     * 录取名
      */
     private String roundType;
 
@@ -105,14 +105,14 @@ public class GameEventProject extends TenantEntity {
     private String award;
 
     /**
-     * 计时点名称
+     * 比赛轮次
      */
-    private String timePoint;
+    private String gameRound;
 
     /**
-     * 控制盒编号
+     * 比赛阶段
      */
-    private String boxCode;
+    private String gameStage;
 
     /**
      * 状态(0正常 1停用)

+ 4 - 4
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/GameScore.java

@@ -79,14 +79,14 @@ public class GameScore extends TenantEntity {
     private String award;
 
     /**
-     * 附加成绩1
+     * 领导加分
      */
-    private String extraScore1;
+    private Integer leaderPoint;
 
     /**
-     * 附加成绩2
+     * 额外加分
      */
-    private String extraScore2;
+    private Integer extraPoint;
 
     /**
      * 成绩状态(0等待处理1处理完毕)

+ 12 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/app/AppIndexVo.java

@@ -0,0 +1,12 @@
+package org.dromara.system.domain.app;
+
+import lombok.Data;
+import org.dromara.system.domain.vo.GameNavigatorVo;
+
+import java.util.List;
+
+@Data
+public class AppIndexVo {
+    private List<GameNavigatorVo> navigatorVos;
+    private String bgUrl;
+}

+ 86 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/app/GameUser.java

@@ -0,0 +1,86 @@
+package org.dromara.system.domain.app;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 赛事用户对象 game_user
+ *
+ * @author Lion Li
+ * @date 2025-08-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("game_user")
+public class GameUser extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    @TableId(value = "user_id")
+    private Long userId;
+
+    /**
+     * 用户名/账号
+     */
+    private String username;
+
+    /**
+     * 密码
+     */
+    private String password;
+
+    /**
+     * 邮箱
+     */
+    private String email;
+
+    /**
+     * 手机号
+     */
+    private String phone;
+
+    /**
+     * 头像
+     */
+    private String avatar;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 微信openid
+     */
+    private String openid;
+
+    /**
+     * 微信unionid
+     */
+    private String unionid;
+
+    /**
+     * 微信昵称
+     */
+    private String nickname;
+
+}

+ 27 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/app/QRCodeReferee.java

@@ -0,0 +1,27 @@
+package org.dromara.system.domain.app;
+
+import lombok.Data;
+import org.dromara.system.domain.vo.GameEventProjectVo;
+
+import java.util.List;
+
+/**
+ * 裁判二维码数据对象
+ */
+@Data
+public class QRCodeReferee {
+    // 赛事id
+    private Long eventId;
+    // 赛事名称
+    private String eventName;
+    // 裁判id
+    private Long refereeId;
+    // 裁判名称
+    private String name;
+    // 账号
+    private String account;
+    // 密码
+    private String password;
+    // 项目列表
+    private List<GameEventProjectVo> projectList;
+}

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

@@ -0,0 +1,15 @@
+package org.dromara.system.domain.app;
+
+import lombok.Data;
+
+/**
+ * 微信登录结果类
+ */
+@Data
+public class WxLoginResult {
+    private String openid;
+    private String session_key;
+    private String unionid;
+    private Integer errcode;
+    private String errmsg;
+}

+ 4 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/bo/AdviceBo.java

@@ -21,10 +21,13 @@ import jakarta.validation.constraints.*;
 public class AdviceBo {
 
     /**
-     *
+     * 意见id
      */
     private Long id;
 
+    /**
+     * 赛事id
+     */
     @NotNull(message = "赛事id不能为空", groups = { AddGroup.class, EditGroup.class })
     private Long eventId;
 

+ 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 Integer leaderPoint;
+
+    /**
+     * 附加成绩2
+     */
+    private Integer extraPoint;
+
+    /**
+     * 备注
+     */
+    private String remark;
+}

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

@@ -124,7 +124,7 @@ public class GameAthleteBo extends BaseEntity {
      */
 //    @NotBlank(message = "参与项目列表不能为空", groups = { AddGroup.class, EditGroup.class })
     private String projectValue;
-    private List<String> projectList;
+    private List<Long> projectList;
 
     /**
      * 状态(0正常 1停用)

+ 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;
+
+}

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

@@ -1,17 +1,16 @@
 package org.dromara.system.domain.bo;
 
-import org.dromara.system.domain.GameEvent;
-import org.dromara.common.mybatis.core.domain.BaseEntity;
-import org.dromara.common.core.validate.AddGroup;
-import org.dromara.common.core.validate.EditGroup;
 import io.github.linpeilie.annotations.AutoMapper;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import jakarta.validation.constraints.*;
-import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.system.domain.GameEvent;
+
 import java.util.Date;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import org.dromara.common.translation.constant.TransConstant;
 
 /**
  * 赛事基本信息业务对象 game_event
@@ -54,7 +53,7 @@ public class GameEventBo extends BaseEntity {
     /**
      * 用途
      */
-    @NotBlank(message = "用途不能为空", groups = { AddGroup.class, EditGroup.class })
+//    @NotBlank(message = "用途不能为空", groups = { AddGroup.class, EditGroup.class })
     private String purpose;
 
     /**
@@ -72,13 +71,13 @@ public class GameEventBo extends BaseEntity {
     /**
      * 赛事链接
      */
-    @NotBlank(message = "赛事链接不能为空", groups = { AddGroup.class, EditGroup.class })
+//    @NotBlank(message = "赛事链接不能为空", groups = { AddGroup.class, EditGroup.class })
     private String eventUrl;
 
     /**
      * 裁判码
      */
-    @NotBlank(message = "裁判码不能为空", groups = { AddGroup.class, EditGroup.class })
+//    @NotBlank(message = "裁判码不能为空", groups = { AddGroup.class, EditGroup.class })
     private String refereeUrl;
 
     /**

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

@@ -1,5 +1,6 @@
 package org.dromara.system.domain.bo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import org.dromara.system.domain.GameEventGroup;
 import org.dromara.common.mybatis.core.domain.BaseEntity;
 import org.dromara.common.core.validate.AddGroup;
@@ -74,12 +75,14 @@ public class GameEventGroupBo extends BaseEntity {
      * 组别比赛开始时间
      */
 //    @NotNull(message = "组别比赛开始时间不能为空", groups = { AddGroup.class, EditGroup.class })
+    @JsonFormat(pattern = "HH:mm")
     private Date beginTime;
 
     /**
      * 组别比赛结束时间
      */
 //    @NotNull(message = "组别比赛结束时间不能为空", groups = { AddGroup.class, EditGroup.class })
+    @JsonFormat(pattern = "HH:mm")
     private Date endTime;
 
     /**

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

@@ -23,7 +23,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
  */
 @Data
 @EqualsAndHashCode(callSuper = true)
-@AutoMapper(target = GameEventProject.class, reverseConvertGenerate = false)
+@AutoMapper(target = GameEventProject.class)
 public class GameEventProjectBo extends BaseEntity {
 
     /**
@@ -66,7 +66,7 @@ public class GameEventProjectBo extends BaseEntity {
     /**
      * 裁判组员集合
      */
-    private List<String> refereeGroups;
+    private List<Long> refereeGroups;
 
     /**
      * 比赛场地
@@ -102,7 +102,7 @@ public class GameEventProjectBo extends BaseEntity {
     private Long participateNum;
 
     /**
-     * 
+     * 录取名
      */
     private String roundType;
 
@@ -128,14 +128,14 @@ public class GameEventProjectBo extends BaseEntity {
     private String award;
 
     /**
-     * 计时点名称
+     * 比赛轮次
      */
-    private String timePoint;
+    private String gameRound;
 
     /**
-     * 控制盒编号
+     * 比赛阶段
      */
-    private String boxCode;
+    private String gameStage;
 
     /**
      * 状态(0正常 1停用)

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

@@ -59,7 +59,7 @@ public class GameRefereeBo extends BaseEntity {
      * 负责的赛事项目
      */
     private String projectList;
-    private List<String> projectList2;
+    private List<Long> projectList2;
 
     /**
      * 裁判码

+ 6 - 6
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;
 
     /**
@@ -84,14 +84,14 @@ public class GameScoreBo extends BaseEntity {
     private String award;
 
     /**
-     * 附加成绩1
+     * 领导加分
      */
-    private String extraScore1;
+    private Integer leaderPoint;
 
     /**
-     * 附加成绩2
+     * 额外加分
      */
-    private String extraScore2;
+    private Integer extraPoint;
 
     /**
      * 更新时间

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

@@ -61,7 +61,7 @@ public class GameTeamBo extends BaseEntity {
      */
 //    @NotBlank(message = "队员列表不能为空", groups = { AddGroup.class, EditGroup.class })
     private String athleteValue;
-    private List<String> athleteList;
+    private List<Long> athleteList;
 
     /**
      * 参与项目列表

+ 1 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/AdviceVo.java

@@ -27,7 +27,7 @@ public class AdviceVo implements Serializable {
     /**
      *
      */
-    @ExcelProperty(value = "")
+//    @ExcelProperty(value = "")
     private Long id;
     private Long eventId;
     /**

+ 35 - 3
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/AppScoreVo.java

@@ -1,5 +1,6 @@
 package org.dromara.system.domain.vo;
 
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import lombok.Data;
 
 import java.io.Serial;
@@ -8,6 +9,7 @@ import java.math.BigDecimal;
 
 /**
  * 小程序端查询成绩信息结果Vo
+ * @author zlt
  */
 @Data
 public class AppScoreVo implements Serializable {
@@ -15,19 +17,49 @@ public class AppScoreVo implements Serializable {
     @Serial
     private static final long serialVersionUID = 1L;
 
+    /**
+     * 项目id
+     */
     private Long projectId;
+    /**
+     * 项目名称
+     */
     private String projectName;
+    /**
+     * 项目归类
+     */
     private String classification;
-
+    /**
+     * 队伍id
+     */
     private Long teamId;
+    /**
+     * 队伍名称
+     */
     private String teamName;
-
+    /**
+     * 运动员id
+     */
     private Long athleteId;
+    /**
+     * 运动员名称
+     */
     private String athleteName;
-
+    /**
+     * 个人成绩
+     */
     private BigDecimal individualPerformance;
+    /**
+     * 团队成绩
+     */
     private BigDecimal teamPerformance;
+    /**
+     * 积分
+     */
     private Integer scorePoint;
+    /**
+     * 排名
+     */
     private Integer scoreRank;
 
 }

+ 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;
+
+}

+ 1 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameAthleteVo.java

@@ -131,7 +131,7 @@ public class GameAthleteVo implements Serializable {
     @ExcelProperty(value = "参与项目列表")
     private String projectValue;
 
-    private List<String> projectList;
+    private List<Long> projectList;
 
     /**
      * 状态(0正常 1停用)

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

@@ -1,5 +1,6 @@
 package org.dromara.system.domain.vo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import org.dromara.system.domain.GameEventGroup;
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;

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

@@ -3,6 +3,7 @@ package org.dromara.system.domain.vo;
 import java.util.Date;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import org.dromara.system.domain.GameEventProject;
+import org.dromara.system.domain.bo.GameEventProjectBo;
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;
 import org.dromara.common.excel.annotation.ExcelDictFormat;
@@ -19,6 +20,9 @@ import java.util.List;
 /**
  * 赛事项目视图对象 game_event_project
  *
+ * 该类用于表示赛事项目的视图数据,包含赛事项目的基本信息、时间信息、
+ * 参赛信息、评分规则等。该类仅映射到GameEventProject实体类。
+ *
  * @author zlt
  * @date 2025-07-30
  */
@@ -73,7 +77,7 @@ public class GameEventProjectVo implements Serializable {
     /**
      * 裁判组员
      */
-    private List<String> refereeGroups;
+    private List<Long> refereeGroups;
 
     /**
      * 比赛场地
@@ -112,9 +116,9 @@ public class GameEventProjectVo implements Serializable {
     private Long participateNum;
 
     /**
-     * 
+     * 录取名
      */
-    @ExcelProperty(value = "次")
+    @ExcelProperty(value = "录取名次")
     private String roundType;
 
     /**
@@ -143,16 +147,16 @@ public class GameEventProjectVo implements Serializable {
     private String award;
 
     /**
-     * 计时点名称
+     * 比赛轮次
      */
-    @ExcelProperty(value = "计时点名称")
-    private String timePoint;
+    @ExcelProperty(value = "比赛轮次")
+    private String gameRound;
 
     /**
-     * 控制盒编号
+     * 比赛阶段
      */
-    @ExcelProperty(value = "控制盒编号")
-    private String boxCode;
+    @ExcelProperty(value = "比赛阶段")
+    private String gameStage;
 
     /**
      * 状态(0正常 1停用)

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

@@ -1,16 +1,14 @@
 package org.dromara.system.domain.vo;
 
-import org.dromara.common.translation.annotation.Translation;
-import java.util.Date;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import org.dromara.common.translation.constant.TransConstant;
-import org.dromara.system.domain.GameEvent;
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;
-import org.dromara.common.excel.annotation.ExcelDictFormat;
-import org.dromara.common.excel.convert.ExcelDictConvert;
 import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+import org.dromara.system.domain.GameEvent;
 
 import java.io.Serial;
 import java.io.Serializable;
@@ -64,6 +62,7 @@ public class GameEventVo implements Serializable {
      * 用途
      */
     @ExcelProperty(value = "用途")
+    @ExcelDictFormat(dictType = "game_event_purpose")
     private String purpose;
 
     /**
@@ -81,7 +80,7 @@ public class GameEventVo implements Serializable {
     /**
      * 赛事链接
      */
-    @ExcelProperty(value = "赛事链接")
+//    @ExcelProperty(value = "赛事链接")
     private String eventUrl;
 
     /**
@@ -103,7 +102,7 @@ public class GameEventVo implements Serializable {
     /**
      * 签到码
      */
-    @ExcelProperty(value = "签到码")
+//    @ExcelProperty(value = "签到码")
     private String registerUrl;
 
     /**

+ 1 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameRefereeVo.java

@@ -67,7 +67,7 @@ public class GameRefereeVo implements Serializable {
      * 负责的赛事项目
      */
     private String projectList;
-    private List<String> projectList2;
+    private List<Long> projectList2;
 
     /**
      * 裁判码

+ 9 - 3
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameScoreSummaryExportVo.java

@@ -47,10 +47,16 @@ public class GameScoreSummaryExportVo implements Serializable {
     private Integer totalScore;
 
     /**
-     * 加分
+     * 领导加分
      */
-    @ExcelProperty(value = "加分", index = 4)
-    private Integer extraScore;
+    @ExcelProperty(value = "领导加分", index = 4)
+    private Integer leaderPoint;
+
+    /**
+     * 额外加分
+     */
+    @ExcelProperty(value = "额外加分", index = 5)
+    private Integer extraPoint;
 
     /**
      * 动态项目积分存储

+ 6 - 6
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/GameScoreVo.java

@@ -94,16 +94,16 @@ public class GameScoreVo implements Serializable {
     private String award;
 
     /**
-     * 附加成绩1
+     * 领导加分
      */
-    @ExcelProperty(value = "附加成绩1")
-    private String extraScore1;
+    @ExcelProperty(value = "领导加分")
+    private Integer leaderPoint;
 
     /**
-     * 附加成绩2
+     * 额外加分
      */
-    @ExcelProperty(value = "附加成绩2")
-    private String extraScore2;
+    @ExcelProperty(value = "额外加分")
+    private Integer extraPoint;
 
     /**
      * 更新时间

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

@@ -28,7 +28,7 @@ public class GameTeamVo implements Serializable {
     @Serial
     private static final long serialVersionUID = 1L;
 
-   @ExcelProperty(value = "主键")
+//   @ExcelProperty(value = "主键")
     private Long teamId;
 
     /**
@@ -38,9 +38,9 @@ public class GameTeamVo implements Serializable {
     private Long eventId;
 
     /**
-     * 赛事ID
+     * 赛事名称
      */
-//    @ExcelProperty(value = "赛事名称")
+    @ExcelProperty(value = "赛事名称")
     private String eventName;
 
     /**
@@ -66,7 +66,7 @@ public class GameTeamVo implements Serializable {
      */
 //    @ExcelProperty(value = "队员列表")
     private String athleteValue;
-    private List<String> athleteList;
+    private List<Long> athleteList;
 
     /**
      * 参与项目列表
@@ -83,7 +83,7 @@ public class GameTeamVo implements Serializable {
     /**
      * 号码段
      */
-//    @ExcelProperty(value = "号码段")
+    @ExcelProperty(value = "号码段")
     private String numberRange;
 
     /**

+ 48 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/DistanceScoreUploadVo.java

@@ -0,0 +1,48 @@
+package org.dromara.system.domain.vo.app;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 澳亚特设备成绩上传请求视图对象
+ *
+ * @author zlt
+ * @date 2025-09-01
+ */
+@Data
+public class DistanceScoreUploadVo {
+    /**
+     * 场次代码(分组ID)
+     */
+    private Long ccdm;
+
+    /**
+     * 组次
+     */
+    private Long groupIndex;
+
+    /**
+     * 成绩数据(格式:序号,后三轮序号,号码,姓名,成绩1,风速1,成绩2,风速2,成绩3,风速3,成绩4,风速4,成绩5,风速5,成绩6,风速6,名次|......)
+     */
+    private String cj;
+
+    @Data
+    public static class ScoreItem{
+        //序号列表
+        private List<String> indexList;
+
+        //运动员名称
+        private String athleteName;
+
+        //运动员编号
+        private String athleteCode;
+
+        //成绩列表
+        private List<BigDecimal> scoreList;
+
+        //名次
+        private Integer rank;
+    }
+}

+ 86 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/ElectrometerDataItemVo.java

@@ -0,0 +1,86 @@
+package org.dromara.system.domain.vo.app;
+
+import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 澳亚特设备数据项视图对象
+ *
+ * @author zlt
+ * @date 2025-01-20
+ */
+@Data
+public class ElectrometerDataItemVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 项目类型(计算规则), 对应 game_event_project.score_rule
+     */
+    private String tjsDm;
+
+    /**
+     * 项目属性, 1-径赛, 2-田赛, 对应 score_rule 的计算规则
+     */
+    private String sx;
+
+    /**
+     * 号码(编号), 对应 game_athlete.athlete_code
+     */
+    private String hm;
+
+    /**
+     * 姓名, 对应 game_athlete.name
+     */
+    private String xm;
+
+    /**
+     * 性别, 男女, 对应 game_athlete.gender
+     */
+    private String xb;
+
+    /**
+     * 单位, 对应 game_athlete.unit
+     */
+    private String dw;
+
+    /**
+     * 组别名称, 甲组, 对应分组名称
+     */
+    private String zbMc;
+
+    /**
+     * 项目名称, 对应 game_event_project.project_name
+     */
+    private String xmMc;
+
+    /**
+     * 赛次名称(预赛, 复赛, 半决赛, 决赛), 对应比赛轮次
+     */
+    private String scMc;
+
+    /**
+     * 场次代码, 编排给的比赛分组唯一标识, 对应 game_athlete_competition_group.group_id
+     */
+    private String ccDm;
+
+    /**
+     * 组次, 比赛项目分组号, 对应分组信息
+     */
+    private String zc;
+
+    /**
+     * 道次, 对应分组中的道次
+     */
+    private String dc;
+
+    /**
+     * 比赛时间, 对应比赛时间
+     */
+    private Date tm;
+
+    /**
+     * 比赛单元, 对应比赛单元
+     */
+    private String bsDm;
+}

+ 62 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/ElectrometerScoreUploadVo.java

@@ -0,0 +1,62 @@
+package org.dromara.system.domain.vo.app;
+
+import lombok.Data;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 澳亚特设备成绩上传请求视图对象
+ *
+ * @author zlt
+ * @date 2025-09-01
+ */
+@Data
+public class ElectrometerScoreUploadVo implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 场次代码(分组ID)
+     */
+    private Long ccdm;
+
+    /**
+     * 运动员编号
+     */
+    private String athleteCode;
+
+    /**
+     * 风速
+     */
+    private String windSpeed;
+
+    /**
+     * 反应时
+     */
+    private String reactionTime;
+
+    /**
+     * 组次
+     */
+    private Long groupIndex;
+
+    /**
+     * 道次
+     */
+    private Long trackIndex;
+
+    /**
+     * 成绩(以分钟为单位)
+     */
+    private BigDecimal score;
+
+    /**
+     * 名次
+     */
+    private Integer rank;
+
+    /**
+     * 状态
+     */
+    private String status;
+}

+ 96 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/GameUserVo.java

@@ -0,0 +1,96 @@
+package org.dromara.system.domain.vo.app;
+import org.dromara.system.domain.app.GameUser;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import org.dromara.common.excel.annotation.ExcelDictFormat;
+import org.dromara.common.excel.convert.ExcelDictConvert;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+
+
+/**
+ * 赛事用户视图对象 game_user
+ *
+ * @author Lion Li
+ * @date 2025-08-27
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = GameUser.class)
+public class GameUserVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 用户ID
+     */
+    @ExcelProperty(value = "用户ID")
+    private Long userId;
+
+    /**
+     * 用户名/账号
+     */
+    @ExcelProperty(value = "用户名/账号")
+    private String username;
+
+    /**
+     * 密码
+     */
+    @ExcelProperty(value = "密码")
+    private String password;
+
+    /**
+     * 邮箱
+     */
+    @ExcelProperty(value = "邮箱")
+    private String email;
+
+    /**
+     * 手机号
+     */
+    @ExcelProperty(value = "手机号")
+    private String phone;
+
+    /**
+     * 头像
+     */
+    @ExcelProperty(value = "头像")
+    private String avatar;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态", converter = ExcelDictConvert.class)
+    @ExcelDictFormat(readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+    /**
+     * 微信openid
+     */
+    @ExcelProperty(value = "微信openid")
+    private String openid;
+
+    /**
+     * 微信unionid
+     */
+    @ExcelProperty(value = "微信unionid")
+    private String unionid;
+
+    /**
+     * 微信昵称
+     */
+    @ExcelProperty(value = "微信昵称")
+    private String nickname;
+
+}

+ 29 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/PhysicalDeviceVo.java

@@ -0,0 +1,29 @@
+package org.dromara.system.domain.vo.app;
+
+import lombok.Data;
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class PhysicalDeviceVo{
+    //测试项目名称
+    private String testName;
+    //成绩记录条数
+    private int recordCount;
+    //结果记录数据
+    private List<Record> resultInfo = new ArrayList<>();
+
+    @Data
+    public static class Record{
+        //学号-映射运动员身份证号
+        private String stuNo;
+        //学生姓名--运动员姓名
+        private String name;
+        //测试结果
+        private String resultData;
+        //测试老师--裁判员
+        private String referee;
+        //测试时间
+        private String testTime;
+    }
+}

+ 24 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/PhysicalTestVo.java

@@ -0,0 +1,24 @@
+package org.dromara.system.domain.vo.app;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class PhysicalTestVo {
+    //项目名称
+    private String testName;
+    //项目key
+    private String testKey;
+    //对应学生身份证号
+    private String userId;
+    //学生姓名
+    private String name;
+    //测试时间
+    private String testTime;
+    //测试结果
+    private String resultData;
+    //测试分数
+    private BigDecimal resultScore;
+}

+ 34 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/UserEventInfoVo.java

@@ -0,0 +1,34 @@
+package org.dromara.system.domain.vo.app;
+
+import lombok.Data;
+import org.dromara.system.domain.vo.GameAthleteVo;
+import org.dromara.system.domain.vo.GameEventVo;
+
+import java.io.Serial;
+import java.io.Serializable;
+import org.dromara.system.domain.vo.GameEventProjectVo;
+import java.util.List;
+
+/**
+ * 用户赛事信息返回VO
+ *
+ * @author zlt
+ */
+@Data
+public class UserEventInfoVo implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    // 用户基本信息
+    private Long userId;
+    private String username;
+
+    // 运动员信息
+    private GameAthleteVo athleteInfo;
+
+    // 赛事信息
+    private GameEventVo eventInfo;
+
+    // 项目列表(包含成绩信息)
+    private List<GameEventProjectVo> projectList;
+}

+ 52 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/domain/vo/app/UserLoginVo.java

@@ -0,0 +1,52 @@
+package org.dromara.system.domain.vo.app;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+/**
+ * 微信小程序用户登录VO
+ *
+ * @author zlt
+ */
+@Data
+public class UserLoginVo implements Serializable {
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 微信小程序登录凭证
+     */
+    private String code;
+
+    /**
+     * 用户昵称
+     */
+    private String nickName;
+
+    /**
+     * 用户头像
+     */
+    private String avatarUrl;
+
+    /**
+     * 性别 0-未知 1-男 2-女
+     */
+    private Integer gender;
+
+    /**
+     * 国家
+     */
+    private String country;
+
+    /**
+     * 省份
+     */
+    private String province;
+
+    /**
+     * 城市
+     */
+    private String city;
+}

+ 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> {
+
+} 

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

@@ -1,9 +1,13 @@
 package org.dromara.system.mapper;
 
+import org.apache.ibatis.annotations.Select;
 import org.dromara.system.domain.GameAthlete;
 import org.dromara.system.domain.vo.GameAthleteVo;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 参赛队员Mapper接口
  *
@@ -12,4 +16,9 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
  */
 public interface GameAthleteMapper extends BaseMapperPlus<GameAthlete, GameAthleteVo> {
 
+    @Select("select * from game_athlete where user_id = #{userId} AND del_flag = '0'")
+    GameAthlete selectByUserId(Long userId);
+
+    @Select("select athlete_id,name,team_id from game_athlete where event_id = #{eventId} and del_flag = '0'")
+    List<GameAthleteVo> queryAthleteIdAndName(Long eventId);
 }

+ 3 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/GameRefereeMapper.java

@@ -3,6 +3,7 @@ package org.dromara.system.mapper;
 import org.dromara.system.domain.GameReferee;
 import org.dromara.system.domain.vo.GameRefereeVo;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.apache.ibatis.annotations.Mapper;
 
 /**
  * 裁判Mapper接口
@@ -10,6 +11,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
  * @author zlt
  * @date 2025-07-30
  */
+@Mapper
 public interface GameRefereeMapper extends BaseMapperPlus<GameReferee, GameRefereeVo> {
 
-}
+}

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

@@ -1,5 +1,6 @@
 package org.dromara.system.mapper;
 
+import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.dromara.system.domain.GameScore;
 import org.dromara.system.domain.vo.GameScoreVo;
@@ -33,4 +34,15 @@ public interface GameScoreMapper extends BaseMapperPlus<GameScore, GameScoreVo>
     GameScoreVo selectVoByAthleteIdAndProjectId(Long athleteId, Long projectId);
 
 
+    /**
+     * 获取运动员项目成绩
+     * @param athleteId
+     * @return
+     */
+    @Select("select project_id from game_score where athlete_id = #{athleteId} AND del_flag = '0'")
+    List<Long> selectProjectIdsByAthleteId(Long athleteId);
+
+    // 查询运动员在指定项目中的成绩
+    List<GameScore> selectByAthleteAndProjects(@Param("athleteId") Long athleteId,
+                                               @Param("projectIds") List<Long> projectIds);
 }

+ 14 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/app/ElectrometerMapper.java

@@ -0,0 +1,14 @@
+package org.dromara.system.mapper.app;
+
+import org.dromara.system.domain.vo.app.ElectrometerDataItemVo;
+import org.apache.ibatis.annotations.Param;
+import java.util.List;
+
+/**
+ * 澳亚特设备数据Mapper接口
+ *
+ * @author zlt
+ * @date 2025-01-20
+ */
+public interface ElectrometerMapper {
+}

+ 41 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/mapper/app/GameUserMapper.java

@@ -0,0 +1,41 @@
+package org.dromara.system.mapper.app;
+
+import org.apache.ibatis.annotations.Select;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.dromara.system.domain.app.GameUser;
+import org.dromara.system.domain.vo.app.GameUserVo;
+
+/**
+ * 赛事用户Mapper接口
+ *
+ * @author zlt
+ */
+public interface GameUserMapper extends BaseMapperPlus<GameUser, GameUserVo> {
+
+    /**
+     * 根据用户名查询用户
+     *
+     * @param username 用户名
+     * @return 用户
+     */
+    @Select("select * from game_user where username = #{username}")
+    GameUser selectUserByUserName(String username);
+
+    /**
+     * 根据用户ID查询用户
+     *
+     * @param userId 用户ID
+     * @return 用户
+     */
+    @Select("select * from game_user where user_id = #{userId}")
+    GameUser selectUserById(Long userId);
+
+    /**
+     * 根据微信openid查询用户
+     *
+     * @param openid 微信openid
+     * @return 用户
+     */
+    @Select("select * from game_user where openid = #{openid} and del_flag = '0'")
+    GameUser selectByOpenid(String openid);
+}

+ 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);
+}

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

@@ -65,4 +65,6 @@ public interface IGameEventConfigService {
      * @return 是否删除成功
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    Long queryBgUrlByEventId(Long eventId);
 }

+ 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);
 }

+ 19 - 2
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/IGameEventProjectService.java

@@ -29,7 +29,7 @@ public interface IGameEventProjectService {
     GameEventProjectVo queryById(Long projectId);
 
     /**
-     * 分页查询赛事项目列表
+     * 分页查询默认赛事项目列表
      *
      * @param bo        查询条件
      * @param pageQuery 分页参数
@@ -37,6 +37,15 @@ public interface IGameEventProjectService {
      */
     TableDataInfo<GameEventProjectVo> queryPageList(GameEventProjectBo bo, PageQuery pageQuery);
 
+    /**
+     * 分页查询所有赛事项目列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 赛事项目分页列表
+     */
+    TableDataInfo<GameEventProjectVo> queryAllProjectList(GameEventProjectBo bo, PageQuery pageQuery);
+
     /**
      * 查询符合条件的赛事项目列表
      *
@@ -53,6 +62,14 @@ public interface IGameEventProjectService {
      */
     Boolean insertByBo(GameEventProjectBo bo);
 
+    /**
+     * 批量新增赛事项目
+     *
+     * @param projectList 赛事项目
+     * @return 是否新增成功
+     */
+    Boolean BatchInsertByProjectList(Collection<GameEventProject> projectList);
+
     /**
      * 修改赛事项目
      *
@@ -70,7 +87,7 @@ public interface IGameEventProjectService {
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
 
-    List<GameEventProjectVo> listProjectsByEventIdAndProjectIndex(Long eventId, List<String> projectIds);
+    List<GameEventProjectVo> listProjectsByEventIdAndProjectIndex(Long eventId, List<Long> projectIds);
 
     Long countEventProject();
 

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

@@ -2,8 +2,8 @@ package org.dromara.system.service;
 
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.system.domain.app.AppIndexVo;
 import org.dromara.system.domain.bo.GameNavigatorBo;
-import org.dromara.system.domain.vo.EventMenuVo;
 import org.dromara.system.domain.vo.GameNavigatorVo;
 
 import java.util.Collection;
@@ -88,5 +88,5 @@ public interface IGameNavigatorService {
      * @param eventId
      * @return
      */
-    List<GameNavigatorVo> listAppNavByEventId(Long eventId);
+    AppIndexVo listAppNavByEventId(Long eventId);
 }

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

@@ -121,8 +121,22 @@ public interface IGameScoreService {
     /**
      * 用户端查询项目成绩信息
      * @param eventId
-     * @param projectId
      * @return
      */
-    List<AppScoreVo> listAppScore(Long eventId, Long projectId);
+    List<AppScoreVo> listAppScore(Long eventId);
+
+    /**
+     * 获取加分数据
+     */
+    Map<String, Object> getBonusData(Long eventId);
+
+    /**
+     * 更新加分数据
+     */
+    Boolean updateBonusData(Map<String, Object> data);
+
+    /**
+     * 导出加分Excel
+     */
+    void exportBonusExcel(Map<String, Object> data, HttpServletResponse response);
 }

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

@@ -98,6 +98,14 @@ public interface IGameTeamService {
      */
     Map<Long, String> getTeamIdAndNameMap(String eventId);
 
+
+    /**
+     * 获取队伍id和名称的映射关系
+     * @param teamIds
+     * @return
+     */
+    Map<Long, String> getTeamIdAndNameMap(Collection<Long> teamIds);
+
     /**
      * 更新队伍中的运动员列表
      *

+ 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("积分不能为负数");
+            }
+        }
+    }
+}

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

@@ -0,0 +1,43 @@
+package org.dromara.system.service.app;
+
+import org.dromara.system.domain.vo.app.DistanceScoreUploadVo;
+import org.dromara.system.domain.vo.app.ElectrometerDataItemVo;
+import org.dromara.system.domain.vo.app.ElectrometerScoreUploadVo;
+
+import java.util.List;
+
+/**
+ * 澳亚特设备服务接口
+ *
+ * @author zlt
+ * @date 2025-01-20
+ */
+public interface IElectrometerService {
+
+    /**
+     * 获取比赛道路信息
+     *
+     * @param eventId 赛事ID
+     * @param tjsdm 项目类型(1-径赛, 2-田赛)
+     * @return 比赛道路信息列表
+     */
+    List<ElectrometerDataItemVo> getMatchRoadInfo(Long eventId, String tjsdm);
+
+    /**
+     * 上传计时类项目成绩
+     *
+     * @param eventId 赛事ID
+     * @param scoreUpload 成绩上传数据
+     * @return 上传结果
+     */
+    String uploadScore(Long eventId, List<ElectrometerScoreUploadVo> scoreUpload);
+
+    /**
+     * 上传远度距离类项目成绩
+     *
+     * @param eventId 赛事ID
+     * @param scoreUpload 成绩上传数据
+     * @return 上传结果
+     */
+    String uploadDistanceScore(Long eventId, DistanceScoreUploadVo scoreUpload);
+}

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

@@ -0,0 +1,33 @@
+package org.dromara.system.service.app;
+
+import org.dromara.system.domain.vo.app.PhysicalDeviceVo;
+import org.dromara.system.domain.vo.app.PhysicalTestVo;
+
+import java.util.List;
+
+/**
+ * 体质测试Service接口
+ *
+ * @author zlt
+ * @date 2025-01-27
+ */
+public interface IPhysicalTestService {
+
+    /**
+     * 处理体质测试数据上传
+     *
+     * @param results 体质测试数据列表
+     * @param eventId 赛事ID
+     * @return 是否处理成功
+     */
+    Boolean processPhysicalTestData(List<PhysicalTestVo> results, Long eventId);
+
+    /**
+     * 保存设备体质测试数据
+     *
+     * @param deviceVo 设备体质测试数据
+     * @param eventId  赛事ID
+     * @return 是否保存成功
+     */
+    boolean saveDeviceData(PhysicalDeviceVo deviceVo, Long eventId);
+}

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

@@ -0,0 +1,12 @@
+package org.dromara.system.service.app;
+
+import org.dromara.system.domain.vo.app.UserEventInfoVo;
+import org.dromara.system.domain.vo.app.UserLoginVo;
+
+public interface IUserEventService {
+    // 用户登录
+    UserEventInfoVo login(UserLoginVo loginVo);
+
+    // 获取用户赛事信息
+    UserEventInfoVo getUserEventInfo(Long userId);
+}

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

@@ -3,7 +3,7 @@ package org.dromara.system.service.dbbackup;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.jdbc.ScriptRunner;
-import org.dromara.system.util.FilteredCommentInputStream;
+import org.dromara.system.utils.FilteredCommentInputStream;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;

+ 11 - 12
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/AdviceServiceImpl.java

@@ -22,9 +22,7 @@ import org.dromara.system.service.IAdviceService;
 import org.dromara.system.service.IGameTeamService;
 import org.springframework.stereotype.Service;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -66,16 +64,16 @@ public class AdviceServiceImpl implements IAdviceService {
         bo.setEventId(defaultEventId);
         LambdaQueryWrapper<Advice> lqw = buildQueryWrapper(bo);
         Page<AdviceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
-        result.getRecords()
+        //提前查询出队伍id和name的映射并缓存
+        Set<Long> ids = result.getRecords()
             .stream()
-            .map(vo -> {
-                GameTeamVo gameTeamVo = gameTeamService.queryById(vo.getTeamId());
-                if(gameTeamVo!=null) {
-                    vo.setTeamName(gameTeamVo.getTeamName());
-                }
-                return vo;
-            })
-            .collect(Collectors.toList());
+            .map(AdviceVo::getTeamId)
+            .collect(Collectors.toSet());
+        Map<Long, String> map = gameTeamService.getTeamIdAndNameMap(ids);
+        //赋值
+        result.getRecords().forEach(vo -> {
+            vo.setTeamName(map.getOrDefault(vo.getTeamId(), ""));
+        });
         return TableDataInfo.build(result);
     }
 
@@ -94,6 +92,7 @@ public class AdviceServiceImpl implements IAdviceService {
     private LambdaQueryWrapper<Advice> buildQueryWrapper(AdviceBo bo) {
         LambdaQueryWrapper<Advice> lqw = Wrappers.lambdaQuery();
         lqw.orderByAsc(Advice::getId);
+        lqw.eq(bo.getEventId() != null, Advice::getEventId, bo.getEventId());
         lqw.like(StringUtils.isNotBlank(bo.getName()), Advice::getName, bo.getName());
         if (StrUtil.isNotEmpty(bo.getTeamName())) {
             GameTeamBo gameTeamBo = new GameTeamBo();

+ 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;
+    }
+}

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

@@ -149,7 +149,7 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
         Optional.ofNullable(vo.getProjectValue())
             .filter(StringUtils::isNotBlank)
             .ifPresent(projectValue -> {
-                List<String> projects = JSONUtil.toList(projectValue, String.class);
+                List<Long> projects = JSONUtil.toList(projectValue, Long.class);
                 vo.setProjectList(projects);
             });
         return vo;
@@ -180,7 +180,7 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
                     .filter(StringUtils::isNotBlank)
                     .ifPresent(projectValue -> {
 //                        String[] projectIds = projectValue.split(",");
-                        List<String> projectIds = JSONUtil.toList(projectValue, String.class);
+                        List<Long> projectIds = JSONUtil.toList(projectValue, Long.class);
                         if (CollUtil.isNotEmpty(projectIds)) {
                             List<GameEventProjectVo> projects =
                                 gameEventProjectService.listProjectsByEventIdAndProjectIndex(
@@ -226,7 +226,7 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
             Optional.ofNullable(vo.getProjectValue())
                 .filter(StringUtils::isNotBlank)
                 .ifPresent(projectValue -> {
-                    List<String> projects = JSONUtil.toList(projectValue, String.class);
+                    List<Long> projects = JSONUtil.toList(projectValue, Long.class);
                     vo.setProjectList(projects);
                 });
         });
@@ -416,7 +416,7 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
         );
         list.forEach(vo -> {
             if (vo.getProjectValue() != null) {
-                vo.setProjectList(JSONUtil.toList(vo.getProjectValue(), String.class));
+                vo.setProjectList(JSONUtil.toList(vo.getProjectValue(), Long.class));
             }
         });
         return list;
@@ -444,7 +444,7 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
         allAthletes.forEach(athlete -> {
             if (athlete.getProjectValue() != null) {
                 try {
-                    athlete.setProjectList(JSONUtil.toList(athlete.getProjectValue(), String.class));
+                    athlete.setProjectList(JSONUtil.toList(athlete.getProjectValue(), Long.class));
                     log.debug("运动员 {} 的项目列表转换成功: {}", athlete.getName(), athlete.getProjectList());
                 } catch (Exception e) {
                     log.warn("转换运动员 {} 的项目列表失败: {}", athlete.getName(), e);
@@ -460,7 +460,7 @@ public class GameAthleteServiceImpl implements IGameAthleteService {
         List<GameAthleteVo> filteredAthletes = allAthletes.stream()
             .filter(athlete -> {
                 if (CollectionUtils.isNotEmpty(athlete.getProjectList())) {
-                    boolean containsProject = athlete.getProjectList().contains(projectId.toString());
+                    boolean containsProject = athlete.getProjectList().contains(projectId);
                     log.debug("运动员 {} 的项目列表: {}, 包含项目 {}: {}",
                             athlete.getName(), athlete.getProjectList(), projectId, containsProject);
                     return containsProject;

+ 13 - 3
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventConfigServiceImpl.java

@@ -42,7 +42,7 @@ public class GameEventConfigServiceImpl implements IGameEventConfigService {
      * @return 赛事配置
      */
     @Override
-    public GameEventConfigVo queryById(Long configId){
+    public GameEventConfigVo queryById(Long configId) {
         return baseMapper.selectVoById(configId);
     }
 
@@ -149,7 +149,7 @@ public class GameEventConfigServiceImpl implements IGameEventConfigService {
     /**
      * 保存前的数据校验
      */
-    private void validEntityBeforeSave(GameEventConfig entity){
+    private void validEntityBeforeSave(GameEventConfig entity) {
         //TODO 做一些数据校验,如唯一约束
     }
 
@@ -162,9 +162,19 @@ public class GameEventConfigServiceImpl implements IGameEventConfigService {
      */
     @Override
     public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
+        if (isValid) {
             //TODO 做一些业务上的校验,判断是否需要校验
         }
         return baseMapper.deleteByIds(ids) > 0;
     }
+
+    @Override
+    public Long queryBgUrlByEventId(Long eventId) {
+        String ossId = baseMapper.selectOne(
+            Wrappers.lambdaQuery(GameEventConfig.class)
+                .eq(GameEventConfig::getEventId, eventId)
+                .eq(GameEventConfig::getConfigKey, "background_img")
+        ).getConfigValue();
+        return Long.valueOf(ossId);
+    }
 }

+ 234 - 9
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;
+    }
 
     /**
      * 查询赛事分组
@@ -77,7 +121,7 @@ public class GameEventGroupServiceImpl implements IGameEventGroupService {
                 Optional.ofNullable(vo.getProjectList())
                     .filter(StringUtils::isNotBlank)
                     .ifPresent(projectValue -> {
-                        List<String> projectIds = JSONUtil.toList(projectValue, String.class);
+                        List<Long> projectIds = JSONUtil.toList(projectValue, Long.class);
                         List<GameEventProjectVo> projects =
                             gameEventProjectService.listProjectsByEventIdAndProjectIndex(
                                 vo.getEventId(),
@@ -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;
+    }
 }

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

@@ -29,9 +29,11 @@ import org.dromara.system.domain.vo.GameEventProjectVo;
 import org.dromara.system.domain.GameEventProject;
 import org.dromara.system.mapper.GameEventProjectMapper;
 import org.dromara.system.service.IGameEventProjectService;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * 赛事项目Service业务层处理
@@ -57,7 +59,7 @@ public class GameEventProjectServiceImpl implements IGameEventProjectService {
     @Override
     public GameEventProjectVo queryById(Long projectId) {
         GameEventProjectVo vo = baseMapper.selectVoById(projectId);
-        vo.setRefereeGroups(JSONUtil.toList(vo.getRefereeGroup(), String.class));
+        vo.setRefereeGroups(JSONUtil.toList(vo.getRefereeGroup(), Long.class));
         return vo;
     }
 
@@ -95,8 +97,8 @@ public class GameEventProjectServiceImpl implements IGameEventProjectService {
 
                 Optional.ofNullable(vo.getRefereeGroup())
                     .filter(ObjectUtil::isNotEmpty)
-                    .ifPresent(refereeIdStr -> {
-                        List<String> refereeList = JSONUtil.toList(refereeIdStr.toString(), String.class);
+                    .ifPresent(refereeGroup -> {
+                        List<Long> refereeList = JSONUtil.toList(refereeGroup, Long.class);
                         vo.setRefereeGroups(refereeList);
                     });
             });
@@ -104,6 +106,48 @@ public class GameEventProjectServiceImpl implements IGameEventProjectService {
         return TableDataInfo.build(result);
     }
 
+    @Override
+    public TableDataInfo<GameEventProjectVo> queryAllProjectList(GameEventProjectBo bo, PageQuery pageQuery) {
+        // 获取默认赛事ID
+        Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
+        final Long defaultEventID;
+        if (cacheObject instanceof Integer) {
+            defaultEventID = ((Integer) cacheObject).longValue();
+        } else if (cacheObject instanceof Long) {
+            defaultEventID = (Long) cacheObject;
+        } else {
+            defaultEventID = null;
+        }
+
+        // 构建查询条件,排除默认赛事ID
+        LambdaQueryWrapper<GameEventProject> lqw = buildQueryWrapperForProjectLibrary(bo, defaultEventID);
+        Page<GameEventProjectVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+
+        // 处理记录
+        result.getRecords().forEach(record -> {
+            Optional.ofNullable(record.getEventId())
+                .filter(ObjectUtil::isNotEmpty)
+                .ifPresent(eventId -> {
+                    GameEvent gameEvent = gameEventMapper.selectById(eventId);
+                    if (gameEvent != null) {
+                        record.setEventName(gameEvent.getEventName());
+                    }
+                });
+            //不获取关联性数据
+            record.setRefereeGroup("[]");
+            //移除原始的projectId,确保添加为新项目
+            record.setProjectId(null);
+        });
+
+        // 按更新时间降序排序(最新的在前)
+        List<GameEventProjectVo> sortedRecords = result.getRecords().stream()
+            .sorted(Comparator.comparing(GameEventProjectVo::getUpdateTime, Comparator.nullsLast(Comparator.reverseOrder())))
+            .collect(Collectors.toList());
+        result.setRecords(sortedRecords);
+
+        return TableDataInfo.build(result);
+    }
+
     /**
      * 查询符合条件的赛事项目列表
      *
@@ -135,7 +179,7 @@ public class GameEventProjectServiceImpl implements IGameEventProjectService {
             Optional.ofNullable(vo.getRefereeGroup())
                 .filter(ObjectUtil::isNotEmpty)
                 .ifPresent(refereeIdStr -> {
-                    List<String> refereeList = JSONUtil.toList(refereeIdStr.toString(), String.class);
+                    List<Long> refereeList = JSONUtil.toList(refereeIdStr, Long.class);
                     vo.setRefereeGroups(refereeList);
                 });
         });
@@ -173,6 +217,48 @@ public class GameEventProjectServiceImpl implements IGameEventProjectService {
         return lqw;
     }
 
+    /**
+     * 构建项目库查询条件,排除默认赛事ID
+     */
+    private LambdaQueryWrapper<GameEventProject> buildQueryWrapperForProjectLibrary(GameEventProjectBo bo, Long defaultEventID) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<GameEventProject> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(GameEventProject::getProjectId);
+
+        // 排除默认赛事ID
+        if (defaultEventID != null) {
+            lqw.ne(GameEventProject::getEventId, defaultEventID);
+        }
+
+        // 如果指定了eventId,则按指定条件查询
+        lqw.eq(bo.getEventId() != null, GameEventProject::getEventId, bo.getEventId());
+
+        // 通过名称模糊查询
+        Optional.ofNullable(bo.getEventName())
+            .ifPresent(eventName -> {
+                List<GameEvent> gameEvents = gameEventMapper.selectList(
+                    Wrappers.lambdaQuery(GameEvent.class)
+                        .like(GameEvent::getEventName, bo.getEventName())
+                        .select(GameEvent::getEventId)
+                );
+                if (CollectionUtils.isNotEmpty(gameEvents)) {
+                    List<Long> ids = gameEvents.stream()
+                        .map(GameEvent::getEventId)
+                        .collect(Collectors.toList());
+                    lqw.in(GameEventProject::getEventId, ids);
+                } else {
+                    lqw.apply("1=0");
+                }
+            });
+        lqw.like(StringUtils.isNotBlank(bo.getProjectName()), GameEventProject::getProjectName, bo.getProjectName());
+        lqw.eq(StringUtils.isNotBlank(bo.getProjectType()), GameEventProject::getProjectType, bo.getProjectType());
+        lqw.eq(StringUtils.isNotBlank(bo.getClassification()), GameEventProject::getClassification, bo.getClassification());
+        lqw.eq(bo.getStartTime() != null, GameEventProject::getStartTime, bo.getStartTime());
+        lqw.eq(StringUtils.isNotBlank(bo.getOrderType()), GameEventProject::getOrderType, bo.getOrderType());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), GameEventProject::getStatus, bo.getStatus());
+        return lqw;
+    }
+
     /**
      * 新增赛事项目
      *
@@ -201,6 +287,25 @@ public class GameEventProjectServiceImpl implements IGameEventProjectService {
         return flag;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean BatchInsertByProjectList(Collection<GameEventProject> projectList) {
+        Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
+        final Long defaultEventID;
+        if (cacheObject instanceof Integer) {
+            defaultEventID = ((Integer) cacheObject).longValue();
+        } else if (cacheObject instanceof Long) {
+            defaultEventID = (Long) cacheObject;
+        } else {
+            defaultEventID = null;
+        }
+        projectList.forEach(projectBo -> projectBo.setEventId(defaultEventID));
+        if (CollectionUtils.isNotEmpty(projectList)){
+            return baseMapper.insertBatch(projectList);
+        }
+        return false;
+    }
+
     /**
      * 修改赛事项目
      *
@@ -248,7 +353,7 @@ public class GameEventProjectServiceImpl implements IGameEventProjectService {
     }
 
     @Override
-    public List<GameEventProjectVo> listProjectsByEventIdAndProjectIndex(Long eventId, List<String> projectIds) {
+    public List<GameEventProjectVo> listProjectsByEventIdAndProjectIndex(Long eventId, List<Long> projectIds) {
         if (CollectionUtils.isEmpty(projectIds)) {
             return Collections.emptyList();
         }

+ 11 - 32
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameEventServiceImpl.java

@@ -1,5 +1,6 @@
 package org.dromara.system.service.impl;
 
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.img.FontUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -19,6 +20,7 @@ import com.itextpdf.text.pdf.PdfWriter;
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.NotNull;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.output.ByteArrayOutputStream;
@@ -33,17 +35,13 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.redis.utils.RedisUtils;
 import org.dromara.system.domain.GameEvent;
-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.GenerateBibBo;
+import org.dromara.system.domain.GameEventGroup;
+import org.dromara.system.domain.PdfEntry;
+import org.dromara.system.domain.bo.*;
 import org.dromara.system.domain.constant.GameEventConstant;
 import org.dromara.system.domain.vo.*;
 import org.dromara.system.mapper.GameEventMapper;
-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.dromara.system.service.*;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -54,8 +52,13 @@ import javax.imageio.ImageIO;
 import java.awt.image.BufferedImage;
 import java.io.*;
 import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 /**
  * 赛事基本信息Service业务层处理
@@ -122,14 +125,6 @@ public class GameEventServiceImpl implements IGameEventService {
      */
     @Override
     public List<GameEventVo> queryList(GameEventBo bo) {
-        if (bo.getEventId() == null) {
-            Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
-            if (cacheObject instanceof Integer) {
-                bo.setEventId(((Integer) cacheObject).longValue());  // 显式转换为 Long 类型
-            } else if (cacheObject instanceof Long) {
-                bo.setEventId((Long) cacheObject);
-            }
-        }
         LambdaQueryWrapper<GameEvent> lqw = buildQueryWrapper(bo);
         return baseMapper.selectVoList(lqw);
     }
@@ -156,14 +151,6 @@ public class GameEventServiceImpl implements IGameEventService {
      */
     @Override
     public Long insertByBo(GameEventBo bo) {
-        if (bo.getEventId() == null) {
-            Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
-            if (cacheObject instanceof Integer) {
-                bo.setEventId(((Integer) cacheObject).longValue());  // 显式转换为 Long 类型
-            } else if (cacheObject instanceof Long) {
-                bo.setEventId((Long) cacheObject);
-            }
-        }
         GameEvent add = MapstructUtils.convert(bo, GameEvent.class);
         validEntityBeforeSave(add);
         boolean flag = baseMapper.insert(add) > 0;
@@ -181,14 +168,6 @@ public class GameEventServiceImpl implements IGameEventService {
      */
     @Override
     public Boolean updateByBo(GameEventBo bo) {
-        if (bo.getEventId() == null) {
-            Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
-            if (cacheObject instanceof Integer) {
-                bo.setEventId(((Integer) cacheObject).longValue());  // 显式转换为 Long 类型
-            } else if (cacheObject instanceof Long) {
-                bo.setEventId((Long) cacheObject);
-            }
-        }
         GameEvent update = MapstructUtils.convert(bo, GameEvent.class);
         validEntityBeforeSave(update);
         boolean result = baseMapper.updateById(update) > 0;

+ 160 - 12
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/GameRefereeServiceImpl.java

@@ -11,6 +11,7 @@ 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.GameEventProject;
 import org.dromara.system.domain.constant.GameEventConstant;
 import org.jetbrains.annotations.NotNull;
 import org.springframework.stereotype.Service;
@@ -19,10 +20,17 @@ import org.dromara.system.domain.vo.GameRefereeVo;
 import org.dromara.system.domain.GameReferee;
 import org.dromara.system.mapper.GameRefereeMapper;
 import org.dromara.system.service.IGameRefereeService;
+import org.dromara.system.service.IGameEventProjectService;
+import org.dromara.system.domain.vo.GameEventProjectVo;
+import org.dromara.system.domain.bo.GameEventProjectBo;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 
 import java.util.List;
 import java.util.Map;
 import java.util.Collection;
+import java.util.ArrayList;
+import java.util.stream.Collectors;
 
 /**
  * 裁判Service业务层处理
@@ -35,7 +43,8 @@ import java.util.Collection;
 @Service
 public class GameRefereeServiceImpl implements IGameRefereeService {
 
-    private final GameRefereeMapper baseMapper;
+    private final GameRefereeMapper gameRefereeMapper;
+    private final IGameEventProjectService gameEventProjectService;
 
     /**
      * 查询裁判
@@ -45,9 +54,9 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
      */
     @Override
     public GameRefereeVo queryById(Long refereeId){
-        GameRefereeVo vo = baseMapper.selectVoById(refereeId);
+        GameRefereeVo vo = gameRefereeMapper.selectVoById(refereeId);
         if (vo.getProjectList() != null) {
-            vo.setProjectList2(JSONUtil.toList(vo.getProjectList(), String.class));
+            vo.setProjectList2(JSONUtil.toList(vo.getProjectList(), Long.class));
         }
         return vo;
     }
@@ -70,10 +79,10 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
             }
         }
         LambdaQueryWrapper<GameReferee> lqw = buildQueryWrapper(bo);
-        Page<GameRefereeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        Page<GameRefereeVo> result = gameRefereeMapper.selectVoPage(pageQuery.build(), lqw);
         result.getRecords().forEach(vo -> {
             if (vo.getProjectList() != null) {
-                vo.setProjectList2(JSONUtil.toList(vo.getProjectList(), String.class));
+                vo.setProjectList2(JSONUtil.toList(vo.getProjectList(), Long.class));
             }
         });
         return TableDataInfo.build(result);
@@ -96,10 +105,10 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
             }
         }
         LambdaQueryWrapper<GameReferee> lqw = buildQueryWrapper(bo);
-        List<GameRefereeVo> list = baseMapper.selectVoList(lqw);
+        List<GameRefereeVo> list = gameRefereeMapper.selectVoList(lqw);
         list.forEach(vo -> {
             if (vo.getProjectList() != null) {
-                vo.setProjectList2(JSONUtil.toList(vo.getProjectList(), String.class));
+                vo.setProjectList2(JSONUtil.toList(vo.getProjectList(), Long.class));
             }
         });
         return list;
@@ -122,6 +131,7 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
      * @return 是否新增成功
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean insertByBo(GameRefereeBo bo) {
         if (bo.getEventId() == null) {
             Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
@@ -136,9 +146,15 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
         }
         GameReferee add = MapstructUtils.convert(bo, GameReferee.class);
         validEntityBeforeSave(add);
-        boolean flag = baseMapper.insert(add) > 0;
+        boolean flag = gameRefereeMapper.insert(add) > 0;
         if (flag) {
-            bo.setRefereeId(add.getRefereeId());
+            if (add != null) {
+                bo.setRefereeId(add.getRefereeId());
+            }else{
+                return false;
+            }
+            // 更新项目表中的裁判组关联
+            updateProjectRefereeGroups(add.getRefereeId(), bo.getProjectList2(), new ArrayList<>());
         }
         return flag;
     }
@@ -150,6 +166,7 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
      * @return 是否修改成功
      */
     @Override
+    @Transactional(rollbackFor = Exception.class)
     public Boolean updateByBo(GameRefereeBo bo) {
         if (bo.getEventId() == null) {
             Object cacheObject = RedisUtils.getCacheObject(GameEventConstant.DEFAULT_EVENT_ID);
@@ -159,12 +176,118 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
                 bo.setEventId((Long) cacheObject);
             }
         }
+
+        // 获取原始裁判信息
+        GameRefereeVo originalReferee = queryById(bo.getRefereeId());
+        List<Long> originalProjectList = originalReferee.getProjectList2() != null ?
+            originalReferee.getProjectList2() : new ArrayList<>();
+
         if (bo.getProjectList2() != null) {
             bo.setProjectList(JSONUtil.toJsonStr(bo.getProjectList2()));
         }
+
         GameReferee update = MapstructUtils.convert(bo, GameReferee.class);
         validEntityBeforeSave(update);
-        return baseMapper.updateById(update) > 0;
+        boolean flag = gameRefereeMapper.updateById(update) > 0;
+
+        if (flag) {
+            // 更新项目表中的裁判组关联
+            List<Long> newProjectList = bo.getProjectList2() != null ? bo.getProjectList2() : new ArrayList<>();
+            updateProjectRefereeGroups(bo.getRefereeId(), newProjectList, originalProjectList);
+        }
+        return flag;
+    }
+
+    /**
+     * 更新项目表中的裁判组关联
+     *
+     * @param refereeId 裁判ID
+     * @param newProjectList 新的项目列表
+     * @param originalProjectList 原始项目列表
+     */
+    private void updateProjectRefereeGroups(Long refereeId, List<Long> newProjectList, List<Long> originalProjectList) {
+        try {
+//            String refereeIdStr = String.valueOf(refereeId);
+
+            // 找出需要移除的项目(在原列表中但不在新列表中)
+            List<Long> removedProjects = originalProjectList.stream()
+                .filter(projectId -> !newProjectList.contains(projectId))
+                .toList();
+
+            // 找出需要添加的项目(在新列表中但不在原列表中)
+            List<Long> addedProjects = newProjectList.stream()
+                .filter(projectId -> !originalProjectList.contains(projectId))
+                .toList();
+
+            // 从移除的项目中删除该裁判
+            for (Long projectId : removedProjects) {
+                removeRefereeFromProject(projectId, refereeId);
+            }
+
+            // 向添加的项目中添加该裁判
+            for (Long projectId : addedProjects) {
+                addRefereeToProject(projectId, refereeId);
+            }
+
+        } catch (Exception e) {
+            log.error("更新项目裁判组关联失败: refereeId={}, newProjectList={}, originalProjectList={}",
+                refereeId, newProjectList, originalProjectList, e);
+            throw e;
+        }
+    }
+
+    /**
+     * 从项目中移除裁判
+     *
+     * @param projectId 项目ID
+     * @param refereeId 裁判ID
+     */
+    private void removeRefereeFromProject(Long projectId, Long refereeId) {
+        try {
+            GameEventProjectVo vo = gameEventProjectService.queryById(projectId);
+            if (vo != null && vo.getRefereeGroups() != null) {
+                List<Long> refereeGroups = new ArrayList<>(vo.getRefereeGroups());
+                refereeGroups.remove(refereeId);
+
+                GameEventProject project = MapstructUtils.convert(vo, GameEventProject.class);
+
+                GameEventProjectBo projectBo = MapstructUtils.convert(project, GameEventProjectBo.class);
+//                GameEventProjectBo projectBo = ObjectUtils.copy(vo, GameEventProjectBo.class);
+                projectBo.setRefereeGroups(refereeGroups);
+                gameEventProjectService.updateByBo(projectBo);
+            }
+        } catch (Exception e) {
+            log.error("从项目中移除裁判失败: projectId={}, refereeId={}", projectId, refereeId, e);
+            throw e;
+        }
+    }
+
+    /**
+     * 向项目中添加裁判
+     *
+     * @param projectId 项目ID
+     * @param refereeId 裁判ID
+     */
+    private void addRefereeToProject(Long projectId, Long refereeId) {
+        try {
+            GameEventProjectVo vo = gameEventProjectService.queryById(projectId);
+            if (vo != null) {
+                List<Long> refereeGroups = vo.getRefereeGroups() != null ?
+                    new ArrayList<>(vo.getRefereeGroups()) : new ArrayList<>();
+
+                if (!refereeGroups.contains(refereeId)) {
+                    refereeGroups.add(refereeId);
+
+                    GameEventProject project = MapstructUtils.convert(vo, GameEventProject.class);
+                    GameEventProjectBo projectBo = MapstructUtils.convert(project, GameEventProjectBo.class);
+                    projectBo.setRefereeGroups(refereeGroups);
+                    gameEventProjectService.updateByBo(projectBo);
+                }
+            }
+        } catch (Exception e) {
+            log.error("向项目中添加裁判失败: projectId={}, refereeId={}", projectId, refereeId, e);
+            throw e;
+        }
     }
 
     /**
@@ -186,7 +309,32 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
         if(isValid){
             //TODO 做一些业务上的校验,判断是否需要校验
         }
-        return baseMapper.deleteByIds(ids) > 0;
+
+        // 删除前先从所有项目中移除该裁判
+        for (Long refereeId : ids) {
+            removeRefereeFromAllProjects(refereeId);
+        }
+
+        return gameRefereeMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 从所有项目中移除裁判
+     *
+     * @param refereeId 裁判ID
+     */
+    private void removeRefereeFromAllProjects(Long refereeId) {
+        try {
+            GameRefereeVo referee = queryById(refereeId);
+            if (referee != null && referee.getProjectList2() != null) {
+                for (Long projectId : referee.getProjectList2()) {
+                    removeRefereeFromProject(projectId, refereeId);
+                }
+            }
+        } catch (Exception e) {
+            log.error("从所有项目中移除裁判失败: refereeId={}", refereeId, e);
+            throw e;
+        }
     }
 
     /**
@@ -196,7 +344,7 @@ public class GameRefereeServiceImpl implements IGameRefereeService {
      */
     @Override
     public Long countReferee() {
-        return this.baseMapper.selectCount(
+        return this.gameRefereeMapper.selectCount(
             Wrappers.lambdaQuery(GameReferee.class)
         );
     }

+ 357 - 75
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,12 +11,11 @@ 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;
 import org.dromara.system.domain.vo.*;
+import org.dromara.system.mapper.GameAthleteMapper;
 import org.dromara.system.mapper.GameEventProjectMapper;
 import org.dromara.system.service.IGameAthleteService;
 import org.dromara.system.service.IGameEventProjectService;
@@ -34,7 +31,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;
 
@@ -58,10 +54,11 @@ public class GameScoreServiceImpl implements IGameScoreService {
     private final GameScoreMapper baseMapper;
     private final IGameTeamService gameTeamService;
     private final IGameAthleteService gameAthleteService;
-
+    private final GameAthleteMapper gameAthleteMapper;
     private final GameEventProjectMapper projectMapper;
     private final IGameEventProjectService gameEventProjectService;
 
+
     /**
      * 查询成绩
      *
@@ -178,7 +175,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
      * 保存前的数据校验
      */
     private void validEntityBeforeSave(GameScore entity) {
-        //TODO 做一些数据校验,如唯一约束
+
     }
 
     /**
@@ -543,6 +540,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
                 athleteScoreBo.setTeamId(bo.getTeamId());
                 athleteScoreBo.setIndividualPerformance(BigDecimal.ZERO); // 团体项目个人成绩为0
                 athleteScoreBo.setTeamPerformance(bo.getTeamPerformance());
+//                athleteScoreBo.setLeaderPoint(bo.getLeaderPoint());
+//                athleteScoreBo.setExtraPoint(bo.getExtraPoint());
                 athleteScoreBo.setScoreType("team");
                 athleteScoreBo.setStatusFlag("0");
                 athleteScoreBo.setStatus("0");
@@ -649,12 +648,15 @@ public class GameScoreServiceImpl implements IGameScoreService {
             // 升序项目:成绩越小,积分分配越多
             for (int i = 0; i < sortedScores.size(); i++) {
                 GameScoreVo score = sortedScores.get(i);
-                int points = i < pointValues.size() ? pointValues.get(i) : 0;
+                int basePoints = i < pointValues.size() ? pointValues.get(i) : 0;
+//                int leaderPoint = score.getLeaderPoint() != null ? score.getLeaderPoint() : 0;
+//                int extraPoint = score.getExtraPoint() != null ? score.getExtraPoint() : 0;
+//                int totalPoints = basePoints + leaderPoint + extraPoint;
 
                 // 更新成绩记录
                 GameScoreBo updateBo = new GameScoreBo();
                 updateBo.setScoreId(score.getScoreId());
-                updateBo.setScorePoint(points);
+                updateBo.setScorePoint(basePoints);
                 updateBo.setEventId(eventId);
                 updateBo.setProjectId(projectId);
 
@@ -664,12 +666,15 @@ public class GameScoreServiceImpl implements IGameScoreService {
             // 降序项目:成绩越大,积分分配越多
             for (int i = 0; i < sortedScores.size(); i++) {
                 GameScoreVo score = sortedScores.get(i);
-                int points = i < pointValues.size() ? pointValues.get(i) : 0;
+                int basePoints = i < pointValues.size() ? pointValues.get(i) : 0;
+//                int leaderPoint = score.getLeaderPoint() != null ? score.getLeaderPoint() : 0;
+//                int extraPoint = score.getExtraPoint() != null ? score.getExtraPoint() : 0;
+//                int totalPoints = basePoints + leaderPoint + extraPoint;
 
                 // 更新成绩记录
                 GameScoreBo updateBo = new GameScoreBo();
                 updateBo.setScoreId(score.getScoreId());
-                updateBo.setScorePoint(points);
+                updateBo.setScorePoint(basePoints);
                 updateBo.setEventId(eventId);
                 updateBo.setProjectId(projectId);
 
@@ -697,10 +702,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;
@@ -778,8 +783,11 @@ public class GameScoreServiceImpl implements IGameScoreService {
             Map.Entry<Long, BigDecimal> teamEntry = sortedTeamsByPerformance.get(i);
             Long teamId = teamEntry.getKey();
             BigDecimal currentPerformance = teamEntry.getValue();
-            int points = i < pointValues.size() ? pointValues.get(i) : 0;
-            
+            int basePoints = i < pointValues.size() ? pointValues.get(i) : 0;
+//            int leaderPoint = score.getLeaderPoint() != null ? score.getLeaderPoint() : 0;
+//            int extraPoint = score.getExtraPoint() != null ? score.getExtraPoint() : 0;
+//            int totalPoints = basePoints + leaderPoint + extraPoint;
+
             // 如果不是第一个,检查是否与前一个成绩相同
             if (i > 0) {
                 BigDecimal previousPerformance = sortedTeamsByPerformance.get(i - 1).getValue();
@@ -788,7 +796,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
                 }
             }
 
-            teamPoints.put(teamId, points);
+            teamPoints.put(teamId, basePoints);
             teamRanks.put(teamId, currentRank);
         }
 
@@ -800,6 +808,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
                 updateBo.setScoreId(score.getScoreId());
                 updateBo.setScoreRank(teamRanks.get(teamId));
                 updateBo.setScorePoint(teamPoints.get(teamId));
+//                updateBo.setLeaderPoint(score.getLeaderPoint());
+//                updateBo.setExtraPoint(score.getExtraPoint());
                 updateBo.setEventId(eventId);
                 updateBo.setProjectId(projectId);
 
@@ -870,21 +880,21 @@ public class GameScoreServiceImpl implements IGameScoreService {
                     String key = team.getTeamId() + "_" + project.getProjectId();
                     GameScoreVo score = scoreMap.get(key);
 
-                    int projectScore = 0;
+                    int projectPoint = 0;
                     if (score != null && score.getScorePoint() != null) {
-                        projectScore = score.getScorePoint();
-                        totalScore += projectScore;
+                        projectPoint = score.getScorePoint();
+                        totalScore += projectPoint;
                     }
 
                     // 使用项目名称设置积分
-                    exportVo.setProjectScore(project.getProjectName(), projectScore);
+                    exportVo.setProjectScore(project.getProjectName(), projectPoint);
                 }
 
                 // 设置总分
                 exportVo.setTotalScore(totalScore);
 
                 // 设置加分(暂时设为0,后续可根据业务需求调整)
-                exportVo.setExtraScore(0);
+                exportVo.setExtraPoint(0);
 
                 exportData.add(exportVo);
             }
@@ -908,63 +918,96 @@ public class GameScoreServiceImpl implements IGameScoreService {
      * 用户端查询项目成绩信息
      */
     @Override
-    public List<AppScoreVo> listAppScore(Long eventId, Long projectId) {
-        // 获取项目详细信息
-        GameEventProjectVo project = gameEventProjectService.queryById(projectId);
+    public List<AppScoreVo> listAppScore(Long eventId) {
+        try {
+            // 1. 获取所有相关成绩
+            List<GameScoreVo> allScores = baseMapper.selectVoList(
+                Wrappers.lambdaQuery(GameScore.class)
+                    .eq(GameScore::getEventId, eventId)
+            );
 
-        // 查询参与了该项目的运动员
-        List<GameAthleteVo> athletes = gameAthleteService.queryListByEventIdAndProjectId(eventId, projectId, null);
-
-        // 获取所有相关成绩
-        GameScoreBo scoreBo = new GameScoreBo();
-        scoreBo.setEventId(eventId);
-        scoreBo.setProjectId(projectId);
-        List<GameScoreVo> scores = queryList(scoreBo);
-
-        // 创建成绩映射 athleteId -> GameScoreVo
-        Map<Long, GameScoreVo> scoreMap = scores.stream()
-            .collect(Collectors.toMap(GameScoreVo::getAthleteId, Function.identity(), (existing, replacement) -> existing));
-
-        // 队伍id与名称的映射
-        Set<Long> teamIds = athletes.stream().map(GameAthleteVo::getTeamId).collect(Collectors.toSet());
-        Map<Long, String>  teamMap = gameTeamService.queryTeamIdAndName(teamIds);
-
-        List<AppScoreVo> result = result = athletes.stream().map(athlete -> {
-            AppScoreVo vo = new AppScoreVo();
-            vo.setProjectId(projectId);
-            vo.setProjectName(project != null ? project.getProjectName() : "");
-            vo.setClassification(project != null ? project.getClassification() : "");
-            vo.setAthleteId(athlete.getAthleteId());
-            vo.setAthleteName(athlete.getName());
-            vo.setTeamId(athlete.getTeamId());
-
-            // 获取队伍信息
-            if (athlete.getTeamId() != null) {
-                String teamName = teamMap.get(athlete.getTeamId());
-                vo.setTeamName(teamName != null ? teamName : "");
+            if (allScores.isEmpty()) {
+                return new ArrayList<>();
             }
 
-            // 获取成绩信息
-            GameScoreVo score = scoreMap.get(athlete.getAthleteId());
-            if (score != null) {
+            // 2. 获取所有运动员信息
+            List<GameAthleteVo> athletes = gameAthleteMapper.queryAthleteIdAndName(eventId);
+            Map<Long, GameAthleteVo> athleteMap = athletes.stream()
+                .collect(Collectors.toMap(GameAthleteVo::getAthleteId, Function.identity(), (existing, replacement) -> existing));
+
+            // 3. 获取所有队伍信息
+            Set<Long> teamIds = athletes.stream()
+                .map(GameAthleteVo::getTeamId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+            Map<Long, String> teamMap = gameTeamService.queryTeamIdAndName(teamIds);
+
+            // 4. 获取所有项目信息
+            GameEventProjectBo projectQuery = new GameEventProjectBo();
+            projectQuery.setEventId(eventId);
+            List<GameEventProjectVo> projects = gameEventProjectService.queryList(projectQuery);
+            Map<Long, GameEventProjectVo> projectMap = projects.stream()
+                .collect(Collectors.toMap(GameEventProjectVo::getProjectId, Function.identity(), (existing, replacement) -> existing));
+
+            // 5. 构建结果
+            List<AppScoreVo> result = new ArrayList<>();
+
+            for (GameScoreVo score : allScores) {
+                AppScoreVo vo = new AppScoreVo();
+
+                // 设置基本信息
+                vo.setAthleteId(score.getAthleteId());
+                vo.setTeamId(score.getTeamId());
+                vo.setProjectId(score.getProjectId());
+
+                // 设置运动员名称
+                GameAthleteVo athlete = athleteMap.get(score.getAthleteId());
+                if (athlete != null) {
+                    vo.setAthleteName(athlete.getName());
+                } else {
+                    vo.setAthleteName("");
+                }
+
+                // 设置队伍名称
+                if (score.getTeamId() != null) {
+                    String teamName = teamMap.get(score.getTeamId());
+                    vo.setTeamName(teamName != null ? teamName : "");
+                } else {
+                    vo.setTeamName("");
+                }
+
+                // 设置项目信息
+                GameEventProjectVo project = projectMap.get(score.getProjectId());
+                if (project != null) {
+                    vo.setProjectName(project.getProjectName());
+                    vo.setClassification(project.getClassification());
+                } else {
+                    vo.setProjectName("");
+                    vo.setClassification("");
+                }
+
+                // 设置成绩信息
                 vo.setIndividualPerformance(convertToBigDecimal(score.getIndividualPerformance()));
                 vo.setTeamPerformance(convertToBigDecimal(score.getTeamPerformance()));
-                vo.setScorePoint(score.getScorePoint());
-                vo.setScoreRank(score.getScoreRank());
-            } else {
-                vo.setIndividualPerformance(BigDecimal.ZERO);
-                vo.setTeamPerformance(BigDecimal.ZERO);
-                vo.setScorePoint(0);
-                vo.setScoreRank(0);
+                vo.setScorePoint(score.getScorePoint() != null ? score.getScorePoint() : 0);
+                vo.setScoreRank(score.getScoreRank() != null ? score.getScoreRank() : 0);
+
+                result.add(vo);
             }
 
-            return vo;
-        }).toList();
+            // 6. 按排名排序(排名为0或null的排在最后)
+            return result.stream()
+                .sorted((a, b) -> {
+                    Integer rankA = a.getScoreRank() != null ? a.getScoreRank() : Integer.MAX_VALUE;
+                    Integer rankB = b.getScoreRank() != null ? b.getScoreRank() : Integer.MAX_VALUE;
+                    return Integer.compare(rankA, rankB);
+                })
+                .collect(Collectors.toList());
 
-        // 按排名排序
-        return result.stream()
-            .sorted(Comparator.comparing(AppScoreVo::getScoreRank))
-            .collect(Collectors.toList());
+        } catch (Exception e) {
+            log.error("查询用户端成绩信息失败", e);
+            return new ArrayList<>();
+        }
     }
 
     /**
@@ -1025,7 +1068,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
             }
 
             // 加分列
-            rowData.put("加分", exportVo.getExtraScore());
+            rowData.put("领导加分", exportVo.getLeaderPoint());
+            rowData.put("额外加分", exportVo.getExtraPoint());
 
             dynamicData.add(rowData);
         }
@@ -1072,9 +1116,13 @@ public class GameScoreServiceImpl implements IGameScoreService {
             }
 
             // 加分列标题
-            Cell extraCell = headerRow.createCell(colIndex++);
-            extraCell.setCellValue("加分");
-            extraCell.setCellStyle(headerStyle);
+            Cell leaderBonusCell = headerRow.createCell(colIndex++);
+            leaderBonusCell.setCellValue("领导加分");
+            leaderBonusCell.setCellStyle(headerStyle);
+
+            Cell extraBonusCell = headerRow.createCell(colIndex++);
+            extraBonusCell.setCellValue("额外加分");
+            extraBonusCell.setCellStyle(headerStyle);
 
             // 填充数据行
             int rowIndex = 1;
@@ -1100,7 +1148,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
                 }
 
                 // 加分列数据
-                createCellWithStyle(dataRow, colIndex++, (Integer) rowData.get("加分"), currentRowStyle);
+                createCellWithStyle(dataRow, colIndex++, (Integer) rowData.get("领导加分"), currentRowStyle);
+                createCellWithStyle(dataRow, colIndex++, (Integer) rowData.get("额外加分"), currentRowStyle);
             }
 
             // 自动调整列宽
@@ -1208,4 +1257,237 @@ public class GameScoreServiceImpl implements IGameScoreService {
 
         cell.setCellStyle(style);
     }
+
+    /**
+     * 获取加分数据
+     */
+    @Override
+    public Map<String, Object> getBonusData(Long eventId) {
+        try {
+            // 获取所有项目
+            GameEventProjectBo projectQuery = new GameEventProjectBo();
+            projectQuery.setEventId(eventId);
+            List<GameEventProjectVo> projects = gameEventProjectService.queryList(projectQuery);
+
+            // 获取所有队伍
+            GameTeamBo teamQuery = new GameTeamBo();
+            teamQuery.setEventId(eventId);
+            List<GameTeamVo> teams = gameTeamService.queryList(teamQuery);
+
+            // 批量查询所有队伍在所有项目中的成绩
+            List<GameScoreVo> allScores = baseMapper.selectVoList(
+                Wrappers.lambdaQuery(GameScore.class)
+                    .eq(GameScore::getEventId, eventId)
+            );
+
+            // 按队伍ID和项目ID分组成绩数据
+            Map<String, GameScoreVo> scoreMap = new HashMap<>();
+            for (GameScoreVo score : allScores) {
+                if (score.getTeamId() != null && score.getProjectId() != null) {
+                    String key = score.getTeamId() + "_" + score.getProjectId();
+                    scoreMap.put(key, score);
+                }
+            }
+
+            // 构建加分数据
+            List<Map<String, Object>> bonusData = new ArrayList<>();
+
+            for (int i = 0; i < teams.size(); i++) {
+                GameTeamVo team = teams.get(i);
+                Map<String, Object> teamData = new HashMap<>();
+
+                // 设置基本信息
+                teamData.put("teamId", team.getTeamId());
+                teamData.put("teamName", team.getTeamName());
+                teamData.put("rank", i + 1);
+
+                // 计算该队伍在各项目中的积分
+                int totalScore = 0;
+                Map<String, Integer> projectScores = new HashMap<>();
+                int leaderPoint = 0;
+                int extraPoint = 0;
+
+                // 设置各项目积分
+                for (GameEventProjectVo project : projects) {
+                    String key = team.getTeamId() + "_" + project.getProjectId();
+                    GameScoreVo score = scoreMap.get(key);
+
+                    int projectScore = 0;
+                    if (score != null && score.getScorePoint() != null) {
+                        projectScore = score.getScorePoint();
+                        totalScore += projectScore;
+
+                        // 获取加分数据
+                        if (score.getLeaderPoint() != null) {
+                            leaderPoint = score.getLeaderPoint();
+                        }
+                        if (score.getExtraPoint() != null) {
+                            extraPoint = score.getExtraPoint();
+                        }
+                    }
+
+                    projectScores.put(project.getProjectName(), projectScore);
+                }
+
+                teamData.put("projectScores", projectScores);
+                teamData.put("totalScore", totalScore);
+                teamData.put("leaderPoint", leaderPoint);
+                teamData.put("extraPoint", extraPoint);
+
+                bonusData.add(teamData);
+            }
+
+            // 按总分排序,生成排名
+            bonusData.sort((a, b) -> Integer.compare((Integer) b.get("totalScore"), (Integer) a.get("totalScore")));
+            for (int i = 0; i < bonusData.size(); i++) {
+                bonusData.get(i).put("rank", i + 1);
+            }
+
+            Map<String, Object> result = new HashMap<>();
+            result.put("rows", bonusData);
+            result.put("projects", projects);
+
+            return result;
+
+        } catch (Exception e) {
+            log.error("获取加分数据失败", e);
+            throw new RuntimeException("获取加分数据失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 更新加分数据
+     */
+    @Override
+    public Boolean updateBonusData(Map<String, Object> data) {
+        try {
+            Long eventId = Long.valueOf(data.get("eventId").toString());
+            List<Map<String, Object>> updateData = (List<Map<String, Object>>) data.get("data");
+
+            for (Map<String, Object> teamData : updateData) {
+                Long teamId = Long.valueOf(teamData.get("teamId").toString());
+                Integer leaderPoint = Integer.valueOf(teamData.get("leaderPoint").toString());
+                Integer extraPoint = Integer.valueOf(teamData.get("extraPoint").toString());
+
+                // 更新该队伍在所有项目中的加分数据
+                List<GameScoreVo> teamScores = baseMapper.selectVoList(
+                    Wrappers.lambdaQuery(GameScore.class)
+                        .eq(GameScore::getEventId, eventId)
+                        .eq(GameScore::getTeamId, teamId)
+                );
+
+                for (GameScoreVo score : teamScores) {
+                    GameScoreBo updateBo = new GameScoreBo();
+                    updateBo.setScoreId(score.getScoreId());
+                    updateBo.setLeaderPoint(leaderPoint);
+                    updateBo.setExtraPoint(extraPoint);
+
+                    // 重新计算积分
+                    int basePoints = score.getScorePoint() != null ? score.getScorePoint() : 0;
+//                    int totalPoints = basePoints + leaderPoint + extraPoint;
+                    updateBo.setScorePoint(basePoints);
+
+                    updateByBo(updateBo);
+                }
+            }
+
+            return true;
+
+        } catch (Exception e) {
+            log.error("更新加分数据失败", e);
+            throw new RuntimeException("更新加分数据失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 导出加分Excel
+     */
+    @Override
+    public void exportBonusExcel(Map<String, Object> data, HttpServletResponse response) {
+        try {
+            List<Map<String, Object>> bonusData = (List<Map<String, Object>>) data.get("data");
+            List<Map<String, Object>> projects = (List<Map<String, Object>>) data.get("projects");
+
+            // 使用Apache POI导出Excel
+            try (Workbook workbook = new XSSFWorkbook()) {
+                Sheet sheet = workbook.createSheet("加分数据表");
+
+                // 创建标题行样式
+                CellStyle headerStyle = createHeaderStyle(workbook);
+                CellStyle dataStyle = createDataStyle(workbook, IndexedColors.WHITE);
+
+                // 创建标题行
+                Row headerRow = sheet.createRow(0);
+                int colIndex = 0;
+
+                // 基础列标题
+                String[] baseHeaders = {"序号", "代表队名称", "排名", "总分"};
+                for (String header : baseHeaders) {
+                    Cell cell = headerRow.createCell(colIndex++);
+                    cell.setCellValue(header);
+                    cell.setCellStyle(headerStyle);
+                }
+
+                // 动态项目列标题
+                for (Map<String, Object> project : projects) {
+                    String projectName = (String) project.get("projectName");
+                    Cell cell = headerRow.createCell(colIndex++);
+                    cell.setCellValue(projectName);
+                    cell.setCellStyle(headerStyle);
+                }
+
+                // 加分列标题
+                Cell leaderCell = headerRow.createCell(colIndex++);
+                leaderCell.setCellValue("领导加分");
+                leaderCell.setCellStyle(headerStyle);
+
+                Cell extraCell = headerRow.createCell(colIndex++);
+                extraCell.setCellValue("额外加分");
+                extraCell.setCellStyle(headerStyle);
+
+                // 填充数据行
+                int rowIndex = 1;
+                for (Map<String, Object> teamData : bonusData) {
+                    Row dataRow = sheet.createRow(rowIndex++);
+                    colIndex = 0;
+
+                    // 基础列数据
+                    createCellWithStyle(dataRow, colIndex++, rowIndex - 1, dataStyle);
+                    createCellWithStyle(dataRow, colIndex++, teamData.get("teamName"), dataStyle);
+                    createCellWithStyle(dataRow, colIndex++, teamData.get("rank"), dataStyle);
+                    createCellWithStyle(dataRow, colIndex++, teamData.get("totalScore"), dataStyle);
+
+                    // 动态项目列数据
+                    Map<String, Integer> projectScores = (Map<String, Integer>) teamData.get("projectScores");
+                    for (Map<String, Object> project : projects) {
+                        String projectName = (String) project.get("projectName");
+                        Integer score = projectScores != null ? projectScores.get(projectName) : 0;
+                        createCellWithStyle(dataRow, colIndex++, score != null ? score : 0, dataStyle);
+                    }
+
+                    // 加分列数据
+                    createCellWithStyle(dataRow, colIndex++, teamData.get("leaderPoint"), dataStyle);
+                    createCellWithStyle(dataRow, colIndex++, teamData.get("extraPoint"), dataStyle);
+                }
+
+                // 自动调整列宽
+                for (int i = 0; i < colIndex; i++) {
+                    sheet.autoSizeColumn(i);
+                }
+
+                // 设置响应头
+                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+                response.setCharacterEncoding("utf-8");
+                String fileName = "加分数据表_" + System.currentTimeMillis() + ".xlsx";
+                response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
+
+                // 写入响应
+                workbook.write(response.getOutputStream());
+            }
+
+        } catch (Exception e) {
+            log.error("导出加分Excel失败", e);
+            throw new RuntimeException("导出失败:" + e.getMessage());
+        }
+    }
 }

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

@@ -60,7 +60,7 @@ public class GameTeamServiceImpl implements IGameTeamService {
         }
         if (vo.getAthleteValue() != null) {
             log.info("teamId:{}, athleteValue:{}", teamId, vo.getAthleteValue());
-            vo.setAthleteList(JSONUtil.toList(vo.getAthleteValue(), String.class));
+            vo.setAthleteList(JSONUtil.toList(vo.getAthleteValue(), Long.class));
         }
         return vo;
     }
@@ -92,7 +92,7 @@ public class GameTeamServiceImpl implements IGameTeamService {
                     .filter(StringUtils::isNotBlank)
                     .ifPresent(athleteValue -> {
 //                        String[] projectIds = athleteValue.split(",");
-                        List<String> athleteIds = JSONUtil.toList(athleteValue, String.class);
+                        List<Long> athleteIds = JSONUtil.toList(athleteValue, Long.class);
 //                        List<GameAthlete> athletes = gameAthleteMapper.selectList(
 //                            Wrappers.<GameAthlete>lambdaQuery()
 //                                .in(GameAthlete::getAthleteId, projectIds)
@@ -153,7 +153,7 @@ public class GameTeamServiceImpl implements IGameTeamService {
         List<GameTeamVo> list = baseMapper.selectVoList(lqw);
         list.forEach(vo -> {
             if (vo.getAthleteValue() != null) {
-                vo.setAthleteList(JSONUtil.toList(vo.getAthleteValue(), String.class));
+                vo.setAthleteList(JSONUtil.toList(vo.getAthleteValue(), Long.class));
             }
         });
         return list;
@@ -298,6 +298,10 @@ public class GameTeamServiceImpl implements IGameTeamService {
                 .eq(GameTeam::getEventId, eventId)
                 .in(GameTeam::getTeamName, names)
         );
+        //批量添加队伍时,运动员列表为空列表而不是null
+        list.forEach(team -> {
+            team.setAthleteValue(new ArrayList<Long>().toString());
+        });
         if (count > 0) {
             throw new ServiceException("导入失败,有队伍名称重复");
         }
@@ -327,7 +331,7 @@ public class GameTeamServiceImpl implements IGameTeamService {
                 .in(GameTeam::getTeamId, teamIds)
         );
         list.forEach(vo -> {
-            vo.setAthleteList(JSONUtil.toList(vo.getAthleteValue(), String.class));
+            vo.setAthleteList(JSONUtil.toList(vo.getAthleteValue(), Long.class));
         });
         return list;
     }
@@ -349,6 +353,7 @@ public class GameTeamServiceImpl implements IGameTeamService {
         // 将运动员ID列表转换为JSON字符串
         String athleteValue = JSONUtil.toJsonStr(athleteIds);
         team.setAthleteValue(athleteValue);
+        team.setAthleteNum((long) athleteIds.size());
 
         // 更新队伍信息
         return baseMapper.updateById(team) > 0;
@@ -371,6 +376,23 @@ public class GameTeamServiceImpl implements IGameTeamService {
         return map;
     }
 
+    /**
+     * 获取队伍id和名称的映射关系
+     *
+     * @param teamIds
+     * @return
+     */
+    @Override
+    public Map<Long, String> getTeamIdAndNameMap(Collection<Long> teamIds) {
+        List<GameTeamVo> list = baseMapper.selectVoList(
+            Wrappers.lambdaQuery(GameTeam.class)
+                .eq(GameTeam::getTeamId, teamIds)
+        );
+        Map<Long, String> map = list.stream()
+            .collect(Collectors.toMap(GameTeamVo::getTeamId, GameTeamVo::getTeamName));
+        return map;
+    }
+
     @Override
     public Long countByEventId(Long eventId) {
         return baseMapper.selectCount(

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

@@ -40,7 +40,6 @@ public class IEnrollServiceImpl implements IEnrollService {
     private final IGameEventProjectService gameEventProjectService;
     private final IGameTeamService gameTeamService;
     private final IGameAthleteService gameAthleteService;
-    private final IGameEventService gameEventService;
 
     /**
      * 使用poi生成报名表模板

+ 20 - 4
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/IGameNavigatorServiceImpl.java

@@ -12,13 +12,16 @@ import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.system.domain.GameNavigator;
-import org.dromara.system.domain.bo.EventMenuBo;
+import org.dromara.system.domain.app.AppIndexVo;
 import org.dromara.system.domain.bo.GameNavigatorBo;
 import org.dromara.system.domain.vo.EventMenuVo;
 import org.dromara.system.domain.vo.GameNavigatorVo;
+import org.dromara.system.domain.vo.SysOssVo;
 import org.dromara.system.mapper.GameNavigatorMapper;
 import org.dromara.system.service.IEventMenuService;
+import org.dromara.system.service.IGameEventConfigService;
 import org.dromara.system.service.IGameNavigatorService;
+import org.dromara.system.service.ISysOssService;
 import org.springframework.stereotype.Service;
 
 import java.util.*;
@@ -36,6 +39,8 @@ public class IGameNavigatorServiceImpl implements IGameNavigatorService {
 
     private final GameNavigatorMapper baseMapper;
     private final IEventMenuService eventMenuService;
+    private final IGameEventConfigService gameEventConfigService;
+    private final ISysOssService ossService;
 
     /**
      * 查询主导航表
@@ -186,19 +191,30 @@ public class IGameNavigatorServiceImpl implements IGameNavigatorService {
      * @return
      */
     @Override
-    public List<GameNavigatorVo> listAppNavByEventId(Long eventId) {
+    public AppIndexVo listAppNavByEventId(Long eventId) {
         EventMenuVo vo = eventMenuService.queryByEventId(eventId);
+        AppIndexVo appIndexVo = new AppIndexVo();
         List<Long> navIdList = Optional.ofNullable(vo)
             .map(v -> JSONUtil.toList(v.getMenuList(), Long.class))
             .orElse(Collections.emptyList());
         // 如果 navIdList 为空,直接返回空分页
         if (navIdList.isEmpty()) {
-            return List.of();
+            appIndexVo.setNavigatorVos(List.of());
         }
         List<GameNavigatorVo> result = baseMapper.selectVoList(
             Wrappers.lambdaQuery(GameNavigator.class)
                 .in(GameNavigator::getNavId, navIdList)
         );
-        return result;
+        appIndexVo.setNavigatorVos(result);
+
+        // 根据赛事id查询赛事配置表获取ossId
+        Long ossId = gameEventConfigService.queryBgUrlByEventId(eventId);
+        if (ossId != null) {
+            SysOssVo oss = ossService.getById(ossId);
+            if (oss != null) {
+                appIndexVo.setBgUrl(oss.getUrl());
+            }
+        }
+        return appIndexVo;
     }
 }

+ 409 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/app/ElectrometerServiceImpl.java

@@ -0,0 +1,409 @@
+package org.dromara.system.service.impl.app;
+
+import com.esotericsoftware.minlog.Log;
+import lombok.RequiredArgsConstructor;
+import org.dromara.system.domain.*;
+import org.dromara.system.domain.vo.app.DistanceScoreUploadVo;
+import org.dromara.system.domain.vo.app.ElectrometerDataItemVo;
+import org.dromara.system.domain.vo.app.ElectrometerScoreUploadVo;
+import org.dromara.system.mapper.*;
+import org.dromara.system.service.app.IElectrometerService;
+import org.springframework.stereotype.Service;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 澳亚特设备服务实现
+ *
+ * @author zlt
+ * @date 2025-01-20
+ */
+@RequiredArgsConstructor
+@Service
+public class ElectrometerServiceImpl implements IElectrometerService {
+
+    private final GameAthleteMapper gameAthleteMapper;
+    private final GameEventProjectMapper gameEventProjectMapper;
+    private final GameAthleteCompetitionGroupMapper gameAthleteCompetitionGroupMapper;
+    private final GameEventGroupMapper gameEventGroupMapper;
+    private final GameScoreMapper gameScoreMapper;
+
+    /**
+     * 获取比赛道路信息
+     *
+     * @param eventId 赛事ID
+     * @param tjsdm 项目类型(1-径赛, 2-田赛)
+     * @return 比赛道路信息列表
+     */
+    @Override
+    public List<ElectrometerDataItemVo> getMatchRoadInfo(Long eventId, String tjsdm) {
+        // 1. 查询运动员信息
+        LambdaQueryWrapper<GameAthlete> athleteWrapper = Wrappers.lambdaQuery(GameAthlete.class)
+                .eq(GameAthlete::getEventId, eventId)
+                .eq(GameAthlete::getStatus, "0")
+                .eq(GameAthlete::getDelFlag, "0");
+        List<GameAthlete> athletes = gameAthleteMapper.selectList(athleteWrapper);
+
+        if (athletes.isEmpty()) {
+            return List.of();
+        }
+
+        // 2. 查询项目信息
+        LambdaQueryWrapper<GameEventProject> projectWrapper = Wrappers.lambdaQuery(GameEventProject.class)
+                .eq(GameEventProject::getEventId, eventId)
+                .eq(GameEventProject::getScoreRule,tjsdm);
+
+        List<GameEventProject> projects = gameEventProjectMapper.selectList(projectWrapper);
+        Map<Long, GameEventProject> projectMap = projects.stream()
+                .collect(Collectors.toMap(GameEventProject::getProjectId, p -> p));
+
+        // 3. 查询运动员比赛分组信息
+        List<Long> athleteIds = athletes.stream().map(GameAthlete::getAthleteId).collect(Collectors.toList());
+        LambdaQueryWrapper<GameAthleteCompetitionGroup> groupWrapper = Wrappers.lambdaQuery(GameAthleteCompetitionGroup.class)
+                .in(GameAthleteCompetitionGroup::getAthleteId, athleteIds);
+        List<GameAthleteCompetitionGroup> competitionGroups = gameAthleteCompetitionGroupMapper.selectList(groupWrapper);
+        Map<Long, GameAthleteCompetitionGroup> groupMap = competitionGroups.stream()
+                .collect(Collectors.toMap(GameAthleteCompetitionGroup::getAthleteId, g -> g));
+
+        // 4. 查询分组信息
+        List<Long> groupIds = competitionGroups.stream().map(GameAthleteCompetitionGroup::getGroupId).collect(Collectors.toList());
+        Map<Long, GameEventGroup> eventGroupMap;
+        if (!groupIds.isEmpty()) {
+            LambdaQueryWrapper<GameEventGroup> eventGroupWrapper = Wrappers.lambdaQuery(GameEventGroup.class)
+                    .in(GameEventGroup::getGroupId, groupIds);
+            List<GameEventGroup> eventGroups = gameEventGroupMapper.selectList(eventGroupWrapper);
+            eventGroupMap = eventGroups.stream()
+                    .collect(Collectors.toMap(GameEventGroup::getGroupId, g -> g));
+        } else {
+            eventGroupMap = Map.of();
+        }
+
+        // 5. 组装数据
+//        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+        return athletes.stream().map(athlete -> {
+            ElectrometerDataItemVo item = new ElectrometerDataItemVo();
+
+            // 基本信息
+            item.setHm(athlete.getAthleteCode());
+            item.setXm(athlete.getName());
+            item.setXb(athlete.getGender());
+            item.setDw(athlete.getUnit());
+
+            // 获取比赛分组信息
+            GameAthleteCompetitionGroup competitionGroup = groupMap.get(athlete.getAthleteId());
+            if (competitionGroup != null) {
+                item.setCcDm(String.valueOf(competitionGroup.getGroupId()));
+                item.setZc(String.valueOf(competitionGroup.getGroupIndex()));
+                item.setDc(String.valueOf(competitionGroup.getTrackIndex()));
+
+                // 组别信息
+                if (!eventGroupMap.isEmpty()) {
+                    GameEventGroup eventGroup = eventGroupMap.get(competitionGroup.getGroupId());
+                    if (eventGroup != null) {
+                        item.setZbMc(eventGroup.getGroupName());
+                        if (eventGroup.getBeginTime() != null) {
+                            item.setTm(eventGroup.getBeginTime());
+                        }
+                    }
+                }
+
+                // 项目信息 - 根据比赛分组中的项目ID查找对应项目
+                GameEventProject project = projectMap.get(competitionGroup.getProjectId());
+                if (project != null) {
+                    item.setXmMc(project.getProjectName());
+                    item.setTjsDm(project.getProjectType());
+                    item.setSx(project.getScoreRule()); // 计算规则-项目属性
+                    if (project.getStartTime() != null) {
+                        item.setTm(project.getStartTime());
+                    }
+                }
+            }
+
+            return item;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * 上传计时类项目成绩
+     *
+     * @param eventId 赛事ID
+     * @param scoreUpload 成绩上传数据
+     * @return 上传结果
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String uploadScore(Long eventId, List<ElectrometerScoreUploadVo> scoreUpload) {
+        try {
+//            Log.info("开始上传成绩,赛事ID: {}, 场次代码: {}", eventId, scoreUpload.getCcDm());
+            // 5. 保存成绩数据
+            int successCount = 0;
+            for (ElectrometerScoreUploadVo scoreItem : scoreUpload) {
+                // 2. 根据场次代码获取分组信息
+                GameAthleteCompetitionGroup groupInfo = gameAthleteCompetitionGroupMapper.selectById(scoreItem.getCcdm());
+                if (groupInfo == null) {
+                    return "未找到对应的比赛分组信息";
+                }
+                // 3. 获取项目信息
+                GameEventProject project = gameEventProjectMapper.selectById(groupInfo.getProjectId());
+                if (project == null) {
+                    return "未找到对应的项目信息";
+                }
+                // 4. 验证成绩数据与分组信息的一致性
+                String validationResult = validateScoreData(scoreItem, groupInfo);
+                if (!"OK".equals(validationResult)) {
+                    return validationResult;
+                }
+                if (saveScoreRecord(eventId, project, scoreItem)) {
+                    successCount++;
+                }
+            }
+//            Log.info("成绩上传完成,成功保存 {} 条记录", successCount);
+            return String.format("成绩上传成功,共保存 %d 条记录", successCount);
+
+        } catch (Exception e) {
+            Log.error("成绩上传失败", e);
+            throw new RuntimeException("成绩上传失败: " + e.getMessage());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public String uploadDistanceScore(Long eventId, DistanceScoreUploadVo scoreUpload) {
+        try{
+            //根据场次代码获取分组信息
+            GameAthleteCompetitionGroup groupInfo = gameAthleteCompetitionGroupMapper.selectById(scoreUpload.getCcdm());
+            if (groupInfo == null) {
+                return "未找到对应的比赛分组信息";
+            }
+            //根据分组信息获取项目信息
+            GameEventProject project = gameEventProjectMapper.selectById(groupInfo.getProjectId());
+            if (project == null) {
+                return "未找到对应的项目";
+            }
+            //根据分组信息获取运动员信息
+            List<DistanceScoreUploadVo.ScoreItem> scoreList = parseScoreData(scoreUpload.getCj());
+            GameAthlete athlete = gameAthleteMapper.selectById(groupInfo.getAthleteId());
+            if (athlete == null) {
+                return "未找到对应运动员";
+            }
+            int successCount = saveScoreRecord(eventId, project, scoreList);
+            if(successCount > 0){
+                return String.format("成绩上传成功,共保存 %d 条记录", successCount);
+            }else{
+                return "成绩上传失败";
+            }
+
+        } catch (Exception e) {
+            throw new RuntimeException("成绩上传失败: " + e.getMessage());
+        }
+    }
+
+
+    /**
+     * 解析成绩数据字符串
+     */
+    private List<DistanceScoreUploadVo.ScoreItem> parseScoreData(String scoreData) {
+        List<DistanceScoreUploadVo.ScoreItem> scoreList = new ArrayList<>();
+
+        if (scoreData == null || scoreData.trim().isEmpty()) {
+            return scoreList;
+        }
+
+        String[] items = scoreData.split("\\|");
+        for (String item : items) {
+            if (item.trim().isEmpty()) {
+                continue;
+            }
+
+            String[] fields = item.split(",");
+            // 添加更严格的数据验证
+            if (fields.length >= 15 && !fields[2].trim().isEmpty() && !fields[3].trim().isEmpty()) {
+                try {
+                    DistanceScoreUploadVo.ScoreItem scoreItem = new DistanceScoreUploadVo.ScoreItem();
+                    List<String> indexList = new ArrayList<>();
+                    indexList.add(fields[0]); // 组次
+                    indexList.add(fields[1]); // 道次
+                    scoreItem.setIndexList(indexList);
+                    scoreItem.setAthleteCode(fields[2]); // 运动员编号
+                    scoreItem.setAthleteName(fields[3]); // 姓名
+
+                    // 安全地解析成绩数据
+                    List<BigDecimal> scores = new ArrayList<>();
+                    for (int i = 4; i < fields.length - 1; i += 2) {
+                        if (i < fields.length && !fields[i].trim().isEmpty()) {
+                            try {
+                                scores.add(BigDecimal.valueOf(Double.parseDouble(fields[i])));
+                            } catch (NumberFormatException e) {
+                                // 忽略无效的成绩数据
+                                Log.warn("解析成绩数据失败: {}", fields[i], e);
+                            }
+                        }
+                    }
+                    scoreItem.setScoreList(scores);
+
+                    // 安全地解析排名
+                    try {
+                        scoreItem.setRank(Integer.valueOf(fields[fields.length-1]));
+                    } catch (NumberFormatException e) {
+                        Log.warn("解析排名失败: {}", fields[fields.length-1], e);
+                        scoreItem.setRank(0); // 默认排名
+                    }
+
+                    scoreList.add(scoreItem);
+                } catch (Exception e) {
+                    // 记录错误但继续处理其他数据
+                    Log.warn("解析成绩数据失败: {}", item, e);
+                }
+            }
+        }
+
+        return scoreList;
+    }
+
+    /**
+     * 验证成绩数据与分组信息的一致性
+     */
+    private String validateScoreData(ElectrometerScoreUploadVo scoreItem, GameAthleteCompetitionGroup groupInfo) {
+        // 验证场次代码是否一致
+        if (!scoreItem.getTrackIndex().equals(groupInfo.getTrackIndex())) {
+            return "场次代码不一致";
+        }
+        // 验证组次是否一致
+        if (!scoreItem.getGroupIndex().equals(groupInfo.getGroupIndex())) {
+            return "组次不一致";
+        }
+        // 验证运动员编号是否一致
+        if (!scoreItem.getAthleteCode().equals(groupInfo.getAthleteCode())) {
+            return "运动员编号不一致";
+        }
+        return "OK";
+    }
+
+
+    /**
+     * 保存计时类项目成绩记录
+     */
+    private boolean saveScoreRecord(Long eventId, GameEventProject project, ElectrometerScoreUploadVo scoreItem) {
+        try {
+            // 根据运动员编号查找运动员ID
+            LambdaQueryWrapper<GameAthlete> athleteWrapper = Wrappers.lambdaQuery(GameAthlete.class)
+                .eq(GameAthlete::getEventId, eventId)
+                .eq(GameAthlete::getAthleteCode, scoreItem.getAthleteCode())
+                .eq(GameAthlete::getStatus, "0")
+                .eq(GameAthlete::getDelFlag, "0");
+            GameAthlete athlete = gameAthleteMapper.selectOne(athleteWrapper);
+
+            if (athlete == null) {
+               Log.warn("未找到运动员: {}", scoreItem.getAthleteCode());
+                return false;
+            }
+
+            // 检查是否已存在成绩记录
+            LambdaQueryWrapper<GameScore> scoreWrapper = Wrappers.lambdaQuery(GameScore.class)
+                .eq(GameScore::getEventId, eventId)
+                .eq(GameScore::getProjectId, project.getProjectId())
+                .eq(GameScore::getAthleteId, athlete.getAthleteId());
+
+            GameScore existingScore = gameScoreMapper.selectOne(scoreWrapper);
+
+            if (existingScore != null) {
+                // 更新现有记录
+                existingScore.setIndividualPerformance(scoreItem.getScore());
+                existingScore.setScoreRank(scoreItem.getRank());
+                existingScore.setRemark("电计设备上传");
+
+                return gameScoreMapper.updateById(existingScore) > 0;
+            } else {
+                // 创建新记录
+                GameScore newScore = new GameScore();
+                newScore.setEventId(eventId);
+                newScore.setProjectId(project.getProjectId());
+                newScore.setAthleteId(athlete.getAthleteId());
+                newScore.setTeamId(athlete.getTeamId());
+                newScore.setIndividualPerformance(scoreItem.getScore());
+                newScore.setScoreRank(scoreItem.getRank());
+                newScore.setScoreType("individual");
+                newScore.setStatus("0"); // 正常
+                newScore.setRemark("电计设备上传");
+                return gameScoreMapper.insert(newScore) > 0;
+            }
+
+        } catch (Exception e) {
+//            log.error("保存成绩记录失败: {}", e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 保存远度距离类项目成绩记录
+     */
+    private int saveScoreRecord(Long eventId, GameEventProject project, List<DistanceScoreUploadVo.ScoreItem> scoreList) {
+        try {
+            int successCount = 0;
+            for (DistanceScoreUploadVo.ScoreItem scoreItem : scoreList) {
+                // 根据运动员编号查找运动员ID
+                LambdaQueryWrapper<GameAthlete> athleteWrapper = Wrappers.lambdaQuery(GameAthlete.class)
+                    .eq(GameAthlete::getEventId, eventId)
+                    .eq(GameAthlete::getAthleteCode, scoreItem.getAthleteCode())
+                    .eq(GameAthlete::getStatus, "0")
+                    .eq(GameAthlete::getDelFlag, "0");
+                GameAthlete athlete = gameAthleteMapper.selectOne(athleteWrapper);
+
+                if (athlete == null) {
+                    continue;
+                }
+
+                // 检查是否已存在成绩记录
+                LambdaQueryWrapper<GameScore> scoreWrapper = Wrappers.lambdaQuery(GameScore.class)
+                    .eq(GameScore::getEventId, eventId)
+                    .eq(GameScore::getProjectId, project.getProjectId())
+                    .eq(GameScore::getAthleteId, athlete.getAthleteId());
+
+                GameScore existingScore = gameScoreMapper.selectOne(scoreWrapper);
+
+                if (existingScore != null) {
+                    // 更新现有记录 - 远度距离类项目可能有多次成绩,取最好成绩
+                    BigDecimal bestScore = scoreItem.getScoreList().stream()
+                        .max(BigDecimal::compareTo)
+                        .orElse(BigDecimal.ZERO);
+                    existingScore.setIndividualPerformance(bestScore);
+                    existingScore.setScoreRank(scoreItem.getRank());
+                    existingScore.setRemark("电计设备上传-距离类");
+
+                    if (gameScoreMapper.updateById(existingScore) > 0) {
+                        successCount++;
+                    }
+                } else {
+                    // 创建新记录
+                    GameScore newScore = new GameScore();
+                    newScore.setEventId(eventId);
+                    newScore.setProjectId(project.getProjectId());
+                    newScore.setAthleteId(athlete.getAthleteId());
+                    newScore.setTeamId(athlete.getTeamId());
+
+                    // 远度距离类项目取最好成绩
+                    BigDecimal bestScore = scoreItem.getScoreList().stream()
+                        .max(BigDecimal::compareTo)
+                        .orElse(BigDecimal.ZERO);
+                    newScore.setIndividualPerformance(bestScore);
+                    newScore.setScoreRank(scoreItem.getRank());
+                    newScore.setScoreType("individual");
+                    newScore.setStatus("0");
+                    newScore.setRemark("电计设备上传-距离类");
+
+                    if (gameScoreMapper.insert(newScore) > 0) {
+                        successCount++;
+                    }
+                }
+            }
+            return successCount;
+        } catch (Exception e) {
+            return 0;
+        }
+    }
+}

+ 386 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/app/PhysicalTestServiceImpl.java

@@ -0,0 +1,386 @@
+package org.dromara.system.service.impl.app;
+
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.system.domain.GameAthlete;
+import org.dromara.system.domain.GameEventProject;
+import org.dromara.system.domain.GameScore;
+import org.dromara.system.domain.bo.GameAthleteBo;
+import org.dromara.system.domain.vo.app.PhysicalDeviceVo;
+import org.dromara.system.domain.vo.app.PhysicalTestVo;
+import org.dromara.system.mapper.GameAthleteMapper;
+import org.dromara.system.mapper.GameEventProjectMapper;
+import org.dromara.system.mapper.GameScoreMapper;
+import org.dromara.system.service.IGameAthleteService;
+import org.dromara.system.service.app.IPhysicalTestService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 体质测试Service业务层处理
+ *
+ * @author zlt
+ * @date 2025-01-27
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class PhysicalTestServiceImpl implements IPhysicalTestService {
+
+    private final GameEventProjectMapper projectMapper;
+    private final GameAthleteMapper athleteMapper;
+    private final IGameAthleteService athleteService;
+    private final GameScoreMapper scoreMapper;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean processPhysicalTestData(List<PhysicalTestVo> results, Long eventId) {
+        try {
+            log.info("开始处理体质测试数据,数据条数: {}, 赛事ID: {}", results.size(), eventId);
+
+            for (PhysicalTestVo result : results) {
+                log.debug("处理体质测试数据: testName={}, testKey={}, userId={}, name={}",
+                    result.getTestName(), result.getTestKey(), result.getUserId(), result.getName());
+
+                // 1. 处理项目数据
+                Long projectId = processProject(result, eventId);
+                log.debug("项目处理完成,projectId: {}", projectId);
+
+                // 2. 处理运动员数据
+                Long athleteId = processAthlete(result, eventId,projectId);
+                log.debug("运动员处理完成,athleteId: {}", athleteId);
+
+                // 3. 处理成绩数据
+                processScore(result, eventId, projectId, athleteId);
+                log.debug("成绩处理完成");
+            }
+
+            log.info("体质测试数据处理完成,共处理 {} 条数据", results.size());
+            return true;
+        } catch (Exception e) {
+            log.error("处理体质测试数据失败", e);
+            throw e;
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean saveDeviceData(PhysicalDeviceVo deviceVo, Long eventId) {
+        try {
+            log.info("开始处理设备体质测试数据,项目: {}, 记录数: {}", deviceVo.getTestName(), deviceVo.getRecordCount());
+
+            // 1. 处理项目数据 - 根据testName查找或创建项目
+            Long projectId = processDeviceProject(deviceVo.getTestName(), eventId);
+            log.debug("设备项目处理完成,projectId: {}", projectId);
+
+            // 2. 处理每条记录
+            for (PhysicalDeviceVo.Record record : deviceVo.getResultInfo()) {
+                log.debug("处理设备记录: stuNo={}, name={}", record.getStuNo(), record.getName());
+
+                // 3. 处理运动员数据 - 根据stuNo作为身份证号查找或创建运动员
+                Long athleteId = processDeviceAthlete(record, eventId, projectId);
+                log.debug("设备运动员处理完成,athleteId: {}", athleteId);
+
+                // 4. 处理成绩数据 - 将testTime、resultData、referee存为JSON
+                processDeviceScore(record, eventId, projectId, athleteId);
+                log.debug("设备成绩处理完成");
+            }
+
+            log.info("设备体质测试数据处理完成,共处理 {} 条数据", deviceVo.getRecordCount());
+            return true;
+        } catch (Exception e) {
+            log.error("处理设备体质测试数据失败", e);
+            throw e;
+        }
+    }
+
+    /**
+     * 处理项目数据
+     */
+    private Long processProject(PhysicalTestVo result, Long eventId) {
+        // 根据testKey查找项目
+        LambdaQueryWrapper<GameEventProject> projectWrapper = Wrappers.lambdaQuery(GameEventProject.class)
+            .eq(GameEventProject::getEventId, eventId)
+            .eq(GameEventProject::getRemark, result.getTestKey())
+            .eq(GameEventProject::getDelFlag, "0");
+
+        GameEventProject existingProject = projectMapper.selectOne(projectWrapper);
+
+        if (existingProject != null) {
+            log.debug("找到现有项目,projectId: {}", existingProject.getProjectId());
+            // 更新项目名称(如果不同)
+            if (!result.getTestName().equals(existingProject.getProjectName())) {
+                log.info("项目名称不符: {} -> {}", existingProject.getProjectName(), result.getTestName());
+                throw new RuntimeException("项目名称不符");
+            }
+            return existingProject.getProjectId();
+        } else {
+            // 创建新项目
+            log.info("创建新项目: testName={}, testKey={}", result.getTestName(), result.getTestKey());
+            GameEventProject newProject = new GameEventProject();
+            newProject.setEventId(eventId);
+            newProject.setProjectName(result.getTestName());
+            newProject.setRemark(result.getTestKey());
+            newProject.setProjectType("5"); // 体测
+            newProject.setClassification("0"); // 个人
+            newProject.setStatus("0"); // 正常状态
+            newProject.setDelFlag("0");
+
+            int insertResult = projectMapper.insert(newProject);
+            if (insertResult > 0) {
+                log.info("新项目创建成功,projectId: {}", newProject.getProjectId());
+                return newProject.getProjectId();
+            } else {
+                log.error("项目插入失败");
+                throw new RuntimeException("项目创建失败");
+            }
+        }
+    }
+
+    /**
+     * 处理运动员数据
+     */
+    private Long processAthlete(PhysicalTestVo result, Long eventId, Long projectId) {
+        // 根据身份证号查找运动员
+        LambdaQueryWrapper<GameAthlete> athleteWrapper = Wrappers.lambdaQuery(GameAthlete.class)
+            .eq(GameAthlete::getEventId, eventId)
+            .eq(GameAthlete::getIdCard, result.getUserId())
+            .eq(GameAthlete::getDelFlag, "0");
+
+        GameAthlete existingAthlete = athleteMapper.selectOne(athleteWrapper);
+
+        if (existingAthlete != null) {
+            log.debug("找到现有运动员,athleteId: {}", existingAthlete.getAthleteId());
+            // 更新运动员姓名(如果不同)
+            if (!result.getName().equals(existingAthlete.getName())) {
+                log.info("要更新的运动员姓名不符: {} -> {}", existingAthlete.getName(), result.getName());
+                throw new RuntimeException("要更新的运动员姓名不符");
+            }
+            // 更新项目列表
+            updateAthleteProjectList(existingAthlete, projectId);
+            return existingAthlete.getAthleteId();
+        } else {
+            // 创建新运动员
+            log.info("创建新运动员: name={}, idCard={}", result.getName(), result.getUserId());
+            GameAthleteBo newAthlete = new GameAthleteBo();
+            newAthlete.setEventId(eventId);
+            newAthlete.setIdCard(result.getUserId());
+            newAthlete.setName(result.getName());
+            // 初始化项目列表并添加项目ID
+            newAthlete.setProjectList(new ArrayList<>());
+            newAthlete.getProjectList().add(projectId);
+            newAthlete.setStatus("0"); // 正常状态
+
+            boolean res = athleteService.insertByBo(newAthlete);
+            if (res) {
+                log.info("新运动员创建成功,athleteId: {}", newAthlete.getAthleteId());
+                return newAthlete.getAthleteId();
+            } else {
+                log.error("运动员插入失败:{}", newAthlete.getName());
+                throw new RuntimeException("运动员创建失败");
+            }
+        }
+    }
+
+    /**
+     * 处理成绩数据
+     */
+    private void processScore(PhysicalTestVo result, Long eventId, Long projectId, Long athleteId) {
+        // 检查是否已存在该运动员该项目的成绩
+        LambdaQueryWrapper<GameScore> scoreWrapper = Wrappers.lambdaQuery(GameScore.class)
+            .eq(GameScore::getEventId, eventId)
+            .eq(GameScore::getProjectId, projectId)
+            .eq(GameScore::getAthleteId, athleteId)
+            .eq(GameScore::getDelFlag, "0");
+
+        GameScore existingScore = scoreMapper.selectOne(scoreWrapper);
+
+        // 构建remark数据
+        Map<String, Object> remarkData = new HashMap<>();
+        remarkData.put("testTime", result.getTestTime());
+        remarkData.put("resultData", result.getResultData());
+        String remarkJson = JSONUtil.toJsonStr(remarkData);
+
+        if (existingScore != null) {
+            // 更新现有成绩
+            log.info("更新现有成绩记录,scoreId: {}, 新分数: {}", existingScore.getScoreId(), result.getResultScore());
+            existingScore.setIndividualPerformance(result.getResultScore());
+            existingScore.setRemark(remarkJson);
+            scoreMapper.updateById(existingScore);
+        } else {
+            // 创建新成绩记录
+            log.info("创建新成绩记录,athleteId: {}, projectId: {}, score: {}",
+                athleteId, projectId, result.getResultScore());
+            GameScore newScore = new GameScore();
+            newScore.setEventId(eventId);
+            newScore.setProjectId(projectId);
+            newScore.setAthleteId(athleteId);
+            newScore.setIndividualPerformance(result.getResultScore());
+            newScore.setRemark(remarkJson);
+            newScore.setScoreType("physicalTest"); // 默认成绩类型
+            newScore.setStatus("0"); // 正常状态
+            newScore.setDelFlag("0");
+
+            int insertResult = scoreMapper.insert(newScore);
+            if (insertResult > 0) {
+                log.info("新成绩记录创建成功,scoreId: {}", newScore.getScoreId());
+            } else {
+                log.error("成绩记录插入失败");
+                throw new RuntimeException("成绩记录创建失败");
+            }
+        }
+    }
+
+    /**
+     * 处理设备项目数据
+     */
+    private Long processDeviceProject(String testName, Long eventId) {
+        // 根据testName查找项目
+        LambdaQueryWrapper<GameEventProject> projectWrapper = Wrappers.lambdaQuery(GameEventProject.class)
+            .eq(GameEventProject::getEventId, eventId)
+            .eq(GameEventProject::getProjectName, testName)
+            .eq(GameEventProject::getDelFlag, "0");
+
+        GameEventProject existingProject = projectMapper.selectOne(projectWrapper);
+
+        if (existingProject != null) {
+            log.debug("找到现有项目,projectId: {}", existingProject.getProjectId());
+            return existingProject.getProjectId();
+        } else {
+            log.error(testName  + "项目不存在"); //不能创建项目,因为没有项目key
+            throw new RuntimeException(testName  + "项目不存在");
+
+        }
+    }
+
+    /**
+     * 处理设备运动员数据
+     */
+    private Long processDeviceAthlete(PhysicalDeviceVo.Record record, Long eventId, Long projectId) {
+        // 根据stuNo作为身份证号查找运动员
+        LambdaQueryWrapper<GameAthlete> athleteWrapper = Wrappers.lambdaQuery(GameAthlete.class)
+            .eq(GameAthlete::getEventId, eventId)
+            .eq(GameAthlete::getIdCard, record.getStuNo())
+            .eq(GameAthlete::getDelFlag, "0");
+
+        GameAthlete existingAthlete = athleteMapper.selectOne(athleteWrapper);
+
+        if (existingAthlete != null) {
+            log.debug("找到现有运动员,athleteId: {}", existingAthlete.getAthleteId());
+            // 更新运动员姓名(如果不同)
+            if (!record.getName().equals(existingAthlete.getName())) {
+                log.info("要更新的运动员姓名不符: {} -> {}", existingAthlete.getName(), record.getName());
+                throw new RuntimeException("要更新的运动员姓名不符");
+            }
+            // 更新项目列表
+            updateAthleteProjectList(existingAthlete, projectId);
+            return existingAthlete.getAthleteId();
+        } else {
+            // 创建新运动员
+            log.info("创建新运动员: name={}, stuNo={}", record.getName(), record.getStuNo());
+            GameAthleteBo newAthlete = new GameAthleteBo();
+            newAthlete.setEventId(eventId);
+            newAthlete.setIdCard(record.getStuNo()); // stuNo映射到IdCard
+            newAthlete.setName(record.getName()); // name映射到name
+            // 初始化项目列表并添加项目ID
+            newAthlete.setProjectList(new ArrayList<>());
+            newAthlete.getProjectList().add(projectId);
+            newAthlete.setStatus("0"); // 正常状态
+
+            boolean res = athleteService.insertByBo(newAthlete);
+            if (res) {
+                log.info("新运动员创建成功,athleteId: {}", newAthlete.getAthleteId());
+                return newAthlete.getAthleteId();
+            } else {
+                log.error("运动员插入失败:{}", newAthlete.getName());
+                throw new RuntimeException("运动员创建失败");
+            }
+        }
+    }
+
+    /**
+     * 处理设备成绩数据
+     */
+    private void processDeviceScore(PhysicalDeviceVo.Record record, Long eventId, Long projectId, Long athleteId) {
+        // 检查是否已存在该运动员该项目的成绩
+        LambdaQueryWrapper<GameScore> scoreWrapper = Wrappers.lambdaQuery(GameScore.class)
+            .eq(GameScore::getEventId, eventId)
+            .eq(GameScore::getProjectId, projectId)
+            .eq(GameScore::getAthleteId, athleteId)
+            .eq(GameScore::getDelFlag, "0");
+
+        GameScore existingScore = scoreMapper.selectOne(scoreWrapper);
+
+        // 构建remark数据 - 包含testTime、resultData、referee
+        Map<String, Object> remarkData = new HashMap<>();
+        remarkData.put("testTime", record.getTestTime());
+        remarkData.put("resultData", record.getResultData());
+        remarkData.put("referee", record.getReferee());
+        String remarkJson = JSONUtil.toJsonStr(remarkData);
+
+        if (existingScore != null) {
+            // 更新现有成绩
+            log.info("更新现有成绩记录,scoreId: {}", existingScore.getScoreId());
+            existingScore.setRemark(remarkJson);
+            scoreMapper.updateById(existingScore);
+        } else {
+            // 创建新成绩记录
+            log.info("创建新成绩记录,athleteId: {}, projectId: {}", athleteId, projectId);
+            GameScore newScore = new GameScore();
+            newScore.setEventId(eventId);
+            newScore.setProjectId(projectId);
+            newScore.setAthleteId(athleteId);
+            newScore.setRemark(remarkJson);
+            newScore.setScoreType("physicalDevice"); // 默认成绩类型
+            newScore.setStatus("0"); // 正常状态
+            newScore.setDelFlag("0");
+
+            int insertResult = scoreMapper.insert(newScore);
+            if (insertResult > 0) {
+                log.info("新成绩记录创建成功,scoreId: {}", newScore.getScoreId());
+            } else {
+                log.error("成绩记录插入失败");
+                throw new RuntimeException("成绩记录创建失败");
+            }
+        }
+    }
+
+    /**
+     * 更新运动员的项目列表
+     */
+    private void updateAthleteProjectList(GameAthlete athlete, Long projectId) {
+        try {
+            // 解析现有的项目列表
+            List<Long> projectList = new ArrayList<>();
+            if (StringUtils.isNotBlank(athlete.getProjectValue())) {
+                projectList = JSONUtil.toList(athlete.getProjectValue(), Long.class);
+            }
+
+            // 检查项目是否已存在
+//            String projectIdStr = projectId.toString();
+            if (!projectList.contains(projectId)) {
+                projectList.add(projectId);
+
+                // 转换为Bo对象并更新
+                GameAthleteBo athleteBo = MapstructUtils.convert(athlete, GameAthleteBo.class);
+                athleteBo.setProjectList(projectList);
+
+                athleteService.updateByBo(athleteBo);
+                log.info("已为运动员 {} 添加项目 {}", athlete.getName(), projectId);
+            }
+        } catch (Exception e) {
+            log.error("更新运动员项目列表失败", e);
+            // 这里不抛异常,避免影响主流程
+        }
+    }
+}

+ 293 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/service/impl/app/UserEventServiceImpl.java

@@ -0,0 +1,293 @@
+package org.dromara.system.service.impl.app;
+
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.system.domain.GameAthlete;
+import org.dromara.system.domain.GameEvent;
+import org.dromara.system.domain.GameEventProject;
+import org.dromara.system.domain.GameScore;
+import org.dromara.system.domain.app.GameUser;
+import org.dromara.system.domain.app.WxLoginResult;
+import org.dromara.system.domain.vo.GameAthleteVo;
+import org.dromara.system.domain.vo.GameEventProjectVo;
+import org.dromara.system.domain.vo.GameEventVo;
+import org.dromara.system.domain.vo.app.UserEventInfoVo;
+import org.dromara.system.domain.vo.app.UserLoginVo;
+import org.dromara.system.mapper.*;
+import org.dromara.system.mapper.app.GameUserMapper;
+import org.dromara.system.service.app.IUserEventService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
+
+import java.time.Duration;
+import java.util.*;
+
+@Service
+public class UserEventServiceImpl implements IUserEventService {
+    @Autowired
+    private GameUserMapper gameUserMapper;
+
+    @Autowired
+    private GameAthleteMapper gameAthleteMapper;
+
+    @Autowired
+    private GameScoreMapper gameScoreMapper;
+
+    @Autowired
+    private GameEventProjectMapper gameEventProjectMapper;
+
+    @Autowired
+    private GameEventMapper gameEventMapper;
+
+    @Autowired
+    private WebClient webClient;
+
+    // Jackson ObjectMapper用于JSON解析
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    // 微信小程序配置(需要在配置文件中设置)
+    @Value("${wechat.miniapp.appid:}")
+    private String appId;
+
+    @Value("${wechat.miniapp.secret:}")
+    private String secret;
+
+    @Override
+    public UserEventInfoVo login(UserLoginVo loginVo) {
+        try {
+            // 步骤1:通过code获取微信openid和session_key
+            WxLoginResult wxResult = getWxLoginResult(loginVo.getCode());
+            if (wxResult == null || wxResult.getOpenid() == null || wxResult.getOpenid().isEmpty()) {
+                throw new RuntimeException("微信登录失败,无法获取用户信息");
+            }
+
+            // 步骤2:查找或创建用户
+            GameUser user = findOrCreateUser(wxResult.getOpenid(), loginVo);
+
+            // 步骤3:返回用户基本信息
+            UserEventInfoVo result = new UserEventInfoVo();
+            result.setUserId(user.getUserId());
+            result.setUsername(user.getUsername());
+
+            return result;
+        } catch (Exception e) {
+            throw new RuntimeException("微信登录失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 通过code获取微信登录结果
+     */
+    private WxLoginResult getWxLoginResult(String code) {
+        try {
+            if (appId == null || appId.isEmpty() || secret == null || secret.isEmpty()) {
+                throw new RuntimeException("微信小程序配置未设置");
+            }
+
+            String url = String.format(
+                "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
+                appId, secret, code
+            );
+
+            // 先获取字符串响应,避免Content-Type不匹配问题
+            String responseBody = webClient.get()
+                .uri(url)
+                .retrieve()
+                .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(),
+                    response -> response.bodyToMono(String.class)
+                        .flatMap(body -> Mono.error(new RuntimeException("HTTP错误: " + response.statusCode() + ", 响应: " + body))))
+                .bodyToMono(String.class)
+                .timeout(Duration.ofSeconds(30)) // 30秒超时
+                .block(); // 同步等待结果
+
+            if (responseBody == null || responseBody.trim().isEmpty()) {
+                throw new RuntimeException("微信接口返回空结果");
+            }
+
+            // 手动解析JSON响应
+            WxLoginResult result = parseWxResponse(responseBody);
+            
+            if (result.getErrcode() != null && result.getErrcode() != 0) {
+                throw new RuntimeException("微信接口返回错误:" + result.getErrmsg() + " (错误码: " + result.getErrcode() + ")");
+            }
+            
+            if (result.getOpenid() == null || result.getOpenid().isEmpty()) {
+                throw new RuntimeException("微信接口未返回openid");
+            }
+            
+            return result;
+        } catch (Exception e) {
+            if (e instanceof RuntimeException) {
+                throw e;
+            }
+            throw new RuntimeException("调用微信接口失败:" + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 解析微信API响应
+     */
+    private WxLoginResult parseWxResponse(String responseBody) {
+        try {
+            // 使用Jackson解析JSON
+            WxLoginResult result = objectMapper.readValue(responseBody, WxLoginResult.class);
+            return result;
+        } catch (Exception e) {
+            throw new RuntimeException("解析微信响应失败:" + e.getMessage() + ", 响应内容: " + responseBody);
+        }
+    }
+
+    /**
+     * 查找或创建用户
+     */
+    private GameUser findOrCreateUser(String openid, UserLoginVo loginVo) {
+        // 先通过openid查找用户
+        GameUser user = gameUserMapper.selectByOpenid(openid);
+
+        if (user == null) {
+            // 用户不存在,创建新用户
+            user = new GameUser();
+            user.setOpenid(openid);
+            user.setUsername("wx_" + openid.substring(0, Math.min(8, openid.length()))); // 生成用户名
+            user.setNickname(loginVo.getNickName() != null ? loginVo.getNickName() : "微信用户");
+            user.setAvatar(loginVo.getAvatarUrl());
+            // 注意:如果数据库password字段允许为NULL,则不需要设置密码
+            // 如果数据库password字段不允许为NULL,请取消注释下面这行代码
+            // user.setPassword("WX_LOGIN_" + System.currentTimeMillis());
+            user.setCreateTime(new Date());
+            user.setUpdateTime(new Date());
+            user.setStatus("0"); // 正常状态
+            user.setDelFlag("0");
+
+            // 插入用户
+            gameUserMapper.insert(user);
+        } else {
+            // 用户存在,更新信息
+            if (loginVo.getNickName() != null && !loginVo.getNickName().isEmpty()) {
+                user.setNickname(loginVo.getNickName());
+            }
+            if (loginVo.getAvatarUrl() != null && !loginVo.getAvatarUrl().isEmpty()) {
+                user.setAvatar(loginVo.getAvatarUrl());
+            }
+            user.setUpdateTime(new Date());
+
+            // 更新用户
+            gameUserMapper.updateById(user);
+        }
+
+        return user;
+    }
+
+    @Override
+    public UserEventInfoVo getUserEventInfo(Long userId) {
+        // 步骤1:查询用户基本信息
+        GameUser user = gameUserMapper.selectUserById(userId);
+        if (user == null) {
+            throw new RuntimeException("用户不存在");
+        }
+
+        // 步骤2:查询用户关联的运动员信息
+        GameAthlete athlete = gameAthleteMapper.selectByUserId(userId);
+        if (athlete == null) {
+            throw new RuntimeException("用户未关联运动员信息");
+        }
+
+        // 步骤3:查询运动员参与的项目ID列表
+        List<Long> projectIds = gameScoreMapper.selectProjectIdsByAthleteId(athlete.getAthleteId());
+        if (CollectionUtils.isEmpty(projectIds)) {
+            // 用户没有参与任何项目
+            return buildEmptyUserEventInfo(user, athlete);
+        }
+
+        // 步骤4:查询项目详细信息
+        List<GameEventProject> projects = gameEventProjectMapper.selectBatchIds(projectIds);
+
+        // 步骤5:查询赛事信息
+        GameEvent event = gameEventMapper.selectById(projects.get(0).getEventId());
+
+        // 步骤6:查询运动员在各项目中的成绩
+        List<GameScore> scores = gameScoreMapper.selectByAthleteAndProjects(athlete.getAthleteId(), projectIds);
+
+        // 步骤7:组装数据返回
+        return assembleUserEventInfo(user, athlete, event, projects, scores);
+    }
+
+    private UserEventInfoVo buildEmptyUserEventInfo(GameUser user, GameAthlete athlete) {
+        UserEventInfoVo result = new UserEventInfoVo();
+        result.setUserId(user.getUserId());
+        result.setUsername(user.getUsername());
+
+        // 设置运动员信息
+        GameAthleteVo athleteInfo = MapstructUtils.convert(athlete, GameAthleteVo.class);
+        result.setAthleteInfo(athleteInfo);
+
+        // 设置空的赛事和项目信息
+        result.setEventInfo(null);
+        result.setProjectList(new ArrayList<>());
+
+        return result;
+    }
+
+    private UserEventInfoVo assembleUserEventInfo(GameUser user, GameAthlete athlete,
+                                                  GameEvent event, List<GameEventProject> projects,
+                                                  List<GameScore> scores) {
+        UserEventInfoVo result = new UserEventInfoVo();
+        result.setUserId(user.getUserId());
+        result.setUsername(user.getUsername());
+
+        // 组装运动员信息
+        GameAthleteVo athleteInfo = MapstructUtils.convert(athlete, GameAthleteVo.class);
+        result.setAthleteInfo(athleteInfo);
+
+        // 组装赛事信息
+        GameEventVo eventInfo = MapstructUtils.convert(event, GameEventVo.class);
+        result.setEventInfo(eventInfo);
+
+        // 组装项目信息
+        List<GameEventProjectVo> projectList = new ArrayList<>();
+        for (GameEventProject project : projects) {
+            GameEventProjectVo projectInfo = MapstructUtils.convert(project, GameEventProjectVo.class);
+
+            // 查找该项目的成绩信息
+            GameScore score = findScoreByProjectId(scores, project.getProjectId());
+            if (score != null && projectInfo != null) {
+                // 将成绩信息添加到项目信息中,可以通过扩展字段或备注字段存储
+                // 这里我们使用备注字段来存储额外的成绩信息
+                String scoreInfo = String.format("个人成绩:%s, 团队成绩:%s, 积分:%d, 排名:%d, 奖项:%s",
+                    score.getIndividualPerformance() != null ? score.getIndividualPerformance().toString() : "无",
+                    score.getTeamPerformance() != null ? score.getTeamPerformance().toString() : "无",
+                    score.getScorePoint() != null ? score.getScorePoint() : 0,
+                    score.getScoreRank() != null ? score.getScoreRank() : 0,
+                    calculateAward(score.getScoreRank()));
+                projectInfo.setRemark(scoreInfo);
+            }
+
+            projectList.add(projectInfo);
+        }
+
+        // 按项目开始时间排序
+        projectList.sort(Comparator.comparing(GameEventProjectVo::getStartTime));
+        result.setProjectList(projectList);
+
+        return result;
+    }
+
+    private GameScore findScoreByProjectId(List<GameScore> scores, Long projectId) {
+        return scores.stream()
+            .filter(score -> score.getProjectId().equals(projectId))
+            .findFirst()
+            .orElse(null);
+    }
+
+    private String calculateAward(Integer rank) {
+        if (rank == null) return "无";
+        if (rank == 1) return "金牌";
+        if (rank == 2) return "银牌";
+        if (rank == 3) return "铜牌";
+        return "无";
+    }
+}

+ 1 - 1
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/util/FilteredCommentInputStream.java → ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/utils/FilteredCommentInputStream.java

@@ -1,4 +1,4 @@
-package org.dromara.system.util;
+package org.dromara.system.utils;
 
 import java.io.*;
 import java.nio.charset.Charset;

+ 194 - 0
ruoyi-modules/ruoyi-game-event/src/main/java/org/dromara/system/utils/QRCodeUtils.java

@@ -0,0 +1,194 @@
+package org.dromara.system.utils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.WriterException;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.qrcode.QRCodeWriter;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+import jakarta.annotation.Resource;
+import org.dromara.system.domain.app.QRCodeReferee;
+import org.dromara.system.domain.vo.GameEventProjectVo;
+import org.dromara.system.domain.vo.GameEventVo;
+import org.dromara.system.domain.vo.GameRefereeVo;
+import org.dromara.system.service.IGameEventProjectService;
+import org.dromara.system.service.IGameEventService;
+import org.springframework.stereotype.Component;
+
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * 二维码生成工具类
+ *
+ * @author zlt
+ * @date 2025-01-27
+ */
+@Component
+public class QRCodeUtils {
+
+    @Resource
+    private ObjectMapper objectMapper;
+
+    @Resource
+    private IGameEventProjectService gameEventProjectService;
+
+    @Resource
+    private IGameEventService gameEventService;
+
+    /**
+     * 生成二维码并返回Base64编码的图片数据
+     *
+     * @param content 二维码内容
+     * @param width   图片宽度
+     * @param height  图片高度
+     * @return Base64编码的图片数据
+     */
+    public String generateQRCodeBase64(String content, int width, int height) {
+        try {
+            // 设置二维码参数
+            Map<EncodeHintType, Object> hints = new HashMap<>();
+            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 提高容错级别
+            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
+            hints.put(EncodeHintType.MARGIN, 2);// 增加边距
+
+            // 生成二维码
+            QRCodeWriter qrCodeWriter = new QRCodeWriter();
+            BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
+
+            // 转换为BufferedImage
+            BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
+
+            // 转换为字节数组
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            javax.imageio.ImageIO.write(bufferedImage, "PNG", baos);
+            byte[] imageBytes = baos.toByteArray();
+
+            // 转换为Base64
+            return "data:image/png;base64," + Base64.getEncoder().encodeToString(imageBytes);
+
+        } catch (WriterException | IOException e) {
+            throw new RuntimeException("生成二维码失败", e);
+        }
+    }
+
+    /**
+     * 生成二维码并返回Base64编码的图片数据(默认尺寸300x300)
+     *
+     * @param content 二维码内容
+     * @return Base64编码的图片数据
+     */
+    public String generateQRCodeBase64(String content) {
+        return generateQRCodeBase64(content, 300, 300);
+    }
+
+    /**
+     * 根据裁判信息构建二维码载荷对象(返回 QRCodeReferee)
+     */
+    public QRCodeReferee buildRefereeData(GameRefereeVo referee) {
+        if (referee == null) {
+            return null;
+        }
+        QRCodeReferee data = new QRCodeReferee();
+        data.setEventId(referee.getEventId());
+        // eventName
+        try {
+            if (referee.getEventId() != null) {
+                GameEventVo eventVo = gameEventService.queryById(referee.getEventId());
+                if (eventVo != null) {
+                    data.setEventName(eventVo.getEventName());
+                }
+            }
+        } catch (Exception ignore) {}
+        data.setRefereeId(referee.getRefereeId());
+        data.setName(referee.getName());
+        data.setAccount(referee.getAccount());
+        data.setPassword(referee.getPassword());
+
+        // 将项目ID列表转换为项目VO列表
+        List<Long> projectIds = referee.getProjectList2();
+        if (projectIds != null && !projectIds.isEmpty()) {
+            List<GameEventProjectVo> projects = new ArrayList<>();
+            for (Long pid : projectIds) {
+                if (pid == null) continue;
+                GameEventProjectVo pvo = gameEventProjectService.queryById(pid);
+                if (pvo != null) {
+                    projects.add(pvo);
+                }
+            }
+            data.setProjectList(projects);
+        } else {
+            data.setProjectList(Collections.emptyList());
+        }
+        return data;
+    }
+
+    public String buildRefereeData2(GameRefereeVo referee) {
+        try {
+            // 创建简化的数据结构,只包含必要信息
+            Map<String, Object> simpleData = new HashMap<>();
+            simpleData.put("eventId", referee.getEventId());
+            try {
+                if (referee.getEventId() != null) {
+                    GameEventVo eventVo = gameEventService.queryById(referee.getEventId());
+                    if (eventVo != null) {
+                        simpleData.put("eventName", eventVo.getEventName());
+                    }
+                }
+            } catch (Exception ignore) {}
+            simpleData.put("refereeId", referee.getRefereeId());
+            simpleData.put("refereeName", referee.getName());
+            simpleData.put("account", referee.getAccount());
+            simpleData.put("password", referee.getPassword());
+
+            // 将项目ID列表转换为项目VO列表
+            List<Long> projectIds = referee.getProjectList2();
+            if (projectIds != null && !projectIds.isEmpty()) {
+                Map<String, Object> projectList = new HashMap<>();
+                for (Long pid : projectIds) {
+                    if (pid == null) continue;
+                    GameEventProjectVo pvo = gameEventProjectService.queryById(pid);
+                    if (pvo != null) {
+                        Map<String, Object> project = new HashMap<>();
+                        project.put("projectType", pvo.getProjectType());
+                        project.put("projectName", pvo.getProjectName());
+                        project.put("classification", pvo.getClassification());
+                        project.put("orderType", pvo.getOrderType());
+                        project.put("gradeRule", pvo.getScoreRule());
+                        project.put("admissionRank",pvo.getRoundType());
+                        project.put("admissionPoint",pvo.getScoreValue());
+                        projectList.put(pvo.getProjectId().toString(), project);
+                    }
+                }
+                simpleData.put("projectList", projectList);
+            } else {
+                simpleData.put("projectList", Collections.emptyMap());
+            }
+
+            String content = objectMapper.writeValueAsString(simpleData);
+            System.out.println("二维码内容长度: " + content.length());
+            System.out.println("二维码内容: " + content);
+
+            return generateQRCodeBase64(content, 400, 400); // 使用更大的尺寸
+        } catch (Exception e) {
+            throw new RuntimeException("生成裁判二维码失败", e);
+        }
+    }
+
+    /**
+     * 生成裁判二维码(载荷为 QRCodeReferee 的 JSON)并返回Base64
+     */
+    public String generateRefereeQRCodeBase64(GameRefereeVo referee) {
+        try {
+            QRCodeReferee payload = buildRefereeData(referee);
+            String content = objectMapper.writeValueAsString(payload);
+            return generateQRCodeBase64(content);
+        } catch (Exception e) {
+            throw new RuntimeException("生成裁判二维码失败", e);
+        }
+    }
+}

+ 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>

+ 25 - 5
ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/GameScoreMapper.xml

@@ -4,13 +4,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="org.dromara.system.mapper.GameScoreMapper">
 
-    <select id="listAppScoreByEventIdGroupProjectId">
+    <select id="listAppScoreByEventIdGroupProjectId" resultType="org.dromara.system.domain.GameScore">
         SELECT *
-        FROM (SELECT ROW_NUMBER() OVER (PARTITION BY project_id ORDER BY score_value DESC) AS rank_score, game_score.*
-              FROM game_score
-              WHERE event_id = #{eventId}) ranked
+        FROM (
+        SELECT
+        @row_number := CASE
+        WHEN @project = project_id THEN @row_number + 1
+        ELSE 1
+        END AS rank_score,
+        @project := project_id,
+        gs.*
+        FROM game_score gs
+        CROSS JOIN (SELECT @row_number := 0, @project := 0) r
+        WHERE event_id = #{eventId}
+        ORDER BY project_id, score_point DESC
+        ) ranked
         WHERE rank_score &lt;= 3
-        ORDER BY ranked.project_id, ranked.score_value DESC;
+        ORDER BY project_id, score_point DESC;
     </select>
 
     <select id="selectVoByAthleteIdAndProjectId" resultType="GameScoreVo">
@@ -18,4 +28,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         FROM game_score
         WHERE athlete_id = #{athleteId} AND project_id = #{projectId}
     </select>
+
+    <select id="selectByAthleteAndProjects" resultType="GameScore">
+        SELECT *
+        FROM game_score
+        WHERE athlete_id = #{athleteId} AND project_id IN
+        <foreach item="projectId" collection="projectIds" separator="," open="(" close=")">
+            #{projectId}
+        </foreach>
+        AND del_flag = '0'
+    </select>
 </mapper>

+ 7 - 0
ruoyi-modules/ruoyi-game-event/src/main/resources/mapper/system/app/GameUserMapper.xml

@@ -0,0 +1,7 @@
+<?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.app.GameUserMapper">
+
+</mapper>