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..85b87db 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,39 +124,74 @@ 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() + // Get distinct team names + List teamNames = members.stream() .map(MartialAthlete::getTeamName) - .filter(org -> org != null && !org.isEmpty()) + .filter(t -> t != null && !t.isEmpty()) .distinct() - .count(); + .collect(Collectors.toList()); + int teamCount = teamNames.size(); String projectName = project.getProjectName(); String categoryName = getProjectCategoryName(project); - 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) - .displayOrder(displayOrder++) - .totalParticipants(members.size()) - .totalTeams((int) teamCount) - .athletes(members) - .build(); + // Calculate max teams per group (use maxParticipants as max teams) + int maxTeamsPerGroup = project.getMaxParticipants() != null && project.getMaxParticipants() > 0 + ? project.getMaxParticipants() : 10; - int duration; - if (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) { - duration = (int) teamCount * project.getEstimatedDuration(); + int durationPerTeam = (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0) + ? project.getEstimatedDuration() : 5; + + if (teamCount <= maxTeamsPerGroup) { + // No need to split - single group + 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) + .displayOrder(displayOrder++) + .totalParticipants(members.size()) + .totalTeams(teamCount) + .athletes(members) + .estimatedDuration(teamCount * durationPerTeam + Math.max(0, teamCount - 1) * 2) + .build(); + groups.add(group); } else { - duration = (int) teamCount * 5 + Math.max(0, (int) teamCount - 1) * 2; + // Split into multiple groups + int numGroups = (int) Math.ceil((double) teamCount / maxTeamsPerGroup); + + log.info("Team project {} {} has {} teams, splitting into {} groups (max {} per group)", + projectName, categoryName, teamCount, numGroups, maxTeamsPerGroup); + + for (int i = 0; i < numGroups; i++) { + int startIdx = i * maxTeamsPerGroup; + int endIdx = Math.min(startIdx + maxTeamsPerGroup, teamCount); + List subTeamNames = teamNames.subList(startIdx, endIdx); + + // Filter athletes belonging to teams in this sub-group + List subMembers = members.stream() + .filter(a -> subTeamNames.contains(a.getTeamName())) + .collect(Collectors.toList()); + + int subTeamCount = subTeamNames.size(); + + String groupSuffix = " 第" + (i + 1) + "组"; + ScheduleGroupData group = ScheduleGroupData.builder() + .groupName(projectName + " " + categoryName + groupSuffix) + .projectId(first.getProjectId()) + .projectType((project.getType() == 2 || project.getType() == 3) ? 2 : 1) + .maxParticipants(project.getMaxParticipants()) + .category(categoryName) + .displayOrder(displayOrder++) + .totalParticipants(subMembers.size()) + .totalTeams(subTeamCount) + .athletes(subMembers) + .estimatedDuration(subTeamCount * durationPerTeam + Math.max(0, subTeamCount - 1) * 2) + .build(); + groups.add(group); + } } - group.setEstimatedDuration(duration); - groups.add(group); } return displayOrder; } @@ -224,16 +259,17 @@ public class ParticipantGroupingService { int totalPeople = members.size(); int numSubGroups = (int) Math.ceil((double) totalPeople / maxPeoplePerGroup); - log.info("Individual project '{}' has {} people, splitting into {} groups (max {} per group)", - projectName + " " + categoryName, totalPeople, numSubGroups, maxPeoplePerGroup); + log.info("Individual project {} {} has {} people, splitting into {} groups (max {} per group)", + projectName, categoryName, totalPeople, numSubGroups, maxPeoplePerGroup); for (int i = 0; i < numSubGroups; i++) { int startIdx = i * maxPeoplePerGroup; int endIdx = Math.min(startIdx + maxPeoplePerGroup, totalPeople); List subGroupMembers = new ArrayList<>(members.subList(startIdx, endIdx)); + String groupSuffix = " 第" + (i + 1) + "组"; ScheduleGroupData group = ScheduleGroupData.builder() - .groupName(projectName + " " + categoryName + " 第" + (i + 1) + "组") + .groupName(projectName + " " + categoryName + groupSuffix) .projectId(project.getId()) .projectType(1) .maxParticipants(maxPeoplePerGroup)