|
|
@@ -286,12 +286,20 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
if (vo == null) {
|
|
|
throw new RuntimeException("该赛事下不存在此项目");
|
|
|
}
|
|
|
- List<SysDictDataVo> dictDataVos = dictService.selectDictDataByType("game_project_type");
|
|
|
- if (!dictDataVos.isEmpty()){
|
|
|
- dictDataVos.stream()
|
|
|
- .filter(d -> d.getDictValue().equals(vo.getType()))
|
|
|
- .findFirst().map(SysDictDataVo::getDictLabel).ifPresent(vo::setType);
|
|
|
- }
|
|
|
+
|
|
|
+ // 批量查询字典数据,避免多次调用 Redis/MySQL 交互
|
|
|
+ Map<String, List<SysDictDataVo>> dictMap = dictService.selectDictDataByTypes(List.of(
|
|
|
+ "game_project_type", "game_score_type", "game_order_type",
|
|
|
+ "sys_group_sex", "game_stage", "game_round"
|
|
|
+ ));
|
|
|
+
|
|
|
+ vo.setType(getDictLabelFromMap(dictMap, "game_project_type", vo.getType()));
|
|
|
+ vo.setChengjiType(getDictLabelFromMap(dictMap, "game_score_type", vo.getChengjiType()));
|
|
|
+ vo.setPaiMing(getDictLabelFromMap(dictMap, "game_order_type", vo.getPaiMing()));
|
|
|
+ vo.setSex(getDictLabelFromMap(dictMap, "sys_group_sex", vo.getSex()));
|
|
|
+ vo.setJieduan(getDictLabelFromMap(dictMap, "game_stage", vo.getJieduan()));
|
|
|
+ vo.setLunci(getDictLabelFromMap(dictMap, "game_round", vo.getLunci()));
|
|
|
+
|
|
|
// 2. 查询该项目下所有参赛人员和队伍信息 (包括已录入成绩和未录入的)
|
|
|
List<ScoreSheetItemVo> items = baseMapper.selectScoreSheetItems(eventId, projectId);
|
|
|
|
|
|
@@ -300,50 +308,55 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
.eq(GameScoreDetail::getProjectId, projectId)
|
|
|
.eq(GameScoreDetail::getDelFlag, "0"));
|
|
|
|
|
|
- if (!allDetails.isEmpty()) {
|
|
|
- Map<Long, List<ScoreSheetDetailVo>> detailMap;
|
|
|
+ if (CollectionUtils.isNotEmpty(allDetails)) {
|
|
|
+ // 一次性转换所有 detail,避免在循环和分支中重复转换
|
|
|
+ List<ScoreSheetDetailVo> detailVos = allDetails.stream()
|
|
|
+ .map(this::convertToDetailVo)
|
|
|
+ .toList();
|
|
|
+
|
|
|
if (ProjectClassification.TEAM.getValue().equals(vo.getClassification())) {
|
|
|
// 团体项目:按 teamId 分组
|
|
|
- detailMap = allDetails.stream()
|
|
|
+ Map<Long, List<ScoreSheetDetailVo>> teamDetailMap = detailVos.stream()
|
|
|
.filter(d -> d.getTeamId() != null)
|
|
|
- .map(this::convertToDetailVo)
|
|
|
.collect(Collectors.groupingBy(ScoreSheetDetailVo::getTeamId));
|
|
|
|
|
|
- items.forEach(item -> item.setChengji(detailMap.get(item.getTeamId())));
|
|
|
+ items.forEach(item -> item.setChengji(teamDetailMap.get(item.getTeamId())));
|
|
|
} else {
|
|
|
- // 个人项目:优先按 athleteId 分组 (如果存储时没存 athleteId 则按 scoreId)
|
|
|
- detailMap = allDetails.stream()
|
|
|
- .filter(d -> d.getAthleteId() != null)
|
|
|
- .map(this::convertToDetailVo)
|
|
|
+ // 个人项目:优先按 athleteId (即 detailVo.id) 分组,同时生成按 scoreId 的分组以便 O(1) 匹配
|
|
|
+ Map<Long, List<ScoreSheetDetailVo>> athleteDetailMap = detailVos.stream()
|
|
|
+ .filter(d -> d.getId() != null)
|
|
|
.collect(Collectors.groupingBy(ScoreSheetDetailVo::getId));
|
|
|
|
|
|
- items.forEach(item -> item.setChengji(detailMap.get(item.getId())));
|
|
|
+ Map<Long, List<ScoreSheetDetailVo>> scoreDetailMap = detailVos.stream()
|
|
|
+ .filter(d -> d.getScoreId() != null)
|
|
|
+ .collect(Collectors.groupingBy(ScoreSheetDetailVo::getScoreId));
|
|
|
|
|
|
- // 兜底:处理部分仅关联 scoreId 的旧数据
|
|
|
- items.stream().filter(i -> i.getChengji() == null && i.getScoreId() != null).forEach(item -> {
|
|
|
- List<ScoreSheetDetailVo> scoreDetails = allDetails.stream()
|
|
|
- .filter(d -> Objects.equals(d.getScoreId(), item.getScoreId()))
|
|
|
- .map(this::convertToDetailVo)
|
|
|
- .toList();
|
|
|
- item.setChengji(scoreDetails);
|
|
|
+ items.forEach(item -> {
|
|
|
+ List<ScoreSheetDetailVo> chengji = athleteDetailMap.get(item.getId());
|
|
|
+ if (CollectionUtils.isEmpty(chengji) && item.getScoreId() != null) {
|
|
|
+ // 兜底:处理部分仅关联 scoreId 的旧数据,使用预分组的 Map 进行 O(1) 匹配,消除了原来的 O(N*M) 过滤
|
|
|
+ chengji = scoreDetailMap.get(item.getScoreId());
|
|
|
+ }
|
|
|
+ item.setChengji(chengji);
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 4. 处理计时格式转换
|
|
|
- if (vo.getGuize() != null && StringUtils.isNotBlank(vo.getGuize())) {
|
|
|
+ // 4. 处理计时格式转换 (直接传入 String 进行安全解析)
|
|
|
+ if (StringUtils.isNotBlank(vo.getGuize())) {
|
|
|
items.forEach(item -> {
|
|
|
// 格式化主成绩
|
|
|
- if (StringUtils.isNotBlank(item.getScore())) {
|
|
|
- item.setScore(convertDecimalToTimeScore(new BigDecimal(item.getScore()), vo.getGuize()));
|
|
|
+ if (StringUtils.isNotBlank(item.getScore()) && vo.getChengjiType().contains("计时")) {
|
|
|
+ item.setScore(convertDecimalToTimeScore(item.getScore(), vo.getGuize()));
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(item.getYongshi())){
|
|
|
+ item.setYongshi(convertDecimalToTimeScore(item.getYongshi(), vo.getGuize()));
|
|
|
}
|
|
|
// 格式化明细成绩
|
|
|
- if (item.getChengji() != null) {
|
|
|
+ if (CollectionUtils.isNotEmpty(item.getChengji()) && vo.getChengjiType().contains("计时")) {
|
|
|
item.getChengji().forEach(detail -> {
|
|
|
if (StringUtils.isNotBlank(detail.getNum())) {
|
|
|
- detail.setNum(convertDecimalToTimeScore(
|
|
|
- new BigDecimal(detail.getNum()),
|
|
|
- vo.getGuize()));
|
|
|
+ detail.setNum(convertDecimalToTimeScore(detail.getNum(), vo.getGuize()));
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
@@ -354,14 +367,31 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
return vo;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 从预查的字典Map中获取指定类型和值的字典标签,若未匹配则返回原值
|
|
|
+ */
|
|
|
+ private String getDictLabelFromMap(Map<String, List<SysDictDataVo>> dictMap, String dictType, String dictValue) {
|
|
|
+ if (StringUtils.isBlank(dictValue) || dictMap == null) {
|
|
|
+ return dictValue;
|
|
|
+ }
|
|
|
+ List<SysDictDataVo> list = dictMap.get(dictType);
|
|
|
+ if (CollectionUtils.isNotEmpty(list)) {
|
|
|
+ return list.stream()
|
|
|
+ .filter(d -> dictValue.equals(d.getDictValue()))
|
|
|
+ .findFirst()
|
|
|
+ .map(SysDictDataVo::getDictLabel)
|
|
|
+ .orElse(dictValue);
|
|
|
+ }
|
|
|
+ return dictValue;
|
|
|
+ }
|
|
|
+
|
|
|
private ScoreSheetDetailVo convertToDetailVo(GameScoreDetail d) {
|
|
|
ScoreSheetDetailVo dvo = new ScoreSheetDetailVo();
|
|
|
dvo.setDetailId(d.getDetailId());
|
|
|
dvo.setScoreId(d.getScoreId());
|
|
|
dvo.setAttemptIndex(d.getAttemptIndex());
|
|
|
dvo.setNum(d.getPerformanceValue() != null ? d.getPerformanceValue().toString() : null);
|
|
|
- dvo.setShiwuA(d.getFaultA());
|
|
|
- dvo.setShiwuB(d.getFaultB());
|
|
|
+ dvo.setShiwu(d.getFaultA());
|
|
|
// 传递关联 ID 供分组使用
|
|
|
dvo.setId(d.getAthleteId());
|
|
|
dvo.setTeamId(d.getTeamId());
|
|
|
@@ -371,12 +401,24 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
/**
|
|
|
* 将小数格式的成绩转换成时间格式显示
|
|
|
*
|
|
|
- * @param decimalScore 以秒为单位的小数值
|
|
|
- * @param format 格式 (HH:mm:ss.SSS 或 mm:ss.SSS)
|
|
|
+ * @param scoreStr 以秒为单位的小数值字符串
|
|
|
+ * @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) {
|
|
|
+ private String convertDecimalToTimeScore(String scoreStr, String format) {
|
|
|
+ if (StringUtils.isBlank(scoreStr)) {
|
|
|
+ return "0".equals(format) ? "00:00:00.000" : "00:00.000";
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal decimalScore;
|
|
|
+ try {
|
|
|
+ decimalScore = new BigDecimal(scoreStr.trim());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("成绩数字解析失败: scoreStr={}", scoreStr, e);
|
|
|
+ return scoreStr; // 解析失败安全降级,返回原字符串而非崩溃
|
|
|
+ }
|
|
|
+
|
|
|
+ if (decimalScore.compareTo(BigDecimal.ZERO) < 0) {
|
|
|
return "0".equals(format) ? "00:00:00.000" : "00:00.000";
|
|
|
}
|
|
|
|
|
|
@@ -397,7 +439,7 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
log.warn("转换成绩格式失败: score={}, format={}", decimalScore, format, e);
|
|
|
- return decimalScore.toString();
|
|
|
+ return scoreStr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -418,6 +460,24 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 校验成绩是否为空
|
|
|
+ for (ScoreSubmitItemBo item : items) {
|
|
|
+ String displayName = StringUtils.isNotBlank(item.getName()) ? item.getName() :
|
|
|
+ (StringUtils.isNotBlank(item.getNum()) ? item.getNum() : "未知选手");
|
|
|
+ if (CollectionUtils.isNotEmpty(item.getChengji())) {
|
|
|
+ for (ScoreSubmitDetailBo detail : item.getChengji()) {
|
|
|
+ if (detail.getNum() == null) {
|
|
|
+ String attemptStr = detail.getAttemptIndex() != null ? "第 " + detail.getAttemptIndex() + " 轮" : "明细";
|
|
|
+ throw new RuntimeException("提交失败:" + displayName + " 的" + attemptStr + "成绩不能为空");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (StringUtils.isBlank(item.getScore())) {
|
|
|
+ throw new RuntimeException("提交失败:" + displayName + " 的成绩不能为空");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 1. 批量处理队伍 (缺失则新增)
|
|
|
Map<String, Long> teamIdMap = new HashMap<>();
|
|
|
Set<String> missingTeamNames = items.stream()
|
|
|
@@ -515,8 +575,8 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
scoreBo.setProjectId(projectId);
|
|
|
scoreBo.setAthleteId(athleteId);
|
|
|
scoreBo.setTeamId(teamId);
|
|
|
- scoreBo.setFaultA(item.getFaultA());
|
|
|
- scoreBo.setFaultB(item.getFaultB());
|
|
|
+ scoreBo.setFaultA(item.getShiwu());
|
|
|
+ scoreBo.setCompleteTime(item.getYongshi());
|
|
|
|
|
|
// 统一模式:优先处理明细列表
|
|
|
if (CollectionUtils.isNotEmpty(item.getChengji())) {
|
|
|
@@ -525,8 +585,7 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
dbo.setDetailId(d.getDetailId());
|
|
|
dbo.setAttemptIndex(d.getAttemptIndex());
|
|
|
dbo.setPerformanceValue(d.getNum());
|
|
|
- dbo.setFaultA(d.getShiwuA());
|
|
|
- dbo.setFaultB(d.getShiwuB());
|
|
|
+ dbo.setFaultA(d.getShiwu());
|
|
|
return dbo;
|
|
|
}).toList();
|
|
|
scoreBo.setDetails(details);
|
|
|
@@ -608,13 +667,13 @@ public class ToClientServiceImpl implements IToClientService {
|
|
|
list.forEach(item -> {
|
|
|
if (StringUtils.isNotBlank(item.getScore())) {
|
|
|
item.setScore(
|
|
|
- convertDecimalToTimeScore(new BigDecimal(item.getScore()), project.getTimingFormat()));
|
|
|
+ convertDecimalToTimeScore(item.getScore(), project.getTimingFormat()));
|
|
|
}
|
|
|
if (item.getChengji() != null) {
|
|
|
item.getChengji().forEach(detail -> {
|
|
|
if (StringUtils.isNotBlank(detail.getNum())) {
|
|
|
detail.setNum(convertDecimalToTimeScore(
|
|
|
- new BigDecimal(detail.getNum()),
|
|
|
+ detail.getNum(),
|
|
|
project.getTimingFormat()));
|
|
|
}
|
|
|
});
|