|
|
@@ -7,20 +7,25 @@ import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.dromara.common.core.utils.StringUtils;
|
|
|
import org.dromara.system.domain.*;
|
|
|
-import org.dromara.system.domain.bo.ClientProjectSaveBo;
|
|
|
-import org.dromara.system.domain.bo.GameRankGroupBo;
|
|
|
+import org.dromara.system.domain.bo.*;
|
|
|
+import org.dromara.system.domain.constant.ProjectClassification;
|
|
|
+import org.dromara.system.domain.vo.ScoreSheetDetailVo;
|
|
|
+import org.dromara.system.domain.vo.ScoreSheetItemVo;
|
|
|
+import org.dromara.system.domain.vo.ScoreSheetVo;
|
|
|
+import org.dromara.system.mapper.GameAthleteMapper;
|
|
|
import org.dromara.system.mapper.GameEventMapper;
|
|
|
import org.dromara.system.mapper.GameEventProjectMapper;
|
|
|
+import org.dromara.system.mapper.app.ToClientMapper;
|
|
|
+import org.dromara.system.service.IGameScoreService;
|
|
|
import org.dromara.system.service.IGameRankGroupService;
|
|
|
import org.dromara.system.service.app.IToClientService;
|
|
|
import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.List;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Map;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@Slf4j
|
|
|
@@ -31,6 +36,9 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
private final GameEventProjectMapper projectMapper;
|
|
|
private final GameEventMapper gameEventMapper;
|
|
|
private final IGameRankGroupService gameRankGroupService;
|
|
|
+ private final ToClientMapper baseMapper;
|
|
|
+ private final IGameScoreService gameScoreService;
|
|
|
+ private final GameAthleteMapper gameAthleteMapper;
|
|
|
|
|
|
/**
|
|
|
* 客户端同步赛事及项目信息 (接口4)
|
|
|
@@ -106,6 +114,10 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
project.setProjectType(bo.getProjectType());
|
|
|
project.setClassification(bo.getClassification());
|
|
|
project.setScoreRule(bo.getScoreRule());
|
|
|
+ project.setTimingFormat(bo.getTimingFormat());
|
|
|
+ project.setDistanceMode(bo.getDistanceMode());
|
|
|
+ project.setCountUnit(bo.getCountUnit());
|
|
|
+ project.setScoreCount(bo.getScoreCount());
|
|
|
project.setGender(bo.getGender());
|
|
|
project.setRgName(rgName);
|
|
|
project.setRgId(rgId);
|
|
|
@@ -140,28 +152,25 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
public void asyncRemoveEventData(Long eventId) {
|
|
|
log.info("开始异步删除赛事数据,eventId: {}", eventId);
|
|
|
long startTime = System.currentTimeMillis();
|
|
|
- try{
|
|
|
+ try {
|
|
|
// 删除关联的组别
|
|
|
Db.remove(Wrappers.lambdaQuery(GameRankGroup.class)
|
|
|
- .eq(GameRankGroup::getEventId, eventId)
|
|
|
- );
|
|
|
+ .eq(GameRankGroup::getEventId, eventId));
|
|
|
// 删除关联的配置信息
|
|
|
Db.remove(Wrappers.lambdaQuery(GameEventConfig.class)
|
|
|
- .eq(GameEventConfig::getEventId, eventId)
|
|
|
- );
|
|
|
+ .eq(GameEventConfig::getEventId, eventId));
|
|
|
// 删除关联的成绩信息
|
|
|
Db.remove(Wrappers.lambdaQuery(GameScore.class)
|
|
|
- .eq(GameScore::getEventId, eventId)
|
|
|
- );
|
|
|
+ .eq(GameScore::getEventId, eventId));
|
|
|
// 删除关联的项目
|
|
|
int projectCount = projectMapper.delete(Wrappers.lambdaQuery(GameEventProject.class)
|
|
|
- .eq(GameEventProject::getEventId, eventId));
|
|
|
+ .eq(GameEventProject::getEventId, eventId));
|
|
|
log.info("删除项目完成,eventId: {}, 删除数量: {}", eventId, projectCount);
|
|
|
// 删除赛事
|
|
|
gameEventMapper.deleteById(eventId);
|
|
|
long endTime = System.currentTimeMillis();
|
|
|
log.info("赛事数据删除完成,eventId: {}, 耗时: {}ms", eventId, (endTime - startTime));
|
|
|
- }catch (Exception e){
|
|
|
+ } catch (Exception e) {
|
|
|
log.error("异步删除赛事数据失败,eventId: {}", eventId, e);
|
|
|
throw new RuntimeException("删除赛事数据失败: " + e.getMessage());
|
|
|
}
|
|
|
@@ -170,17 +179,269 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
@Override
|
|
|
public List<GameEvent> getEventList() {
|
|
|
return gameEventMapper.selectList(Wrappers.lambdaQuery(GameEvent.class)
|
|
|
- .orderByDesc(GameEvent::getCreateTime)
|
|
|
- .select(GameEvent::getEventId, GameEvent::getEventCode, GameEvent::getEventName, GameEvent::getCreateTime)
|
|
|
- );
|
|
|
+ .orderByDesc(GameEvent::getCreateTime)
|
|
|
+ .select(GameEvent::getEventId, GameEvent::getEventCode, GameEvent::getEventName,
|
|
|
+ GameEvent::getCreateTime));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<GameEventProject> getProjectList(Long eventId) {
|
|
|
return projectMapper.selectList(Wrappers.lambdaQuery(GameEventProject.class)
|
|
|
- .eq(GameEventProject::getEventId, eventId)
|
|
|
- .select(GameEventProject::getProjectId, GameEventProject::getProjectType, GameEventProject::getClassification, GameEventProject::getProjectName)
|
|
|
- );
|
|
|
+ .eq(GameEventProject::getEventId, eventId)
|
|
|
+ .select(GameEventProject::getProjectId, GameEventProject::getProjectType,
|
|
|
+ GameEventProject::getClassification, GameEventProject::getProjectName));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public ScoreSheetVo getScoreSheet(Long eventId, Long projectId) {
|
|
|
+ // 1. 查询元数据和统计信息
|
|
|
+ ScoreSheetVo vo = baseMapper.selectScoreSheetMetadata(eventId, projectId);
|
|
|
+ if (vo == null) {
|
|
|
+ throw new RuntimeException("该赛事下不存在此项目");
|
|
|
+ }
|
|
|
+ // 2. 查询该项目下所有参赛人员和队伍信息 (包括已录入成绩和未录入的)
|
|
|
+ List<ScoreSheetItemVo> items = baseMapper.selectScoreSheetItems(eventId, projectId);
|
|
|
+
|
|
|
+ // 3. 批量查询该项目下所有成绩明细 (适配个人/团体关联差异)
|
|
|
+ List<GameScoreDetail> allDetails = Db.list(Wrappers.lambdaQuery(GameScoreDetail.class)
|
|
|
+ .eq(GameScoreDetail::getProjectId, projectId)
|
|
|
+ .eq(GameScoreDetail::getDelFlag, "0"));
|
|
|
+
|
|
|
+ if (!allDetails.isEmpty()) {
|
|
|
+ Map<Long, List<ScoreSheetDetailVo>> detailMap;
|
|
|
+ if (ProjectClassification.TEAM.getValue().equals(vo.getClassification())) {
|
|
|
+ // 团体项目:按 teamId 分组
|
|
|
+ detailMap = allDetails.stream()
|
|
|
+ .filter(d -> d.getTeamId() != null)
|
|
|
+ .map(this::convertToDetailVo)
|
|
|
+ .collect(Collectors.groupingBy(ScoreSheetDetailVo::getTeamId));
|
|
|
+
|
|
|
+ items.forEach(item -> item.setDetails(detailMap.get(item.getTeamId())));
|
|
|
+ } else {
|
|
|
+ // 个人项目:优先按 athleteId 分组 (如果存储时没存 athleteId 则按 scoreId)
|
|
|
+ detailMap = allDetails.stream()
|
|
|
+ .filter(d -> d.getAthleteId() != null)
|
|
|
+ .map(this::convertToDetailVo)
|
|
|
+ .collect(Collectors.groupingBy(ScoreSheetDetailVo::getAthleteId));
|
|
|
+
|
|
|
+ items.forEach(item -> item.setDetails(detailMap.get(item.getAthleteId())));
|
|
|
+
|
|
|
+ // 兜底:处理部分仅关联 scoreId 的旧数据
|
|
|
+ items.stream().filter(i -> i.getDetails() == null && i.getScoreId() != null).forEach(item -> {
|
|
|
+ List<ScoreSheetDetailVo> scoreDetails = allDetails.stream()
|
|
|
+ .filter(d -> Objects.equals(d.getScoreId(), item.getScoreId()))
|
|
|
+ .map(this::convertToDetailVo)
|
|
|
+ .toList();
|
|
|
+ item.setDetails(scoreDetails);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 处理计时格式转换
|
|
|
+ if (vo.getTimingFormat() != null && StringUtils.isNotBlank(vo.getTimingFormat())) {
|
|
|
+ items.forEach(item -> {
|
|
|
+ // 格式化主成绩
|
|
|
+ if (StringUtils.isNotBlank(item.getScore())) {
|
|
|
+ item.setScore(convertDecimalToTimeScore(new BigDecimal(item.getScore()), vo.getTimingFormat()));
|
|
|
+ }
|
|
|
+ // 格式化明细成绩
|
|
|
+ if (item.getDetails() != null) {
|
|
|
+ item.getDetails().forEach(detail -> {
|
|
|
+ if (StringUtils.isNotBlank(detail.getPerformanceValue())) {
|
|
|
+ detail.setPerformanceValue(convertDecimalToTimeScore(
|
|
|
+ new BigDecimal(detail.getPerformanceValue()),
|
|
|
+ vo.getTimingFormat()));
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setList(items);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private ScoreSheetDetailVo convertToDetailVo(GameScoreDetail d) {
|
|
|
+ ScoreSheetDetailVo dvo = new ScoreSheetDetailVo();
|
|
|
+ dvo.setDetailId(d.getDetailId());
|
|
|
+ dvo.setScoreId(d.getScoreId());
|
|
|
+ dvo.setAttemptIndex(d.getAttemptIndex());
|
|
|
+ dvo.setPerformanceValue(d.getPerformanceValue() != null ? d.getPerformanceValue().toString() : null);
|
|
|
+ dvo.setFaultA(d.getFaultA());
|
|
|
+ dvo.setFaultB(d.getFaultB());
|
|
|
+ // 传递关联 ID 供分组使用
|
|
|
+ dvo.setAthleteId(d.getAthleteId());
|
|
|
+ dvo.setTeamId(d.getTeamId());
|
|
|
+ return dvo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将小数格式的成绩转换成时间格式显示
|
|
|
+ *
|
|
|
+ * @param decimalScore 以秒为单位的小数值
|
|
|
+ * @param format 格式 (HH:mm:ss.SSS 或 mm:ss.SSS)
|
|
|
+ * @return 时间格式字符串
|
|
|
+ */
|
|
|
+ private String convertDecimalToTimeScore(BigDecimal decimalScore, String format) {
|
|
|
+ if (decimalScore == null || decimalScore.compareTo(BigDecimal.ZERO) < 0) {
|
|
|
+ return "0".equals(format) ? "00:00:00.000" : "00:00.000";
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ long totalMs = decimalScore.multiply(new BigDecimal(1000)).setScale(0, RoundingMode.HALF_UP).longValue();
|
|
|
+ long hours = totalMs / 3600000;
|
|
|
+ long minutes = (totalMs % 3600000) / 60000;
|
|
|
+ long seconds = (totalMs % 60000) / 1000;
|
|
|
+ long milliseconds = totalMs % 1000;
|
|
|
+
|
|
|
+ if ("1".equals(format)) {
|
|
|
+ // 如果是 mm:ss.SSS,把小时累加到分钟
|
|
|
+ long totalMinutes = hours * 60 + minutes;
|
|
|
+ return String.format("%02d:%02d.%03d", totalMinutes, seconds, milliseconds);
|
|
|
+ } else {
|
|
|
+ // 默认 HH:mm:ss.SSS
|
|
|
+ return String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("转换成绩格式失败: score={}, format={}", decimalScore, format, e);
|
|
|
+ return decimalScore.toString();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public void submitScoreSheet(ScoreSubmitBo bo) {
|
|
|
+ Long projectId = bo.getProjectId();
|
|
|
+ Long eventId = bo.getEventId();
|
|
|
+ GameEventProject project = projectMapper.selectOne(Wrappers.lambdaQuery(GameEventProject.class)
|
|
|
+ .eq(GameEventProject::getEventId, eventId)
|
|
|
+ .eq(GameEventProject::getProjectId, projectId));
|
|
|
+ if (project == null) {
|
|
|
+ throw new RuntimeException("该赛事下不存在此项目");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<ScoreSubmitItemBo> items = bo.getItems();
|
|
|
+ if (CollectionUtils.isEmpty(items)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 批量处理队伍 (缺失则新增)
|
|
|
+ Map<String, Long> teamIdMap = new HashMap<>();
|
|
|
+ Set<String> missingTeamNames = items.stream()
|
|
|
+ .filter(item -> item.getTeamId() == null && StringUtils.isNotBlank(item.getTeamName()))
|
|
|
+ .map(ScoreSubmitItemBo::getTeamName)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ if (!missingTeamNames.isEmpty()) {
|
|
|
+ List<GameTeam> existingTeams = Db.list(Wrappers.lambdaQuery(GameTeam.class)
|
|
|
+ .eq(GameTeam::getEventId, eventId)
|
|
|
+ .in(GameTeam::getTeamName, missingTeamNames)
|
|
|
+ .select(GameTeam::getTeamId, GameTeam::getTeamName));
|
|
|
+ existingTeams.forEach(t -> teamIdMap.put(t.getTeamName(), t.getTeamId()));
|
|
|
+
|
|
|
+ List<GameTeam> newTeams = missingTeamNames.stream()
|
|
|
+ .filter(name -> !teamIdMap.containsKey(name))
|
|
|
+ .map(name -> {
|
|
|
+ GameTeam t = new GameTeam();
|
|
|
+ t.setEventId(eventId);
|
|
|
+ t.setTeamName(name);
|
|
|
+ return t;
|
|
|
+ }).toList();
|
|
|
+ if (!newTeams.isEmpty()) {
|
|
|
+ Db.saveBatch(newTeams);
|
|
|
+ newTeams.forEach(t -> teamIdMap.put(t.getTeamName(), t.getTeamId()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 批量处理运动员 (缺失则新增,其余准备批量更新)
|
|
|
+ Map<String, Long> athleteIdMap = new HashMap<>();
|
|
|
+ List<ScoreSubmitItemBo> missingAthleteItems = items.stream()
|
|
|
+ .filter(item -> item.getAthleteId() == null && StringUtils.isNotBlank(item.getAthleteCode()))
|
|
|
+ .toList();
|
|
|
+
|
|
|
+ if (!missingAthleteItems.isEmpty()) {
|
|
|
+ // 预查是否存在,防止重复
|
|
|
+ Set<String> codes = missingAthleteItems.stream().map(ScoreSubmitItemBo::getAthleteCode).collect(Collectors.toSet());
|
|
|
+ List<GameAthlete> existAthletes = gameAthleteMapper.selectList(Wrappers.lambdaQuery(GameAthlete.class)
|
|
|
+ .eq(GameAthlete::getEventId, eventId)
|
|
|
+ .in(GameAthlete::getAthleteCode, codes)
|
|
|
+ .select(GameAthlete::getAthleteId, GameAthlete::getAthleteCode));
|
|
|
+ existAthletes.forEach(a -> athleteIdMap.put(a.getAthleteCode(), a.getAthleteId()));
|
|
|
+
|
|
|
+ List<GameAthlete> newAthletes = missingAthleteItems.stream()
|
|
|
+ .filter(item -> !athleteIdMap.containsKey(item.getAthleteCode()))
|
|
|
+ .map(item -> {
|
|
|
+ GameAthlete a = new GameAthlete();
|
|
|
+ a.setEventId(eventId);
|
|
|
+ a.setAthleteCode(item.getAthleteCode());
|
|
|
+ a.setName(item.getName());
|
|
|
+ a.setTeamId(item.getTeamId() != null ? item.getTeamId() : teamIdMap.get(item.getTeamName()));
|
|
|
+ a.setProjectValue("[\"" + projectId + "\"]");
|
|
|
+ a.setTrackIndex(item.getTrackIndex());
|
|
|
+ return a;
|
|
|
+ }).collect(Collectors.toMap(GameAthlete::getAthleteCode, a -> a, (a1, a2) -> a1))
|
|
|
+ .values().stream().toList();
|
|
|
+
|
|
|
+ if (!newAthletes.isEmpty()) {
|
|
|
+ Db.saveBatch(newAthletes);
|
|
|
+ newAthletes.forEach(a -> athleteIdMap.put(a.getAthleteCode(), a.getAthleteId()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 直接批量更新所有上传项的运动员信息 (不再预查对比,直接全量同步)
|
|
|
+ List<GameAthlete> updateBatch = items.stream()
|
|
|
+ .map(item -> {
|
|
|
+ Long aid = item.getAthleteId() != null ? item.getAthleteId() : athleteIdMap.get(item.getAthleteCode());
|
|
|
+ if (aid == null) return null;
|
|
|
+ GameAthlete a = new GameAthlete();
|
|
|
+ a.setAthleteId(aid);
|
|
|
+ a.setTrackIndex(item.getTrackIndex());
|
|
|
+ // 如果有姓名或其它字段变更也可以在此同步
|
|
|
+ return a;
|
|
|
+ }).filter(Objects::nonNull).toList();
|
|
|
+
|
|
|
+ if (!updateBatch.isEmpty()) {
|
|
|
+ Db.updateBatchById(updateBatch);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 循环处理成绩录入 (统一使用 details 明细列表)
|
|
|
+ for (ScoreSubmitItemBo item : items) {
|
|
|
+ Long athleteId = item.getAthleteId() != null ? item.getAthleteId() : athleteIdMap.get(item.getAthleteCode());
|
|
|
+ if (athleteId == null) continue;
|
|
|
+
|
|
|
+ Long teamId = item.getTeamId() != null ? item.getTeamId() : teamIdMap.get(item.getTeamName());
|
|
|
+
|
|
|
+ GameScoreBo scoreBo = new GameScoreBo();
|
|
|
+ scoreBo.setScoreId(item.getScoreId());
|
|
|
+ scoreBo.setEventId(eventId);
|
|
|
+ scoreBo.setProjectId(projectId);
|
|
|
+ scoreBo.setAthleteId(athleteId);
|
|
|
+ scoreBo.setTeamId(teamId);
|
|
|
+ scoreBo.setFaultA(item.getFaultA());
|
|
|
+ scoreBo.setFaultB(item.getFaultB());
|
|
|
+
|
|
|
+ // 统一模式:优先处理明细列表
|
|
|
+ if (CollectionUtils.isNotEmpty(item.getDetails())) {
|
|
|
+ List<GameScoreDetailBo> details = item.getDetails().stream().map(d -> {
|
|
|
+ GameScoreDetailBo dbo = new GameScoreDetailBo();
|
|
|
+ dbo.setDetailId(d.getDetailId());
|
|
|
+ dbo.setAttemptIndex(d.getAttemptIndex());
|
|
|
+ dbo.setPerformanceValue(d.getPerformanceValue());
|
|
|
+ return dbo;
|
|
|
+ }).toList();
|
|
|
+ scoreBo.setDetails(details);
|
|
|
+ } else if (StringUtils.isNotBlank(item.getScore())) {
|
|
|
+ // 如果没有明细,说明只有一个成绩,解析赋值给对应字段
|
|
|
+ BigDecimal performance = parsePerformanceValue(item.getScore());
|
|
|
+ if (ProjectClassification.TEAM.getValue().equals(project.getClassification())) {
|
|
|
+ scoreBo.setTeamPerformance(performance);
|
|
|
+ } else {
|
|
|
+ scoreBo.setIndividualPerformance(performance);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ gameScoreService.updateScoreAndRecalculate(scoreBo);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -188,25 +449,28 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
*/
|
|
|
private void batchSaveConfigs(Long eventId, ClientProjectSaveBo bo) {
|
|
|
Map<String, String> configValues = new HashMap<>();
|
|
|
- if (StringUtils.isNotBlank(bo.getMachineCode())) configValues.put("machine_code", bo.getMachineCode());
|
|
|
- if (StringUtils.isNotBlank(bo.getEventTip())) configValues.put("event_tip", bo.getEventTip());
|
|
|
- if (StringUtils.isNotBlank(bo.getUploadPath())) configValues.put("upload_path", bo.getUploadPath());
|
|
|
+ if (StringUtils.isNotBlank(bo.getMachineCode()))
|
|
|
+ configValues.put("machine_code", bo.getMachineCode());
|
|
|
+ if (StringUtils.isNotBlank(bo.getEventTip()))
|
|
|
+ configValues.put("event_tip", bo.getEventTip());
|
|
|
+ if (StringUtils.isNotBlank(bo.getUploadPath()))
|
|
|
+ configValues.put("upload_path", bo.getUploadPath());
|
|
|
|
|
|
- if (configValues.isEmpty()) return;
|
|
|
+ if (configValues.isEmpty())
|
|
|
+ return;
|
|
|
|
|
|
Map<String, String> configDescs = Map.of(
|
|
|
- "machine_code", "机器编号",
|
|
|
- "event_tip", "赛事提示",
|
|
|
- "upload_path", "上传成绩路径"
|
|
|
- );
|
|
|
+ "machine_code", "机器编号",
|
|
|
+ "event_tip", "赛事提示",
|
|
|
+ "upload_path", "上传成绩路径");
|
|
|
|
|
|
// 批量查询现有配置
|
|
|
List<GameEventConfig> existingConfigs = Db.list(Wrappers.lambdaQuery(GameEventConfig.class)
|
|
|
- .eq(GameEventConfig::getEventId, eventId)
|
|
|
- .in(GameEventConfig::getConfigKey, configValues.keySet()));
|
|
|
+ .eq(GameEventConfig::getEventId, eventId)
|
|
|
+ .in(GameEventConfig::getConfigKey, configValues.keySet()));
|
|
|
|
|
|
Map<String, GameEventConfig> configMap = existingConfigs.stream()
|
|
|
- .collect(Collectors.toMap(GameEventConfig::getConfigKey, c -> c));
|
|
|
+ .collect(Collectors.toMap(GameEventConfig::getConfigKey, c -> c));
|
|
|
|
|
|
List<GameEventConfig> toSave = new ArrayList<>();
|
|
|
for (Map.Entry<String, String> entry : configValues.entrySet()) {
|
|
|
@@ -227,4 +491,41 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
}
|
|
|
Db.saveOrUpdateBatch(toSave);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析成绩字符串为 BigDecimal
|
|
|
+ * 支持普通数字和时间格式 (HH:mm:ss.SSS 或 mm:ss.SSS)
|
|
|
+ */
|
|
|
+ private BigDecimal parsePerformanceValue(String scoreStr) {
|
|
|
+ if (StringUtils.isBlank(scoreStr)) return null;
|
|
|
+ scoreStr = scoreStr.trim();
|
|
|
+ // 兼容时间格式
|
|
|
+ if (scoreStr.contains(":")) {
|
|
|
+ try {
|
|
|
+ String[] parts = scoreStr.split(":");
|
|
|
+ double totalSeconds = 0;
|
|
|
+ if (parts.length == 3) {
|
|
|
+ // HH:mm:ss.SSS
|
|
|
+ totalSeconds = Double.parseDouble(parts[0]) * 3600
|
|
|
+ + Double.parseDouble(parts[1]) * 60
|
|
|
+ + Double.parseDouble(parts[2]);
|
|
|
+ } else if (parts.length == 2) {
|
|
|
+ // mm:ss.SSS
|
|
|
+ totalSeconds = Double.parseDouble(parts[0]) * 60
|
|
|
+ + Double.parseDouble(parts[1]);
|
|
|
+ }
|
|
|
+ return BigDecimal.valueOf(totalSeconds).setScale(3, RoundingMode.HALF_UP);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("成绩时间格式解析失败: {}", scoreStr);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return new BigDecimal(scoreStr);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("成绩数字解析失败: {}", scoreStr);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+
|