From 2e8dc9bb771d4b99f212da6b897b217f1255c9b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=85=E6=88=BF?= Date: Fri, 23 Jan 2026 15:33:48 +0800 Subject: [PATCH] feat(schedule): split team projects into groups when exceeding maxParticipants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added splitTeamIntoSubGroups method for team project grouping - Team projects now split by maxParticipants (max teams per group) - Display format: projectName + category + 第X组 (when split) - Consistent with individual project grouping behavior Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> --- .../grouping/ParticipantGroupingService.java | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/springblade/modules/martial/service/schedule/grouping/ParticipantGroupingService.java b/src/main/java/org/springblade/modules/martial/service/schedule/grouping/ParticipantGroupingService.java index 78c5c83..a277f4b 100644 --- a/src/main/java/org/springblade/modules/martial/service/schedule/grouping/ParticipantGroupingService.java +++ b/src/main/java/org/springblade/modules/martial/service/schedule/grouping/ParticipantGroupingService.java @@ -124,38 +124,70 @@ public class ParticipantGroupingService { continue; } - // For team projects, skip gender filter as validation is done at registration time - // Team athlete records do not have accurate gender info (they represent the team, not individual members) - // members = filterByCategory(members, project); - - long teamCount = members.stream() - .map(MartialAthlete::getTeamName) - .filter(org -> org != null && !org.isEmpty()) - .distinct() - .count(); - String projectName = project.getProjectName(); String categoryName = getProjectCategoryName(project); + int maxTeamsPerGroup = (project.getMaxParticipants() != null && project.getMaxParticipants() > 0) + ? project.getMaxParticipants() : members.size(); + int durationPerTeam = (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) + ? project.getEstimatedDuration() : 5; + + // Team projects: each record in members represents one team + int teamCount = members.size(); + + if (teamCount <= maxTeamsPerGroup) { + // No need to split, create single group + ScheduleGroupData group = ScheduleGroupData.builder() + .groupName(projectName + (categoryName.isEmpty() ? "" : " " + categoryName)) + .projectId(first.getProjectId()) + .projectType(2) + .maxParticipants(maxTeamsPerGroup) + .category(categoryName) + .displayOrder(displayOrder++) + .totalParticipants(members.size()) + .totalTeams(teamCount) + .athletes(members) + .estimatedDuration(teamCount * durationPerTeam) + .build(); + groups.add(group); + } else { + // Split into sub-groups like individual projects + displayOrder = splitTeamIntoSubGroups(members, project, projectName, categoryName, + maxTeamsPerGroup, durationPerTeam, groups, displayOrder); + } + } + return displayOrder; + } + + /** + * Split team project into sub-groups when team count exceeds maxParticipants + */ + private int splitTeamIntoSubGroups(List members, MartialProject project, + String projectName, String categoryName, int maxTeamsPerGroup, + int durationPerTeam, List groups, int displayOrder) { + int totalTeams = members.size(); + int numSubGroups = (int) Math.ceil((double) totalTeams / maxTeamsPerGroup); + + log.info("Team project '{}' has {} teams, splitting into {} groups (max {} per group)", + projectName + " " + categoryName, totalTeams, numSubGroups, maxTeamsPerGroup); + + for (int i = 0; i < numSubGroups; i++) { + int startIdx = i * maxTeamsPerGroup; + int endIdx = Math.min(startIdx + maxTeamsPerGroup, totalTeams); + List subGroupMembers = new ArrayList<>(members.subList(startIdx, endIdx)); + int subGroupTeamCount = subGroupMembers.size(); ScheduleGroupData group = ScheduleGroupData.builder() - .groupName(projectName + (categoryName.isEmpty() ? "" : " " + categoryName)) - .projectId(first.getProjectId()) - .projectType((project.getType() == 2 || project.getType() == 3) ? 2 : 1) - .maxParticipants(project.getMaxParticipants()) - .category(categoryName) + .groupName(projectName + " " + categoryName + " 第" + (i + 1) + "组") + .projectId(project.getId()) + .projectType(2) + .maxParticipants(maxTeamsPerGroup) .displayOrder(displayOrder++) - .totalParticipants(members.size()) - .totalTeams((int) teamCount) - .athletes(members) + .category(categoryName) + .totalParticipants(subGroupMembers.size()) + .totalTeams(subGroupTeamCount) + .athletes(subGroupMembers) + .estimatedDuration(subGroupTeamCount * durationPerTeam) .build(); - - int duration; - if (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) { - duration = (int) teamCount * project.getEstimatedDuration(); - } else { - duration = (int) teamCount * 5 + Math.max(0, (int) teamCount - 1) * 2; - } - group.setEstimatedDuration(duration); groups.add(group); } return displayOrder;