feat: 集体项目按总成员数拆分而非队伍数
- 修改 ParticipantGroupingService 按场地容量(maxParticipants)判断拆分 - 当所有队伍的总成员数超过场地容量时自动拆分成多组 - 新增 getTeamMemberCount 和 calculateTotalMembers 方法 Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
+91
-36
@@ -4,10 +4,15 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.modules.martial.config.ScheduleConfig;
|
||||
import org.springblade.modules.martial.mapper.MartialProjectMapper;
|
||||
import org.springblade.modules.martial.mapper.MartialTeamMapper;
|
||||
import org.springblade.modules.martial.mapper.MartialTeamMemberMapper;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialAthlete;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialProject;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialTeam;
|
||||
import org.springblade.modules.martial.pojo.entity.MartialTeamMember;
|
||||
import org.springblade.modules.martial.service.schedule.model.ScheduleGroupData;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -15,8 +20,6 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* Participant grouping service
|
||||
* Groups athletes by project and category for schedule arrangement
|
||||
*
|
||||
* @author Refactored from MartialScheduleArrangeServiceImpl
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -24,11 +27,10 @@ import java.util.stream.Collectors;
|
||||
public class ParticipantGroupingService {
|
||||
|
||||
private final MartialProjectMapper projectMapper;
|
||||
private final MartialTeamMapper teamMapper;
|
||||
private final MartialTeamMemberMapper teamMemberMapper;
|
||||
private final ScheduleConfig scheduleConfig;
|
||||
|
||||
/**
|
||||
* Auto group participants by project and category
|
||||
*/
|
||||
public List<ScheduleGroupData> autoGroup(List<MartialAthlete> athletes) {
|
||||
List<ScheduleGroupData> groups = new ArrayList<>();
|
||||
int displayOrder = 1;
|
||||
@@ -108,16 +110,51 @@ public class ParticipantGroupingService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get member count for a team by team name
|
||||
*/
|
||||
private int getTeamMemberCount(String teamName) {
|
||||
if (teamName == null || teamName.isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
MartialTeam team = teamMapper.selectOne(
|
||||
new LambdaQueryWrapper<MartialTeam>()
|
||||
.eq(MartialTeam::getTeamName, teamName)
|
||||
.eq(MartialTeam::getIsDeleted, 0)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (team == null) {
|
||||
return 1;
|
||||
}
|
||||
Long count = teamMemberMapper.selectCount(
|
||||
new LambdaQueryWrapper<MartialTeamMember>()
|
||||
.eq(MartialTeamMember::getTeamId, team.getId())
|
||||
.eq(MartialTeamMember::getIsDeleted, 0)
|
||||
);
|
||||
return count != null && count > 0 ? count.intValue() : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate total members across all teams
|
||||
*/
|
||||
private int calculateTotalMembers(List<MartialAthlete> teams) {
|
||||
int total = 0;
|
||||
for (MartialAthlete team : teams) {
|
||||
total += getTeamMemberCount(team.getPlayerName());
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private int groupTeamAthletes(List<MartialAthlete> teamAthletes, Map<Long, MartialProject> projectMap,
|
||||
List<ScheduleGroupData> groups, int displayOrder) {
|
||||
Map<String, List<MartialAthlete>> teamGroupMap = teamAthletes.stream()
|
||||
.collect(Collectors.groupingBy(a -> String.valueOf(a.getProjectId())));
|
||||
|
||||
for (Map.Entry<String, List<MartialAthlete>> entry : teamGroupMap.entrySet()) {
|
||||
List<MartialAthlete> members = entry.getValue();
|
||||
if (members.isEmpty()) continue;
|
||||
List<MartialAthlete> teams = entry.getValue();
|
||||
if (teams.isEmpty()) continue;
|
||||
|
||||
MartialAthlete first = members.get(0);
|
||||
MartialAthlete first = teams.get(0);
|
||||
MartialProject project = projectMap.get(first.getProjectId());
|
||||
if (project == null) {
|
||||
log.warn("Project not found, projectId: {}, skipping", first.getProjectId());
|
||||
@@ -126,66 +163,84 @@ public class ParticipantGroupingService {
|
||||
|
||||
String projectName = project.getProjectName();
|
||||
String categoryName = getProjectCategoryName(project);
|
||||
int maxTeamsPerGroup = (project.getMaxParticipants() != null && project.getMaxParticipants() > 0)
|
||||
? project.getMaxParticipants() : members.size();
|
||||
int maxParticipants = (project.getMaxParticipants() != null && project.getMaxParticipants() > 0)
|
||||
? project.getMaxParticipants() : Integer.MAX_VALUE;
|
||||
int durationPerTeam = (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0)
|
||||
? project.getEstimatedDuration() : 5;
|
||||
|
||||
// Team projects: each record in members represents one team
|
||||
int teamCount = members.size();
|
||||
int totalMembers = calculateTotalMembers(teams);
|
||||
int teamCount = teams.size();
|
||||
|
||||
if (teamCount <= maxTeamsPerGroup) {
|
||||
// No need to split, create single group
|
||||
log.info("Team project '{}': {} teams, {} total members, venue capacity: {}",
|
||||
projectName + " " + categoryName, teamCount, totalMembers, maxParticipants);
|
||||
|
||||
if (totalMembers <= maxParticipants) {
|
||||
ScheduleGroupData group = ScheduleGroupData.builder()
|
||||
.groupName(projectName + (categoryName.isEmpty() ? "" : " " + categoryName))
|
||||
.projectId(first.getProjectId())
|
||||
.projectType(2)
|
||||
.maxParticipants(maxTeamsPerGroup)
|
||||
.maxParticipants(maxParticipants)
|
||||
.category(categoryName)
|
||||
.displayOrder(displayOrder++)
|
||||
.totalParticipants(members.size())
|
||||
.totalParticipants(totalMembers)
|
||||
.totalTeams(teamCount)
|
||||
.athletes(members)
|
||||
.athletes(teams)
|
||||
.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);
|
||||
displayOrder = splitTeamsByMemberCount(teams, project, projectName, categoryName,
|
||||
maxParticipants, durationPerTeam, groups, displayOrder);
|
||||
}
|
||||
}
|
||||
return displayOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split team project into sub-groups when team count exceeds maxParticipants
|
||||
* Split teams into sub-groups based on total member count and venue capacity
|
||||
*/
|
||||
private int splitTeamIntoSubGroups(List<MartialAthlete> members, MartialProject project,
|
||||
String projectName, String categoryName, int maxTeamsPerGroup,
|
||||
int durationPerTeam, List<ScheduleGroupData> groups, int displayOrder) {
|
||||
int totalTeams = members.size();
|
||||
int numSubGroups = (int) Math.ceil((double) totalTeams / maxTeamsPerGroup);
|
||||
private int splitTeamsByMemberCount(List<MartialAthlete> teams, MartialProject project,
|
||||
String projectName, String categoryName, int maxParticipants,
|
||||
int durationPerTeam, List<ScheduleGroupData> groups, int displayOrder) {
|
||||
List<List<MartialAthlete>> subGroups = new ArrayList<>();
|
||||
List<MartialAthlete> currentGroup = new ArrayList<>();
|
||||
int currentMemberCount = 0;
|
||||
|
||||
log.info("Team project '{}' has {} teams, splitting into {} groups (max {} per group)",
|
||||
projectName + " " + categoryName, totalTeams, numSubGroups, maxTeamsPerGroup);
|
||||
for (MartialAthlete team : teams) {
|
||||
int teamMemberCount = getTeamMemberCount(team.getPlayerName());
|
||||
|
||||
if (currentMemberCount + teamMemberCount > maxParticipants && !currentGroup.isEmpty()) {
|
||||
subGroups.add(new ArrayList<>(currentGroup));
|
||||
currentGroup.clear();
|
||||
currentMemberCount = 0;
|
||||
}
|
||||
|
||||
currentGroup.add(team);
|
||||
currentMemberCount += teamMemberCount;
|
||||
}
|
||||
|
||||
if (!currentGroup.isEmpty()) {
|
||||
subGroups.add(currentGroup);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numSubGroups; i++) {
|
||||
int startIdx = i * maxTeamsPerGroup;
|
||||
int endIdx = Math.min(startIdx + maxTeamsPerGroup, totalTeams);
|
||||
List<MartialAthlete> subGroupMembers = new ArrayList<>(members.subList(startIdx, endIdx));
|
||||
int subGroupTeamCount = subGroupMembers.size();
|
||||
log.info("Team project '{}' split into {} groups based on venue capacity {}",
|
||||
projectName + " " + categoryName, subGroups.size(), maxParticipants);
|
||||
|
||||
for (int i = 0; i < subGroups.size(); i++) {
|
||||
List<MartialAthlete> subGroupTeams = subGroups.get(i);
|
||||
int subGroupMemberCount = calculateTotalMembers(subGroupTeams);
|
||||
int subGroupTeamCount = subGroupTeams.size();
|
||||
|
||||
ScheduleGroupData group = ScheduleGroupData.builder()
|
||||
.groupName(projectName + " " + categoryName + " 第" + (i + 1) + "组")
|
||||
.projectId(project.getId())
|
||||
.projectType(2)
|
||||
.maxParticipants(maxTeamsPerGroup)
|
||||
.maxParticipants(maxParticipants)
|
||||
.displayOrder(displayOrder++)
|
||||
.category(categoryName)
|
||||
.totalParticipants(subGroupMembers.size())
|
||||
.totalParticipants(subGroupMemberCount)
|
||||
.totalTeams(subGroupTeamCount)
|
||||
.athletes(subGroupMembers)
|
||||
.athletes(subGroupTeams)
|
||||
.estimatedDuration(subGroupTeamCount * durationPerTeam)
|
||||
.build();
|
||||
groups.add(group);
|
||||
|
||||
Reference in New Issue
Block a user