diff --git a/src/main/java/org/springblade/modules/martial/controller/MartialExportController.java b/src/main/java/org/springblade/modules/martial/controller/MartialExportController.java index 8de38cf..3f572ad 100644 --- a/src/main/java/org/springblade/modules/martial/controller/MartialExportController.java +++ b/src/main/java/org/springblade/modules/martial/controller/MartialExportController.java @@ -1,5 +1,6 @@ package org.springblade.modules.martial.controller; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.servlet.http.HttpServletResponse; @@ -7,14 +8,14 @@ import lombok.AllArgsConstructor; import org.springblade.core.excel.util.ExcelUtil; import org.springblade.core.tool.api.R; import org.springblade.core.tool.utils.DateUtil; -import org.springblade.modules.martial.excel.AthleteExportExcel; -import org.springblade.modules.martial.excel.ResultExportExcel; -import org.springblade.modules.martial.excel.ScheduleExportExcel; -import org.springblade.modules.martial.excel.ScheduleExportExcel2; +import org.springblade.modules.martial.excel.*; +import org.springblade.modules.martial.pojo.dto.CompetitionGroupDTO; +import org.springblade.modules.martial.pojo.dto.ScheduleResultDTO; +import org.springblade.modules.martial.pojo.entity.MartialAthlete; +import org.springblade.modules.martial.pojo.entity.MartialProject; +import org.springblade.modules.martial.pojo.entity.MartialVenue; import org.springblade.modules.martial.pojo.vo.CertificateVO; -import org.springblade.modules.martial.service.IMartialAthleteService; -import org.springblade.modules.martial.service.IMartialResultService; -import org.springblade.modules.martial.service.IMartialScheduleService; +import org.springblade.modules.martial.service.*; import org.springframework.web.bind.annotation.*; import java.io.IOException; @@ -32,6 +33,8 @@ public class MartialExportController { private final IMartialResultService resultService; private final IMartialAthleteService athleteService; private final IMartialScheduleService scheduleService; + private final IMartialProjectService projectService; + private final IMartialVenueService venueService; @GetMapping("/results") @Operation(summary = "导出成绩单", description = "导出指定赛事或项目的成绩单Excel") @@ -111,4 +114,279 @@ public class MartialExportController { CertificateVO certificate = resultService.generateCertificateData(resultId); return R.data(certificate); } + + // ========== 报名详情统计导出 ========== + + @GetMapping("/registration/participants") + @Operation(summary = "导出参赛人数统计", description = "按单位统计参赛人数") + public void exportParticipantStats(@RequestParam Long competitionId, HttpServletResponse response) { + List list = buildParticipantStats(competitionId); + String fileName = "参赛人数统计_" + DateUtil.today(); + ExcelUtil.export(response, fileName, "参赛人数统计", list, ParticipantStatsExcel.class); + } + + @GetMapping("/registration/project-time") + @Operation(summary = "导出项目时间统计", description = "按项目统计时间") + public void exportProjectTimeStats(@RequestParam Long competitionId, HttpServletResponse response) { + List list = buildProjectTimeStats(competitionId); + String fileName = "项目时间统计_" + DateUtil.today(); + ExcelUtil.export(response, fileName, "项目时间统计", list, ProjectTimeStatsExcel.class); + } + + @GetMapping("/registration/amount") + @Operation(summary = "导出金额统计", description = "按单位统计金额") + public void exportAmountStats(@RequestParam Long competitionId, HttpServletResponse response) { + List list = buildAmountStats(competitionId); + String fileName = "金额统计_" + DateUtil.today(); + ExcelUtil.export(response, fileName, "金额统计", list, AmountStatsExcel.class); + } + + @GetMapping("/registration/schedule-table") + @Operation(summary = "导出编排表号统计", description = "导出编排表号") + public void exportScheduleTableStats(@RequestParam Long competitionId, HttpServletResponse response) { + List list = buildScheduleTableStats(competitionId); + String fileName = "编排表号统计_" + DateUtil.today(); + ExcelUtil.export(response, fileName, "编排表号统计", list, ScheduleTableStatsExcel.class); + } + + private List buildParticipantStats(Long competitionId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(MartialAthlete::getCompetitionId, competitionId) + .eq(MartialAthlete::getIsDeleted, 0); + List athletes = athleteService.list(wrapper); + + java.util.Map projectMap = new java.util.HashMap<>(); + java.util.Set projectIds = athletes.stream() + .map(MartialAthlete::getProjectId) + .filter(java.util.Objects::nonNull) + .collect(java.util.stream.Collectors.toSet()); + if (!projectIds.isEmpty()) { + projectService.listByIds(projectIds).forEach(p -> projectMap.put(p.getId(), p)); + } + + java.util.Map orgMap = new java.util.LinkedHashMap<>(); + java.util.Map> orgIdCards = new java.util.HashMap<>(); + + for (MartialAthlete athlete : athletes) { + String org = athlete.getOrganization(); + if (org == null || org.isEmpty()) org = "未知单位"; + + orgMap.computeIfAbsent(org, k -> { + ParticipantStatsExcel excel = new ParticipantStatsExcel(); + excel.setOrganization(k); + excel.setSingleCount(0); + excel.setTeamCount(0); + excel.setMaleCount(0); + excel.setFemaleCount(0); + excel.setTotal(0); + return excel; + }); + orgIdCards.computeIfAbsent(org, k -> new java.util.HashSet<>()); + + ParticipantStatsExcel stat = orgMap.get(org); + MartialProject project = projectMap.get(athlete.getProjectId()); + int projectType = (project != null && project.getType() != null) ? project.getType() : 1; + + if (projectType == 1) { + stat.setSingleCount(stat.getSingleCount() + 1); + } else { + stat.setTeamCount(stat.getTeamCount() + 1); + } + + String idCard = athlete.getIdCard(); + if (idCard != null && !idCard.isEmpty() && !orgIdCards.get(org).contains(idCard)) { + orgIdCards.get(org).add(idCard); + stat.setTotal(stat.getTotal() + 1); + if (athlete.getGender() != null && athlete.getGender() == 1) { + stat.setMaleCount(stat.getMaleCount() + 1); + } else if (athlete.getGender() != null && athlete.getGender() == 2) { + stat.setFemaleCount(stat.getFemaleCount() + 1); + } + } + } + + java.util.List result = new java.util.ArrayList<>(orgMap.values()); + for (int i = 0; i < result.size(); i++) { + result.get(i).setIndex(i + 1); + } + return result; + } + + private List buildProjectTimeStats(Long competitionId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(MartialAthlete::getCompetitionId, competitionId) + .eq(MartialAthlete::getIsDeleted, 0); + List athletes = athleteService.list(wrapper); + + java.util.Map projectMap = new java.util.HashMap<>(); + java.util.Set projectIds = athletes.stream() + .map(MartialAthlete::getProjectId) + .filter(java.util.Objects::nonNull) + .collect(java.util.stream.Collectors.toSet()); + if (!projectIds.isEmpty()) { + projectService.listByIds(projectIds).forEach(p -> projectMap.put(p.getId(), p)); + } + + java.util.Map projectAthleteCount = new java.util.HashMap<>(); + for (MartialAthlete athlete : athletes) { + Long projectId = athlete.getProjectId(); + if (projectId != null) { + projectAthleteCount.merge(projectId, 1, Integer::sum); + } + } + + java.util.List result = new java.util.ArrayList<>(); + int index = 1; + for (java.util.Map.Entry entry : projectAthleteCount.entrySet()) { + MartialProject project = projectMap.get(entry.getKey()); + if (project != null) { + ProjectTimeStatsExcel excel = new ProjectTimeStatsExcel(); + excel.setIndex(index++); + excel.setProjectName(project.getProjectName()); + excel.setProjectType(project.getType() != null && project.getType() == 2 ? "集体" : "单人"); + excel.setAthleteCount(entry.getValue()); + excel.setGroupCount(project.getType() != null && project.getType() == 2 ? 1 : entry.getValue()); + excel.setEstimatedDuration(project.getEstimatedDuration()); + excel.setProjectCode(project.getProjectCode()); + result.add(excel); + } + } + return result; + } + + private List buildAmountStats(Long competitionId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(MartialAthlete::getCompetitionId, competitionId) + .eq(MartialAthlete::getIsDeleted, 0); + List athletes = athleteService.list(wrapper); + + java.util.Map projectMap = new java.util.HashMap<>(); + java.util.Set projectIds = athletes.stream() + .map(MartialAthlete::getProjectId) + .filter(java.util.Objects::nonNull) + .collect(java.util.stream.Collectors.toSet()); + if (!projectIds.isEmpty()) { + projectService.listByIds(projectIds).forEach(p -> projectMap.put(p.getId(), p)); + } + + java.util.Map orgMap = new java.util.LinkedHashMap<>(); + java.util.Map> orgProjectIds = new java.util.HashMap<>(); + + for (MartialAthlete athlete : athletes) { + String org = athlete.getOrganization(); + if (org == null || org.isEmpty()) org = "未知单位"; + + orgMap.computeIfAbsent(org, k -> { + AmountStatsExcel excel = new AmountStatsExcel(); + excel.setOrganization(k); + excel.setProjectCount(0); + excel.setTotalAmount(java.math.BigDecimal.ZERO); + return excel; + }); + orgProjectIds.computeIfAbsent(org, k -> new java.util.HashSet<>()); + + Long projectId = athlete.getProjectId(); + if (projectId != null && !orgProjectIds.get(org).contains(projectId)) { + orgProjectIds.get(org).add(projectId); + AmountStatsExcel stat = orgMap.get(org); + stat.setProjectCount(stat.getProjectCount() + 1); + MartialProject project = projectMap.get(projectId); + if (project != null && project.getPrice() != null) { + stat.setTotalAmount(stat.getTotalAmount().add(project.getPrice())); + } + } + } + + java.util.List result = new java.util.ArrayList<>(orgMap.values()); + for (int i = 0; i < result.size(); i++) { + result.get(i).setIndex(i + 1); + } + return result; + } + + private List buildScheduleTableStats(Long competitionId) { + ScheduleResultDTO scheduleResult = scheduleService.getScheduleResult(competitionId); + if (scheduleResult == null || scheduleResult.getCompetitionGroups() == null) { + return new java.util.ArrayList<>(); + } + + LambdaQueryWrapper venueWrapper = new LambdaQueryWrapper<>(); + venueWrapper.eq(MartialVenue::getCompetitionId, competitionId) + .eq(MartialVenue::getIsDeleted, 0); + List venues = venueService.list(venueWrapper); + java.util.Map venueMap = venues.stream() + .collect(java.util.stream.Collectors.toMap(MartialVenue::getId, v -> v)); + + java.util.List result = new java.util.ArrayList<>(); + java.util.List groups = scheduleResult.getCompetitionGroups(); + + for (int i = 0; i < groups.size(); i++) { + CompetitionGroupDTO group = groups.get(i); + ScheduleTableStatsExcel excel = new ScheduleTableStatsExcel(); + excel.setIndex(i + 1); + excel.setProjectName(group.getTitle()); + excel.setProjectType(group.getType()); + int participantCount = group.getParticipants() != null ? group.getParticipants().size() : 0; + excel.setAthleteCount(participantCount); + excel.setGroupCount("集体".equals(group.getType()) ? 1 : participantCount); + excel.setEstimatedDuration(group.getEstimatedDuration() != null ? group.getEstimatedDuration() : 0); + + MartialVenue venue = venueMap.get(group.getVenueId()); + excel.setVenueName(venue != null ? venue.getVenueName() : "-"); + + String timeSlot = group.getTimeSlot(); + if (timeSlot != null && timeSlot.contains(":")) { + int hour = Integer.parseInt(timeSlot.split(":")[0]); + excel.setTimeSlot(hour < 12 ? "上午" : "下午"); + } else { + excel.setTimeSlot("-"); + } + + excel.setTableNo(generateTableNo(group, groups, venueMap)); + result.add(excel); + } + return result; + } + + private String generateTableNo(CompetitionGroupDTO group, + java.util.List allGroups, + java.util.Map venueMap) { + int venueNo = 1; + if (group.getVenueId() != null) { + MartialVenue venue = venueMap.get(group.getVenueId()); + if (venue != null && venue.getVenueName() != null) { + java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("\\d+").matcher(venue.getVenueName()); + if (matcher.find()) { + venueNo = Integer.parseInt(matcher.group()); + } + } + } + + int period = 1; + if (group.getTimeSlot() != null && group.getTimeSlot().contains(":")) { + int hour = Integer.parseInt(group.getTimeSlot().split(":")[0]); + period = hour < 12 ? 1 : 2; + } + + final int finalPeriod = period; + final Long venueId = group.getVenueId(); + java.util.List sameSlotGroups = allGroups.stream() + .filter(g -> { + if (!java.util.Objects.equals(g.getVenueId(), venueId)) return false; + if (g.getTimeSlot() == null || !g.getTimeSlot().contains(":")) return false; + int gHour = Integer.parseInt(g.getTimeSlot().split(":")[0]); + return (gHour < 12 ? 1 : 2) == finalPeriod; + }) + .collect(java.util.stream.Collectors.toList()); + + int orderIndex = 0; + for (int i = 0; i < sameSlotGroups.size(); i++) { + if (java.util.Objects.equals(sameSlotGroups.get(i).getId(), group.getId())) { + orderIndex = i + 1; + break; + } + } + + return String.format("%d%d%02d", venueNo, period, orderIndex); + } } diff --git a/src/main/java/org/springblade/modules/martial/excel/AmountStatsExcel.java b/src/main/java/org/springblade/modules/martial/excel/AmountStatsExcel.java new file mode 100644 index 0000000..85ed671 --- /dev/null +++ b/src/main/java/org/springblade/modules/martial/excel/AmountStatsExcel.java @@ -0,0 +1,39 @@ +package org.springblade.modules.martial.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 金额统计导出Excel + */ +@Data +@ColumnWidth(15) +@HeadRowHeight(20) +@ContentRowHeight(18) +public class AmountStatsExcel implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty("序号") + @ColumnWidth(8) + private Integer index; + + @ExcelProperty("学校/单位") + @ColumnWidth(25) + private String organization; + + @ExcelProperty("项目数") + @ColumnWidth(10) + private Integer projectCount; + + @ExcelProperty("金额") + @ColumnWidth(12) + private BigDecimal totalAmount; +} diff --git a/src/main/java/org/springblade/modules/martial/excel/ParticipantStatsExcel.java b/src/main/java/org/springblade/modules/martial/excel/ParticipantStatsExcel.java new file mode 100644 index 0000000..32d345d --- /dev/null +++ b/src/main/java/org/springblade/modules/martial/excel/ParticipantStatsExcel.java @@ -0,0 +1,50 @@ +package org.springblade.modules.martial.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 参赛人数统计导出Excel + */ +@Data +@ColumnWidth(15) +@HeadRowHeight(20) +@ContentRowHeight(18) +public class ParticipantStatsExcel implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty("序号") + @ColumnWidth(8) + private Integer index; + + @ExcelProperty("单位") + @ColumnWidth(25) + private String organization; + + @ExcelProperty("单人项目") + @ColumnWidth(12) + private Integer singleCount; + + @ExcelProperty("集体项目") + @ColumnWidth(12) + private Integer teamCount; + + @ExcelProperty("男") + @ColumnWidth(8) + private Integer maleCount; + + @ExcelProperty("女") + @ColumnWidth(8) + private Integer femaleCount; + + @ExcelProperty("合计") + @ColumnWidth(8) + private Integer total; +} diff --git a/src/main/java/org/springblade/modules/martial/excel/ProjectTimeStatsExcel.java b/src/main/java/org/springblade/modules/martial/excel/ProjectTimeStatsExcel.java new file mode 100644 index 0000000..9218b81 --- /dev/null +++ b/src/main/java/org/springblade/modules/martial/excel/ProjectTimeStatsExcel.java @@ -0,0 +1,50 @@ +package org.springblade.modules.martial.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 项目时间统计导出Excel + */ +@Data +@ColumnWidth(15) +@HeadRowHeight(20) +@ContentRowHeight(18) +public class ProjectTimeStatsExcel implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty("序号") + @ColumnWidth(8) + private Integer index; + + @ExcelProperty("项目") + @ColumnWidth(20) + private String projectName; + + @ExcelProperty("单人/集体") + @ColumnWidth(12) + private String projectType; + + @ExcelProperty("人数/集体") + @ColumnWidth(12) + private Integer athleteCount; + + @ExcelProperty("组数") + @ColumnWidth(8) + private Integer groupCount; + + @ExcelProperty("时长(分)") + @ColumnWidth(10) + private Integer estimatedDuration; + + @ExcelProperty("项目编码") + @ColumnWidth(12) + private String projectCode; +} diff --git a/src/main/java/org/springblade/modules/martial/excel/ScheduleTableStatsExcel.java b/src/main/java/org/springblade/modules/martial/excel/ScheduleTableStatsExcel.java new file mode 100644 index 0000000..9ca68a3 --- /dev/null +++ b/src/main/java/org/springblade/modules/martial/excel/ScheduleTableStatsExcel.java @@ -0,0 +1,58 @@ +package org.springblade.modules.martial.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 编排表号统计导出Excel + */ +@Data +@ColumnWidth(15) +@HeadRowHeight(20) +@ContentRowHeight(18) +public class ScheduleTableStatsExcel implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty("序号") + @ColumnWidth(8) + private Integer index; + + @ExcelProperty("项目") + @ColumnWidth(20) + private String projectName; + + @ExcelProperty("单人/集体") + @ColumnWidth(12) + private String projectType; + + @ExcelProperty("人数") + @ColumnWidth(8) + private Integer athleteCount; + + @ExcelProperty("组数") + @ColumnWidth(8) + private Integer groupCount; + + @ExcelProperty("时长(分)") + @ColumnWidth(10) + private Integer estimatedDuration; + + @ExcelProperty("场地") + @ColumnWidth(12) + private String venueName; + + @ExcelProperty("时段") + @ColumnWidth(8) + private String timeSlot; + + @ExcelProperty("表号") + @ColumnWidth(10) + private String tableNo; +}