|
|
@@ -1,6 +1,16 @@
|
|
|
package org.dromara.system.service.impl;
|
|
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.idev.excel.FastExcel;
|
|
|
+import cn.idev.excel.metadata.Head;
|
|
|
+import cn.idev.excel.metadata.data.WriteCellData;
|
|
|
+import cn.idev.excel.write.handler.AbstractCellWriteHandler;
|
|
|
+import cn.idev.excel.write.handler.SheetWriteHandler;
|
|
|
+import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
|
|
|
+import cn.idev.excel.write.metadata.holder.WriteTableHolder;
|
|
|
+import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;
|
|
|
+import cn.idev.excel.write.style.column.SimpleColumnWidthStyleStrategy;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
import org.dromara.common.core.utils.MapstructUtils;
|
|
|
import org.dromara.common.core.utils.StringUtils;
|
|
|
@@ -44,6 +54,8 @@ import java.net.URLEncoder;
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.dromara.common.excel.utils.ExcelUtil;
|
|
|
+import java.io.IOException;
|
|
|
|
|
|
/**
|
|
|
* 成绩Service业务层处理
|
|
|
@@ -320,9 +332,9 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
@Override
|
|
|
public Map<String, Object> getProjectScoreData(Long eventId, Long projectId, String classification,
|
|
|
String searchValue, PageQuery pageQuery) {
|
|
|
- log.info(
|
|
|
- "开始获取项目成绩数据 (真分页): eventId={}, projectId={}, classification={}, searchValue={}, pageNum={}, pageSize={}",
|
|
|
- eventId, projectId, classification, searchValue, pageQuery.getPageNum(), pageQuery.getPageSize());
|
|
|
+// log.info(
|
|
|
+// "开始获取项目成绩数据 (真分页): eventId={}, projectId={}, classification={}, searchValue={}, pageNum={}, pageSize={}",
|
|
|
+// eventId, projectId, classification, searchValue, pageQuery.getPageNum(), pageQuery.getPageSize());
|
|
|
|
|
|
Map<String, Object> result = new HashMap<>();
|
|
|
|
|
|
@@ -332,7 +344,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
long registrationCount = gameAthleteService.selectAthleteCountByProjectId(eventId, projectId);
|
|
|
stats.put("registrationCount", registrationCount);
|
|
|
|
|
|
- if ("0".equals(classification)) {
|
|
|
+ if (ProjectClassification.SINGLE.getValue().equals(classification)) {
|
|
|
// 个人项目统计 (按人)
|
|
|
stats.put("participantCount", registrationCount);
|
|
|
// 完赛人数 (有成绩记录的运动员数量)
|
|
|
@@ -372,7 +384,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
|
|
|
// 2. 获取分页数据 (真分页)
|
|
|
TableDataInfo<Map<String, Object>> tableDataInfo;
|
|
|
- if ("0".equals(classification)) {
|
|
|
+ if (ProjectClassification.SINGLE.getValue().equals(classification)) {
|
|
|
// 个人项目分页
|
|
|
tableDataInfo = getIndividualProjectDataPaged(eventId, projectId, searchValue, pageQuery);
|
|
|
} else {
|
|
|
@@ -560,42 +572,47 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
|
|
|
// 1. 获取项目配置信息
|
|
|
GameEventProjectVo project = gameEventProjectService.queryById(bo.getProjectId());
|
|
|
- if (project == null) return false;
|
|
|
+ if (project == null)
|
|
|
+ return false;
|
|
|
|
|
|
- // 自动分析汇总成绩和失误
|
|
|
- if (bo.getDetails() != null && !bo.getDetails().isEmpty()) {
|
|
|
- calculateAndSetAggregatePerformance(bo, project);
|
|
|
- }
|
|
|
+ // 判断是否是“纯排名计算”请求(不带具体的录入对象)
|
|
|
+ boolean isOnlyRecalculate = bo.getAthleteId() == null && bo.getTeamId() == null
|
|
|
+ && (bo.getScoreId() == null || bo.getScoreId() == 0);
|
|
|
|
|
|
- Boolean result = false;
|
|
|
+ Boolean result = true;
|
|
|
|
|
|
- if (ProjectClassification.TEAM.getValue().equals(project.getClassification())) {
|
|
|
- // 团体项目:为队伍中的所有运动员创建或更新成绩记录
|
|
|
- // log.info("处理团体项目成绩更新");
|
|
|
- result = handleTeamScoreUpdate(bo);
|
|
|
- } else {
|
|
|
- // 个人项目:直接更新或插入
|
|
|
- // log.info("处理个人项目成绩更新,scoreId: {}", bo.getScoreId());
|
|
|
- if (bo.getScoreId() != null && bo.getScoreId() > 0) {
|
|
|
- result = updateByBo(bo);
|
|
|
- // log.info("更新现有成绩记录,结果: {}", result);
|
|
|
+ if (!isOnlyRecalculate) {
|
|
|
+ // 自动分析汇总成绩和失误
|
|
|
+ if (bo.getDetails() != null && !bo.getDetails().isEmpty()) {
|
|
|
+ calculateAndSetAggregatePerformance(bo, project);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ProjectClassification.TEAM.getValue().equals(project.getClassification())) {
|
|
|
+ // 团体项目:为队伍中的所有运动员创建或更新成绩记录
|
|
|
+ result = handleTeamScoreUpdate(bo);
|
|
|
} else {
|
|
|
- result = insertByBo(bo);
|
|
|
- // log.info("插入新成绩记录,结果: {}", result);
|
|
|
+ // 个人项目:直接更新或插入
|
|
|
+ if (bo.getScoreId() != null && bo.getScoreId() > 0) {
|
|
|
+ result = updateByBo(bo);
|
|
|
+ } else {
|
|
|
+ result = insertByBo(bo);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (result) {
|
|
|
- // 个人项目:如果传了明细,则保存 (团体项目已在 handleTeamScoreUpdate 中处理过)
|
|
|
- if ("0".equals(project.getClassification()) && bo.getDetails() != null && !bo.getDetails().isEmpty()) {
|
|
|
- saveScoreDetails(bo.getScoreId(), bo.getProjectId(), bo.getAthleteId(), null, bo.getDetails());
|
|
|
+ if (result) {
|
|
|
+ // 个人项目:如果传了明细,则保存 (团体项目已在 handleTeamScoreUpdate 中处理过)
|
|
|
+ if (ProjectClassification.SINGLE.getValue().equals(project.getClassification())
|
|
|
+ && bo.getDetails() != null && !bo.getDetails().isEmpty()) {
|
|
|
+ saveScoreDetails(bo.getScoreId(), bo.getProjectId(), bo.getAthleteId(), null, bo.getDetails());
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
+ if (result) {
|
|
|
// 重新计算排名和积分
|
|
|
recalculateRankingsAndPoints(bo.getEventId(), bo.getProjectId());
|
|
|
}
|
|
|
|
|
|
- // log.info("成绩更新处理完成,结果: {}", result);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
@@ -618,7 +635,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
.filter(Objects::nonNull)
|
|
|
.toList();
|
|
|
|
|
|
- if (values.isEmpty()) return;
|
|
|
+ if (values.isEmpty())
|
|
|
+ return;
|
|
|
|
|
|
BigDecimal aggregate = BigDecimal.ZERO;
|
|
|
|
|
|
@@ -694,10 +712,19 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ // 提前查询已存在的成绩记录,获取 score_id 以便执行更新而非冲突插入
|
|
|
+ Map<Long, Long> athleteScoreIdMap = baseMapper.selectList(Wrappers.<GameScore>lambdaQuery()
|
|
|
+ .select(GameScore::getScoreId, GameScore::getAthleteId)
|
|
|
+ .eq(GameScore::getProjectId, bo.getProjectId())
|
|
|
+ .in(GameScore::getAthleteId, atheleteIds))
|
|
|
+ .stream()
|
|
|
+ .collect(Collectors.toMap(GameScore::getAthleteId, GameScore::getScoreId, (v1, v2) -> v1));
|
|
|
+
|
|
|
List<GameScore> scoreList = new ArrayList<>();
|
|
|
// 为每个运动员创建或更新成绩记录
|
|
|
for (Long athleteId : atheleteIds) {
|
|
|
GameScore athleteScore = new GameScore();
|
|
|
+ athleteScore.setScoreId(athleteScoreIdMap.get(athleteId)); // 设置已存在的 ID
|
|
|
athleteScore.setEventId(bo.getEventId());
|
|
|
athleteScore.setProjectId(bo.getProjectId());
|
|
|
athleteScore.setAthleteId(athleteId);
|
|
|
@@ -774,14 +801,15 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
List<GameScore> updateList = new ArrayList<>();
|
|
|
|
|
|
// 3. 根据项目类型执行不同的排名逻辑
|
|
|
- if ("0".equals(project.getClassification())) {
|
|
|
+ if (ProjectClassification.SINGLE.getValue().equals(project.getClassification())) {
|
|
|
// --- 个人项目排名 ---
|
|
|
- allScores.sort((a, b) -> compareScores(a, b, orderType, "0"));
|
|
|
+ allScores.sort((a, b) -> compareScores(a, b, orderType, ProjectClassification.SINGLE.getValue()));
|
|
|
|
|
|
int currentRank = 1;
|
|
|
for (int i = 0; i < allScores.size(); i++) {
|
|
|
GameScoreVo current = allScores.get(i);
|
|
|
- if (i > 0 && compareScores(current, allScores.get(i - 1), orderType, "0") != 0) {
|
|
|
+ if (i > 0 && compareScores(current, allScores.get(i - 1), orderType,
|
|
|
+ ProjectClassification.SINGLE.getValue()) != 0) {
|
|
|
currentRank = i + 1;
|
|
|
}
|
|
|
int points = (currentRank <= pointConfig.size()) ? pointConfig.get(currentRank - 1) : 0;
|
|
|
@@ -800,7 +828,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
sortedTeamIds.sort((id1, id2) -> {
|
|
|
GameScoreVo score1 = teamGroups.get(id1).get(0);
|
|
|
GameScoreVo score2 = teamGroups.get(id2).get(0);
|
|
|
- return compareScores(score1, score2, orderType, "1");
|
|
|
+ return compareScores(score1, score2, orderType, ProjectClassification.TEAM.getValue());
|
|
|
});
|
|
|
|
|
|
// 3.3 分配名次并分发给队员
|
|
|
@@ -810,7 +838,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
if (i > 0) {
|
|
|
GameScoreVo currentTeamScore = teamGroups.get(teamId).get(0);
|
|
|
GameScoreVo prevTeamScore = teamGroups.get(sortedTeamIds.get(i - 1)).get(0);
|
|
|
- if (compareScores(currentTeamScore, prevTeamScore, orderType, "1") != 0) {
|
|
|
+ if (compareScores(currentTeamScore, prevTeamScore, orderType,
|
|
|
+ ProjectClassification.TEAM.getValue()) != 0) {
|
|
|
currentRank = i + 1;
|
|
|
}
|
|
|
}
|
|
|
@@ -846,8 +875,10 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
*/
|
|
|
private int compareScores(GameScoreVo a, GameScoreVo b, String orderType, String classification) {
|
|
|
// 第一级:主成绩 (0-个人, 1-团队)
|
|
|
- BigDecimal perfA = "0".equals(classification) ? a.getIndividualPerformance() : a.getTeamPerformance();
|
|
|
- BigDecimal perfB = "0".equals(classification) ? b.getIndividualPerformance() : b.getTeamPerformance();
|
|
|
+ BigDecimal perfA = ProjectClassification.SINGLE.getValue().equals(classification) ? a.getIndividualPerformance()
|
|
|
+ : a.getTeamPerformance();
|
|
|
+ BigDecimal perfB = ProjectClassification.SINGLE.getValue().equals(classification) ? b.getIndividualPerformance()
|
|
|
+ : b.getTeamPerformance();
|
|
|
if (perfA == null)
|
|
|
perfA = BigDecimal.ZERO;
|
|
|
if (perfB == null)
|
|
|
@@ -2287,4 +2318,335 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
throw new RuntimeException("导出失败:" + e.getMessage());
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void importTemplate(Long eventId, Long projectId, String classification, HttpServletResponse response) {
|
|
|
+ GameEventProjectVo project = gameEventProjectService.queryById(projectId);
|
|
|
+ int scoreCount = project.getScoreCount() != null ? project.getScoreCount() : 1;
|
|
|
+ List<List<String>> head = new ArrayList<>();
|
|
|
+ if (ProjectClassification.SINGLE.getValue().equals(classification)) {
|
|
|
+ head.add(List.of("运动员号码"));
|
|
|
+ head.add(List.of("姓名"));
|
|
|
+ } else {
|
|
|
+ head.add(List.of("队伍编号"));
|
|
|
+ head.add(List.of("队伍名"));
|
|
|
+ }
|
|
|
+ for (int i = 1; i <= scoreCount; i++) {
|
|
|
+ head.add(List.of("第" + i + "轮成绩"));
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ ExcelUtil.resetResponse("成绩导入模板", response);
|
|
|
+ // 设置文本格式的 Handler
|
|
|
+ SimpleColumnWidthStyleStrategy columnWidthStyleStrategy = new SimpleColumnWidthStyleStrategy(20);
|
|
|
+
|
|
|
+ FastExcel.write(response.getOutputStream())
|
|
|
+ .head(head)
|
|
|
+ .registerWriteHandler(new SheetWriteHandler() {
|
|
|
+ @Override
|
|
|
+ public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder,
|
|
|
+ WriteSheetHolder writeSheetHolder) {
|
|
|
+ Workbook workbook = writeWorkbookHolder.getWorkbook();
|
|
|
+ CellStyle textStyle = workbook.createCellStyle();
|
|
|
+ textStyle.setDataFormat(workbook.createDataFormat().getFormat("@"));
|
|
|
+ Sheet sheet = writeSheetHolder.getSheet();
|
|
|
+ // 根据表头的实际长度,动态为所有列设置默认文本格式
|
|
|
+ for (int i = 0; i < head.size(); i++) {
|
|
|
+ sheet.setDefaultColumnStyle(i, textStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .registerWriteHandler(new AbstractCellWriteHandler() {
|
|
|
+ @Override
|
|
|
+ public void afterCellDispose(WriteSheetHolder writeSheetHolder,
|
|
|
+ WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell,
|
|
|
+ Head head, Integer relativeRowIndex, Boolean isHead) {
|
|
|
+ if (!isHead) {
|
|
|
+ Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
|
|
+ CellStyle textStyle = workbook.createCellStyle();
|
|
|
+ textStyle.setDataFormat(workbook.createDataFormat().getFormat("@"));
|
|
|
+ cell.setCellStyle(textStyle);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .registerWriteHandler(columnWidthStyleStrategy)
|
|
|
+ .sheet("模板")
|
|
|
+ .doWrite(new ArrayList<>());
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("生成导入模板失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public String importScore(Long eventId, Long projectId, String classification, Boolean updateSupport,
|
|
|
+ org.springframework.web.multipart.MultipartFile file) {
|
|
|
+ GameEventProjectVo project = gameEventProjectService.queryById(projectId);
|
|
|
+ int scoreCount = project.getScoreCount() != null ? project.getScoreCount() : 1;
|
|
|
+
|
|
|
+ List<Map<Integer, String>> list;
|
|
|
+ try {
|
|
|
+ list = FastExcel.read(file.getInputStream()).sheet().doReadSync();
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("读取导入文件失败", e);
|
|
|
+ return "读取文件失败";
|
|
|
+ }
|
|
|
+
|
|
|
+ if (CollUtil.isEmpty(list)) {
|
|
|
+ return "文件内容为空";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 预提取所有编号
|
|
|
+ Set<String> codes = list.stream()
|
|
|
+ .skip(1)
|
|
|
+ .map(row -> row.get(0) == null ? "" : String.valueOf(row.get(0)).trim())
|
|
|
+ .filter(code -> StringUtils.isNotBlank(code) && !"null".equals(code))
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ if (codes.isEmpty())
|
|
|
+ return "未检测到有效数据";
|
|
|
+
|
|
|
+ // 2. 批量预加载(仅查询必要字段)
|
|
|
+ Map<String, GameAthlete> athleteMap = new HashMap<>();
|
|
|
+ Map<String, GameTeam> teamMap = new HashMap<>();
|
|
|
+ Map<Long, GameScore> existingScoreMap = new HashMap<>();
|
|
|
+
|
|
|
+ if (ProjectClassification.SINGLE.getValue().equals(classification)) {
|
|
|
+ List<GameAthlete> athletes = gameAthleteMapper.selectList(Wrappers.<GameAthlete>lambdaQuery()
|
|
|
+ .select(GameAthlete::getAthleteId, GameAthlete::getAthleteCode, GameAthlete::getTeamId,
|
|
|
+ GameAthlete::getName)
|
|
|
+ .eq(GameAthlete::getEventId, eventId)
|
|
|
+ .in(GameAthlete::getAthleteCode, codes)
|
|
|
+ .eq(GameAthlete::getDelFlag, "0"));
|
|
|
+ athleteMap = athletes.stream().collect(Collectors.toMap(GameAthlete::getAthleteCode, a -> a));
|
|
|
+
|
|
|
+ if (!athleteMap.isEmpty()) {
|
|
|
+ List<GameScore> scores = baseMapper.selectList(Wrappers.<GameScore>lambdaQuery()
|
|
|
+ .select(GameScore::getScoreId, GameScore::getAthleteId)
|
|
|
+ .eq(GameScore::getProjectId, projectId)
|
|
|
+ .in(GameScore::getAthleteId,
|
|
|
+ athleteMap.values().stream().map(GameAthlete::getAthleteId).toList()));
|
|
|
+ existingScoreMap = scores.stream().collect(Collectors.toMap(GameScore::getAthleteId, s -> s));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ List<GameTeam> teams = gameTeamMapper.selectList(Wrappers.<GameTeam>lambdaQuery()
|
|
|
+ .select(GameTeam::getTeamId, GameTeam::getTeamCode, GameTeam::getTeamName)
|
|
|
+ .eq(GameTeam::getEventId, eventId)
|
|
|
+ .in(GameTeam::getTeamCode, codes)
|
|
|
+ .eq(GameTeam::getDelFlag, "0"));
|
|
|
+ teamMap = teams.stream().collect(Collectors.toMap(GameTeam::getTeamCode, t -> t));
|
|
|
+
|
|
|
+ if (!teamMap.isEmpty()) {
|
|
|
+ List<GameScore> scores = baseMapper.selectList(Wrappers.<GameScore>lambdaQuery()
|
|
|
+ .select(GameScore::getScoreId, GameScore::getTeamId)
|
|
|
+ .eq(GameScore::getProjectId, projectId)
|
|
|
+ .in(GameScore::getTeamId, teamMap.values().stream().map(GameTeam::getTeamId).toList()));
|
|
|
+ // 团体项目成绩按队伍 ID 映射 (取一个代表)
|
|
|
+ existingScoreMap = scores.stream()
|
|
|
+ .collect(Collectors.toMap(GameScore::getTeamId, s -> s, (s1, s2) -> s1));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 处理数据
|
|
|
+ List<GameScore> scoreSaveList = new ArrayList<>();
|
|
|
+ List<GameScoreDetail> detailSaveList = new ArrayList<>();
|
|
|
+ Map<Long, GameScoreBo> teamBoMap = new HashMap<>(); // 团体项目成绩暂存
|
|
|
+ int successNum = 0;
|
|
|
+ int failureNum = 0;
|
|
|
+ StringBuilder failureMsg = new StringBuilder();
|
|
|
+
|
|
|
+ for (int i = 0; i < list.size(); i++) {
|
|
|
+ Map<Integer, String> row = list.get(i);
|
|
|
+ String code = row.get(0) == null ? "" : String.valueOf(row.get(0)).trim();
|
|
|
+ String name = row.get(1) == null ? "" : String.valueOf(row.get(1)).trim();
|
|
|
+ if (i == 0 && ("运动员号码".equals(code) || "队伍编号".equals(code)))
|
|
|
+ continue;
|
|
|
+ if (StringUtils.isBlank(code) || "null".equals(code))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ try {
|
|
|
+ GameScoreBo bo = new GameScoreBo();
|
|
|
+ bo.setEventId(eventId);
|
|
|
+ bo.setProjectId(projectId);
|
|
|
+
|
|
|
+ if (ProjectClassification.SINGLE.getValue().equals(classification)) {
|
|
|
+ GameAthlete athlete = athleteMap.get(code);
|
|
|
+ if (athlete == null) {
|
|
|
+ failureNum++;
|
|
|
+ failureMsg.append("<br/>").append(failureNum).append("、运动员号码 [").append(code).append("] 不存在");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!athlete.getName().equals(name)) {
|
|
|
+ failureNum++;
|
|
|
+ failureMsg.append("<br/>").append(failureNum).append("、运动员号码 [").append(code).append("] 与姓名 [")
|
|
|
+ .append(name).append("] 不匹配,系统内姓名为 [").append(athlete.getName()).append("]");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ GameScore es = existingScoreMap.get(athlete.getAthleteId());
|
|
|
+ if (es != null) {
|
|
|
+ if (!updateSupport) {
|
|
|
+ failureNum++;
|
|
|
+ failureMsg.append("<br/>").append(failureNum).append("、运动员 [").append(name)
|
|
|
+ .append("] 成绩已存在,已跳过");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bo.setScoreId(es.getScoreId());
|
|
|
+ }
|
|
|
+
|
|
|
+ bo.setAthleteId(athlete.getAthleteId());
|
|
|
+ bo.setTeamId(athlete.getTeamId());
|
|
|
+ bo.setScoreType("individual");
|
|
|
+ } else {
|
|
|
+ GameTeam team = teamMap.get(code);
|
|
|
+ if (team == null) {
|
|
|
+ failureNum++;
|
|
|
+ failureMsg.append("<br/>").append(failureNum).append("、队伍编号 [").append(code).append("] 不存在");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (!team.getTeamName().equals(name)) {
|
|
|
+ failureNum++;
|
|
|
+ failureMsg.append("<br/>").append(failureNum).append("、队伍编号 [").append(code).append("] 与队伍名 [")
|
|
|
+ .append(name).append("] 不匹配,系统内队伍名为 [").append(team.getTeamName()).append("]");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ GameScore es = existingScoreMap.get(team.getTeamId());
|
|
|
+ if (es != null) {
|
|
|
+ if (!updateSupport) {
|
|
|
+ failureNum++;
|
|
|
+ failureMsg.append("<br/>").append(failureNum).append("、队伍 [").append(name)
|
|
|
+ .append("] 成绩已存在,已跳过");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ bo.setScoreId(es.getScoreId());
|
|
|
+ }
|
|
|
+
|
|
|
+ bo.setTeamId(team.getTeamId());
|
|
|
+ bo.setScoreType("team");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 填充成绩明细
|
|
|
+ List<GameScoreDetailBo> detailBos = new ArrayList<>();
|
|
|
+ for (int j = 1; j <= scoreCount; j++) {
|
|
|
+ String scoreStr = row.get(j + 1) == null ? "" : String.valueOf(row.get(j + 1)).trim();
|
|
|
+ if (StringUtils.isNotBlank(scoreStr) && !"null".equals(scoreStr)) {
|
|
|
+ BigDecimal performanceValue = parsePerformanceValue(scoreStr, i, j + 1, failureNum,
|
|
|
+ failureMsg);
|
|
|
+ if (performanceValue == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ GameScoreDetailBo dBo = new GameScoreDetailBo();
|
|
|
+ dBo.setAttemptIndex(j);
|
|
|
+ dBo.setPerformanceValue(performanceValue);
|
|
|
+ detailBos.add(dBo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验:如果没有任何成绩明细,则视为无效行
|
|
|
+ if (detailBos.isEmpty()) {
|
|
|
+ failureNum++;
|
|
|
+ failureMsg.append("<br/>").append(failureNum).append("、第").append(i + 1)
|
|
|
+ .append("行:未检测到有效成绩,请检查数据。");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ bo.setDetails(detailBos);
|
|
|
+
|
|
|
+ // 计算统计值 (aggregatePerformance, faultA, faultB)
|
|
|
+ calculateAndSetAggregatePerformance(bo, project);
|
|
|
+
|
|
|
+ if (ProjectClassification.SINGLE.getValue().equals(classification)) {
|
|
|
+ GameScore score = MapstructUtils.convert(bo, GameScore.class);
|
|
|
+ scoreSaveList.add(score);
|
|
|
+
|
|
|
+ // 个人项目:准备持久化明细
|
|
|
+ for (GameScoreDetailBo dBo : detailBos) {
|
|
|
+ GameScoreDetail detail = BeanUtil.copyProperties(dBo, GameScoreDetail.class);
|
|
|
+ detail.setProjectId(projectId);
|
|
|
+ detail.setAthleteId(bo.getAthleteId());
|
|
|
+ detail.setScoreId(bo.getScoreId());
|
|
|
+ detailSaveList.add(detail);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 团体项目暂存 BO,用于后续 handleTeamScoreUpdate
|
|
|
+ teamBoMap.put(bo.getTeamId(), bo);
|
|
|
+ }
|
|
|
+ successNum++;
|
|
|
+ } catch (Exception e) {
|
|
|
+ failureNum++;
|
|
|
+ failureMsg.append("<br/>").append(failureNum).append("、第").append(i + 1).append("行处理失败:")
|
|
|
+ .append(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 执行批量入库
|
|
|
+ if (!scoreSaveList.isEmpty()) {
|
|
|
+ baseMapper.insertOrUpdateBatch(scoreSaveList);
|
|
|
+ // 更新明细中的 scoreId (仅个人项目需要)
|
|
|
+ for (int i = 0; i < scoreSaveList.size(); i++) {
|
|
|
+ final Long scoreId = scoreSaveList.get(i).getScoreId();
|
|
|
+ final Long athleteId = scoreSaveList.get(i).getAthleteId();
|
|
|
+ detailSaveList.stream()
|
|
|
+ .filter(d -> athleteId.equals(d.getAthleteId()))
|
|
|
+ .forEach(d -> d.setScoreId(scoreId));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 团体项目同步给队伍所有队员
|
|
|
+ if (ProjectClassification.TEAM.getValue().equals(classification) && !teamBoMap.isEmpty()) {
|
|
|
+ for (GameScoreBo teamBo : teamBoMap.values()) {
|
|
|
+ handleTeamScoreUpdate(teamBo);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!detailSaveList.isEmpty()) {
|
|
|
+ scoreDetailMapper.insertOrUpdateBatch(detailSaveList);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 统一重计排名 (仅此一次)
|
|
|
+ recalculateRankingsAndPoints(eventId, projectId);
|
|
|
+
|
|
|
+ if (failureNum > 0) {
|
|
|
+ failureMsg.insert(0, "导入完成。共 " + successNum + " 条成功," + failureNum + " 条失败,错误如下:");
|
|
|
+ } else {
|
|
|
+ failureMsg.setLength(0);
|
|
|
+ failureMsg.append("恭喜您,数据已全部导入成功!共 ").append(successNum).append(" 条。");
|
|
|
+ }
|
|
|
+ return failureMsg.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private BigDecimal parsePerformanceValue(String scoreStr, int rowIndex, int colIndex, int failureNum,
|
|
|
+ StringBuilder failureMsg) {
|
|
|
+ if (StringUtils.isBlank(scoreStr))
|
|
|
+ return null;
|
|
|
+ scoreStr = scoreStr.trim();
|
|
|
+ // 如果包含冒号,则认为是时间格式 (HH:mm:ss.SSS 或 mm:ss.SSS)
|
|
|
+ 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]);
|
|
|
+ } else {
|
|
|
+ throw new IllegalArgumentException("格式非法");
|
|
|
+ }
|
|
|
+ return BigDecimal.valueOf(totalSeconds).setScale(3, java.math.RoundingMode.HALF_UP);
|
|
|
+ } catch (Exception e) {
|
|
|
+ failureMsg.append("<br/>第").append(rowIndex + 1).append("行第").append(colIndex + 1)
|
|
|
+ .append("列时间格式不正确:[").append(scoreStr).append("]");
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 普通数字格式
|
|
|
+ return new BigDecimal(scoreStr);
|
|
|
+ }
|
|
|
}
|