|
|
@@ -1359,23 +1359,22 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
try {
|
|
|
// 1. 获取所有项目(绕过 Service 层繁重的统计逻辑,直接查持久层)
|
|
|
List<GameEventProjectVo> projects = gameEventProjectMapper.selectVoList(
|
|
|
- Wrappers.lambdaQuery(GameEventProject.class)
|
|
|
- .eq(GameEventProject::getEventId, eventId)
|
|
|
- .eq(GameEventProject::getDelFlag, "0")
|
|
|
- );
|
|
|
+ Wrappers.lambdaQuery(GameEventProject.class)
|
|
|
+ .eq(GameEventProject::getEventId, eventId)
|
|
|
+ .eq(GameEventProject::getDelFlag, "0"));
|
|
|
|
|
|
// 2. 获取所有队伍(手动实现 ancestors 过滤,彻底避开 Service 层的 N+1 问题)
|
|
|
LambdaQueryWrapper<GameTeam> teamWrapper = Wrappers.lambdaQuery(GameTeam.class)
|
|
|
- .eq(GameTeam::getEventId, eventId);
|
|
|
+ .eq(GameTeam::getEventId, eventId);
|
|
|
|
|
|
List<GameTeamVo> teams;
|
|
|
if (rgId != null) {
|
|
|
// 找出当前分组及其所有子孙分组的 ID
|
|
|
List<Long> allRgIds = gameRankGroupMapper.selectList(
|
|
|
- Wrappers.lambdaQuery(GameRankGroup.class)
|
|
|
- .select(GameRankGroup::getRgId)
|
|
|
- .apply("(rg_id = {0} OR FIND_IN_SET({0}, ancestors))", rgId)
|
|
|
- ).stream().map(GameRankGroup::getRgId).collect(Collectors.toList());
|
|
|
+ Wrappers.lambdaQuery(GameRankGroup.class)
|
|
|
+ .select(GameRankGroup::getRgId)
|
|
|
+ .apply("(rg_id = {0} OR FIND_IN_SET({0}, ancestors))", rgId))
|
|
|
+ .stream().map(GameRankGroup::getRgId).collect(Collectors.toList());
|
|
|
|
|
|
if (allRgIds.isEmpty()) {
|
|
|
teams = new ArrayList<>();
|
|
|
@@ -1631,14 +1630,14 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
* @param response HTTP响应对象
|
|
|
*/
|
|
|
@Override
|
|
|
- public void exportScoresDetail(Long eventId, HttpServletResponse response) {
|
|
|
+ public void exportScoresDetail(Long eventId, Integer topN, List<Long> projectIds, HttpServletResponse response) {
|
|
|
try {
|
|
|
- log.info("开始导出成绩详情,eventId: {}", eventId);
|
|
|
+ log.info("开始导出成绩详情,eventId: {}, topN: {}, projectIds: {}", eventId, topN, projectIds);
|
|
|
|
|
|
- // 1. 获取所有项目
|
|
|
- List<GameEventProjectVo> projects = gameEventProjectService.queryListByEventId(eventId);
|
|
|
+ // 1. 获取项目列表并根据 projectIds 过滤
|
|
|
+ List<GameEventProjectVo> projects = gameEventProjectService.queryListByEventIdAndProjectIds(eventId, projectIds);
|
|
|
if (projects.isEmpty()) {
|
|
|
- throw new RuntimeException("未找到赛事项目");
|
|
|
+ throw new RuntimeException("未找到待导出的赛事项目");
|
|
|
}
|
|
|
|
|
|
// 2. 创建Excel工作簿
|
|
|
@@ -1651,7 +1650,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
|
|
|
// 4. 为每个项目创建Sheet页
|
|
|
for (GameEventProjectVo project : projects) {
|
|
|
- createProjectSheet(workbook, project, eventId, headerStyle, dataStyleOdd, dataStyleEven);
|
|
|
+ createProjectSheet(workbook, project, eventId, topN, headerStyle, dataStyleOdd, dataStyleEven);
|
|
|
}
|
|
|
|
|
|
// 5. 设置响应头并输出
|
|
|
@@ -1670,7 +1669,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
/**
|
|
|
* 为单个项目创建Sheet页
|
|
|
*/
|
|
|
- private void createProjectSheet(Workbook workbook, GameEventProjectVo project, Long eventId,
|
|
|
+ private void createProjectSheet(Workbook workbook, GameEventProjectVo project, Long eventId, Integer topN,
|
|
|
CellStyle headerStyle, CellStyle dataStyleOdd, CellStyle dataStyleEven) {
|
|
|
|
|
|
// 1. 创建Sheet页,名称限制在31个字符内
|
|
|
@@ -1678,18 +1677,37 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
Sheet sheet = workbook.createSheet(sheetName);
|
|
|
|
|
|
// 2. 获取项目详细数据
|
|
|
- List<Map<String, Object>> projectData = getProjectScoreDataAll(
|
|
|
+ List<Map<String, Object>> projectScore = getProjectScoreDataAll(
|
|
|
eventId,
|
|
|
project.getProjectId(),
|
|
|
project.getClassification());
|
|
|
+ if (projectScore == null || projectScore.isEmpty()){
|
|
|
+ throw new RuntimeException("未找到待导出的赛事项目成绩详细数据");
|
|
|
+ }
|
|
|
|
|
|
- // 3. 创建标题行
|
|
|
+ // 3. 如果指定了 topN,则过滤前N名
|
|
|
+ if (topN != null && topN > 0) {
|
|
|
+ projectScore = projectScore.stream()
|
|
|
+ .filter(data -> {
|
|
|
+ Object rank = data.get("scoreRank");
|
|
|
+ return rank != null && (Integer)rank > 0;
|
|
|
+ })
|
|
|
+ .sorted((a, b) -> {
|
|
|
+ Integer rankA = (Integer) a.get("scoreRank");
|
|
|
+ Integer rankB = (Integer) b.get("scoreRank");
|
|
|
+ return rankA.compareTo(rankB);
|
|
|
+ })
|
|
|
+ .limit(topN)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 创建标题行
|
|
|
createProjectHeaderRow(sheet, project, headerStyle);
|
|
|
|
|
|
- // 4. 填充数据行
|
|
|
- fillProjectDataRows(sheet, projectData, project, dataStyleOdd, dataStyleEven);
|
|
|
+ // 5. 填充数据行
|
|
|
+ fillProjectDataRows(sheet, projectScore, project, dataStyleOdd, dataStyleEven);
|
|
|
|
|
|
- // 5. 自动调整列宽
|
|
|
+ // 6. 自动调整列宽
|
|
|
autoSizeColumns(sheet);
|
|
|
}
|
|
|
|
|
|
@@ -1830,6 +1848,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
data.put("teamCode", team.getTeamCode());
|
|
|
data.put("eventId", eventId);
|
|
|
data.put("projectId", projectId);
|
|
|
+ data.put("projectType", project.getProjectType());
|
|
|
+ data.put("projectName", project.getProjectName());
|
|
|
|
|
|
// 查询成绩信息
|
|
|
GameScoreVo score = getScoreByAthleteIdAndProjectId(athlete.getAthleteId(), projectId);
|
|
|
@@ -1872,8 +1892,8 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
if (ProjectClassification.SINGLE.getValue().equals(project.getClassification())) {
|
|
|
// 个人项目列标题
|
|
|
String[] headers = {
|
|
|
- "序号", "队伍编号", "队伍名称", "姓名", "号码",
|
|
|
- "个人成绩", "积分", "排名", "更新时间"
|
|
|
+ "序号", "队伍编号", "队伍名称", "项目类型", "项目", "号码", "姓名",
|
|
|
+ "性别", "个人成绩", "积分", "排名", "更新时间"
|
|
|
};
|
|
|
|
|
|
for (String header : headers) {
|
|
|
@@ -1884,7 +1904,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
} else {
|
|
|
// 团体项目列标题
|
|
|
String[] headers = {
|
|
|
- "序号", "队伍编号", "队伍名称", "团队成绩",
|
|
|
+ "序号", "队伍编号", "队伍名称", "项目类型", "项目", "团队成绩",
|
|
|
"积分", "排名", "更新时间"
|
|
|
};
|
|
|
|
|
|
@@ -1896,15 +1916,34 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 根据项目类型值获取对应的标签
|
|
|
+ * @param dictInfo 项目类型字典列表
|
|
|
+ * @param dictValue 项目类型值
|
|
|
+ * @return 项目类型标签,如果未找到则返回原值
|
|
|
+ */
|
|
|
+ private String getDictLabel(List<SysDictDataVo> dictInfo, Object dictValue) {
|
|
|
+ if (dictValue == null || dictInfo == null) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+
|
|
|
+ return dictInfo.stream()
|
|
|
+ .filter(pt -> Objects.equals(pt.getDictValue(), dictValue.toString()))
|
|
|
+ .map(SysDictDataVo::getDictLabel)
|
|
|
+ .findFirst()
|
|
|
+ .orElse(dictValue.toString());
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 填充项目数据行
|
|
|
*/
|
|
|
- private void fillProjectDataRows(Sheet sheet, List<Map<String, Object>> projectData,
|
|
|
+ private void fillProjectDataRows(Sheet sheet, List<Map<String, Object>> projectScore,
|
|
|
GameEventProjectVo project, CellStyle dataStyleOdd, CellStyle dataStyleEven) {
|
|
|
|
|
|
+ List<SysDictDataVo> projectTypes = dictTypeService.selectDictDataByType("game_project_type");
|
|
|
int rowIndex = 1;
|
|
|
- for (int i = 0; i < projectData.size(); i++) {
|
|
|
- Map<String, Object> data = projectData.get(i);
|
|
|
+ for (int i = 0; i < projectScore.size(); i++) {
|
|
|
+ Map<String, Object> data = projectScore.get(i);
|
|
|
Row dataRow = sheet.createRow(rowIndex++);
|
|
|
|
|
|
// 选择行样式
|
|
|
@@ -1917,11 +1956,12 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
createCellWithStyle(dataRow, colIndex++, i + 1, currentRowStyle); // 序号
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("teamCode"), currentRowStyle); // 队伍编号
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("teamName"), currentRowStyle); // 队伍名称
|
|
|
+ createCellWithStyle(dataRow, colIndex++, getDictLabel(projectTypes, data.get("projectType")), currentRowStyle); // 项目类型
|
|
|
+ createCellWithStyle(dataRow, colIndex++, data.get("projectName"), currentRowStyle); // 项目名称
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("athleteCode"), currentRowStyle); // 号码
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("name"), currentRowStyle); // 姓名
|
|
|
- createCellWithStyle(dataRow, colIndex++, data.get("gender") == null ? "" : data.get("gender").equals("1") ? "男" : "女", currentRowStyle); // 性别
|
|
|
- createCellWithStyle(dataRow, colIndex++, data.get("projectType"), currentRowStyle); // 项目类型
|
|
|
- createCellWithStyle(dataRow, colIndex++, data.get("projectName"), currentRowStyle); // 项目名称
|
|
|
+ createCellWithStyle(dataRow, colIndex++,
|
|
|
+ data.get("gender") == null ? "" : data.get("gender").equals("1") ? "男" : "女", currentRowStyle); // 性别
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("individualPerformance"), currentRowStyle); // 个人成绩
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("scorePoint"), currentRowStyle); // 积分
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("scoreRank"), currentRowStyle); // 排名
|
|
|
@@ -1931,7 +1971,7 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
createCellWithStyle(dataRow, colIndex++, i + 1, currentRowStyle); // 序号
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("teamCode"), currentRowStyle); // 队伍编号
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("teamName"), currentRowStyle); // 队伍名称
|
|
|
- createCellWithStyle(dataRow, colIndex++, data.get("projectType"), currentRowStyle); // 项目类型
|
|
|
+ createCellWithStyle(dataRow, colIndex++, getDictLabel(projectTypes, data.get("projectType")), currentRowStyle); // 项目类型
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("projectName"), currentRowStyle); // 项目名称
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("teamPerformance"), currentRowStyle); // 团队成绩
|
|
|
createCellWithStyle(dataRow, colIndex++, data.get("scorePoint"), currentRowStyle); // 积分
|
|
|
@@ -2092,4 +2132,67 @@ public class GameScoreServiceImpl implements IGameScoreService {
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出单个项目的成绩详情
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void exportProjectScore(Long eventId, Long projectId, Integer topN, HttpServletResponse response) {
|
|
|
+ try {
|
|
|
+ // 1. 获取项目信息
|
|
|
+ GameEventProjectVo project = gameEventProjectService.queryById(projectId);
|
|
|
+ if (project == null) {
|
|
|
+ throw new RuntimeException("项目不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 获取数据
|
|
|
+ List<Map<String, Object>> dataList = getProjectScoreDataAll(eventId, projectId,
|
|
|
+ project.getClassification());
|
|
|
+
|
|
|
+ // 3. 如果指定了 topN,则按排名过滤或截取前 N 条
|
|
|
+ if (topN != null && topN > 0 && !dataList.isEmpty()) {
|
|
|
+ // 先按排名排序,确保 topN 准确
|
|
|
+ dataList.sort((a, b) -> {
|
|
|
+ Integer rankA = a.get("scoreRank") != null ? (Integer) a.get("scoreRank") : Integer.MAX_VALUE;
|
|
|
+ Integer rankB = b.get("scoreRank") != null ? (Integer) b.get("scoreRank") : Integer.MAX_VALUE;
|
|
|
+ // 如果排名为0,放到最后
|
|
|
+ if (rankA == 0)
|
|
|
+ rankA = Integer.MAX_VALUE;
|
|
|
+ if (rankB == 0)
|
|
|
+ rankB = Integer.MAX_VALUE;
|
|
|
+ return rankA.compareTo(rankB);
|
|
|
+ });
|
|
|
+
|
|
|
+ if (dataList.size() > topN) {
|
|
|
+ dataList = dataList.subList(0, topN);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 创建Excel
|
|
|
+ try (Workbook workbook = new XSSFWorkbook()) {
|
|
|
+ CellStyle headerStyle = createHeaderStyle(workbook);
|
|
|
+ CellStyle dataStyleOdd = createDataStyle(workbook, IndexedColors.WHITE);
|
|
|
+ CellStyle dataStyleEven = createDataStyle(workbook, IndexedColors.GREY_25_PERCENT);
|
|
|
+
|
|
|
+ String sheetName = truncateSheetName(project.getProjectName());
|
|
|
+ Sheet sheet = workbook.createSheet(sheetName);
|
|
|
+
|
|
|
+ createProjectHeaderRow(sheet, project, headerStyle);
|
|
|
+ fillProjectDataRows(sheet, dataList, project, dataStyleOdd, dataStyleEven);
|
|
|
+ autoSizeColumns(sheet);
|
|
|
+
|
|
|
+ // 5. 设置文件名并响应
|
|
|
+ String classificationStr = ProjectClassification.SINGLE.getValue().equals(project.getClassification())
|
|
|
+ ? "个人"
|
|
|
+ : "团体";
|
|
|
+ String fileName = classificationStr + "_" + project.getProjectName() + "_成绩详情";
|
|
|
+ setResponseHeaders(response, fileName);
|
|
|
+
|
|
|
+ workbook.write(response.getOutputStream());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("导出项目成绩失败", e);
|
|
|
+ throw new RuntimeException("导出失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|