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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.springblade.modules.martial.config.ScheduleConfig;
|
import org.springblade.modules.martial.config.ScheduleConfig;
|
||||||
import org.springblade.modules.martial.mapper.MartialProjectMapper;
|
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.MartialAthlete;
|
||||||
import org.springblade.modules.martial.pojo.entity.MartialProject;
|
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.springblade.modules.martial.service.schedule.model.ScheduleGroupData;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -15,8 +20,6 @@ import java.util.stream.Collectors;
|
|||||||
/**
|
/**
|
||||||
* Participant grouping service
|
* Participant grouping service
|
||||||
* Groups athletes by project and category for schedule arrangement
|
* Groups athletes by project and category for schedule arrangement
|
||||||
*
|
|
||||||
* @author Refactored from MartialScheduleArrangeServiceImpl
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@@ -24,11 +27,10 @@ import java.util.stream.Collectors;
|
|||||||
public class ParticipantGroupingService {
|
public class ParticipantGroupingService {
|
||||||
|
|
||||||
private final MartialProjectMapper projectMapper;
|
private final MartialProjectMapper projectMapper;
|
||||||
|
private final MartialTeamMapper teamMapper;
|
||||||
|
private final MartialTeamMemberMapper teamMemberMapper;
|
||||||
private final ScheduleConfig scheduleConfig;
|
private final ScheduleConfig scheduleConfig;
|
||||||
|
|
||||||
/**
|
|
||||||
* Auto group participants by project and category
|
|
||||||
*/
|
|
||||||
public List<ScheduleGroupData> autoGroup(List<MartialAthlete> athletes) {
|
public List<ScheduleGroupData> autoGroup(List<MartialAthlete> athletes) {
|
||||||
List<ScheduleGroupData> groups = new ArrayList<>();
|
List<ScheduleGroupData> groups = new ArrayList<>();
|
||||||
int displayOrder = 1;
|
int displayOrder = 1;
|
||||||
@@ -108,16 +110,51 @@ public class ParticipantGroupingService {
|
|||||||
.collect(Collectors.toList());
|
.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,
|
private int groupTeamAthletes(List<MartialAthlete> teamAthletes, Map<Long, MartialProject> projectMap,
|
||||||
List<ScheduleGroupData> groups, int displayOrder) {
|
List<ScheduleGroupData> groups, int displayOrder) {
|
||||||
Map<String, List<MartialAthlete>> teamGroupMap = teamAthletes.stream()
|
Map<String, List<MartialAthlete>> teamGroupMap = teamAthletes.stream()
|
||||||
.collect(Collectors.groupingBy(a -> String.valueOf(a.getProjectId())));
|
.collect(Collectors.groupingBy(a -> String.valueOf(a.getProjectId())));
|
||||||
|
|
||||||
for (Map.Entry<String, List<MartialAthlete>> entry : teamGroupMap.entrySet()) {
|
for (Map.Entry<String, List<MartialAthlete>> entry : teamGroupMap.entrySet()) {
|
||||||
List<MartialAthlete> members = entry.getValue();
|
List<MartialAthlete> teams = entry.getValue();
|
||||||
if (members.isEmpty()) continue;
|
if (teams.isEmpty()) continue;
|
||||||
|
|
||||||
MartialAthlete first = members.get(0);
|
MartialAthlete first = teams.get(0);
|
||||||
MartialProject project = projectMap.get(first.getProjectId());
|
MartialProject project = projectMap.get(first.getProjectId());
|
||||||
if (project == null) {
|
if (project == null) {
|
||||||
log.warn("Project not found, projectId: {}, skipping", first.getProjectId());
|
log.warn("Project not found, projectId: {}, skipping", first.getProjectId());
|
||||||
@@ -126,66 +163,84 @@ public class ParticipantGroupingService {
|
|||||||
|
|
||||||
String projectName = project.getProjectName();
|
String projectName = project.getProjectName();
|
||||||
String categoryName = getProjectCategoryName(project);
|
String categoryName = getProjectCategoryName(project);
|
||||||
int maxTeamsPerGroup = (project.getMaxParticipants() != null && project.getMaxParticipants() > 0)
|
int maxParticipants = (project.getMaxParticipants() != null && project.getMaxParticipants() > 0)
|
||||||
? project.getMaxParticipants() : members.size();
|
? project.getMaxParticipants() : Integer.MAX_VALUE;
|
||||||
int durationPerTeam = (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0)
|
int durationPerTeam = (project.getEstimatedDuration() != null && project.getEstimatedDuration() > 0)
|
||||||
? project.getEstimatedDuration() : 5;
|
? project.getEstimatedDuration() : 5;
|
||||||
|
|
||||||
// Team projects: each record in members represents one team
|
int totalMembers = calculateTotalMembers(teams);
|
||||||
int teamCount = members.size();
|
int teamCount = teams.size();
|
||||||
|
|
||||||
if (teamCount <= maxTeamsPerGroup) {
|
log.info("Team project '{}': {} teams, {} total members, venue capacity: {}",
|
||||||
// No need to split, create single group
|
projectName + " " + categoryName, teamCount, totalMembers, maxParticipants);
|
||||||
|
|
||||||
|
if (totalMembers <= maxParticipants) {
|
||||||
ScheduleGroupData group = ScheduleGroupData.builder()
|
ScheduleGroupData group = ScheduleGroupData.builder()
|
||||||
.groupName(projectName + (categoryName.isEmpty() ? "" : " " + categoryName))
|
.groupName(projectName + (categoryName.isEmpty() ? "" : " " + categoryName))
|
||||||
.projectId(first.getProjectId())
|
.projectId(first.getProjectId())
|
||||||
.projectType(2)
|
.projectType(2)
|
||||||
.maxParticipants(maxTeamsPerGroup)
|
.maxParticipants(maxParticipants)
|
||||||
.category(categoryName)
|
.category(categoryName)
|
||||||
.displayOrder(displayOrder++)
|
.displayOrder(displayOrder++)
|
||||||
.totalParticipants(members.size())
|
.totalParticipants(totalMembers)
|
||||||
.totalTeams(teamCount)
|
.totalTeams(teamCount)
|
||||||
.athletes(members)
|
.athletes(teams)
|
||||||
.estimatedDuration(teamCount * durationPerTeam)
|
.estimatedDuration(teamCount * durationPerTeam)
|
||||||
.build();
|
.build();
|
||||||
groups.add(group);
|
groups.add(group);
|
||||||
} else {
|
} else {
|
||||||
// Split into sub-groups like individual projects
|
displayOrder = splitTeamsByMemberCount(teams, project, projectName, categoryName,
|
||||||
displayOrder = splitTeamIntoSubGroups(members, project, projectName, categoryName,
|
maxParticipants, durationPerTeam, groups, displayOrder);
|
||||||
maxTeamsPerGroup, durationPerTeam, groups, displayOrder);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 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,
|
private int splitTeamsByMemberCount(List<MartialAthlete> teams, MartialProject project,
|
||||||
String projectName, String categoryName, int maxTeamsPerGroup,
|
String projectName, String categoryName, int maxParticipants,
|
||||||
int durationPerTeam, List<ScheduleGroupData> groups, int displayOrder) {
|
int durationPerTeam, List<ScheduleGroupData> groups, int displayOrder) {
|
||||||
int totalTeams = members.size();
|
List<List<MartialAthlete>> subGroups = new ArrayList<>();
|
||||||
int numSubGroups = (int) Math.ceil((double) totalTeams / maxTeamsPerGroup);
|
List<MartialAthlete> currentGroup = new ArrayList<>();
|
||||||
|
int currentMemberCount = 0;
|
||||||
|
|
||||||
log.info("Team project '{}' has {} teams, splitting into {} groups (max {} per group)",
|
for (MartialAthlete team : teams) {
|
||||||
projectName + " " + categoryName, totalTeams, numSubGroups, maxTeamsPerGroup);
|
int teamMemberCount = getTeamMemberCount(team.getPlayerName());
|
||||||
|
|
||||||
for (int i = 0; i < numSubGroups; i++) {
|
if (currentMemberCount + teamMemberCount > maxParticipants && !currentGroup.isEmpty()) {
|
||||||
int startIdx = i * maxTeamsPerGroup;
|
subGroups.add(new ArrayList<>(currentGroup));
|
||||||
int endIdx = Math.min(startIdx + maxTeamsPerGroup, totalTeams);
|
currentGroup.clear();
|
||||||
List<MartialAthlete> subGroupMembers = new ArrayList<>(members.subList(startIdx, endIdx));
|
currentMemberCount = 0;
|
||||||
int subGroupTeamCount = subGroupMembers.size();
|
}
|
||||||
|
|
||||||
|
currentGroup.add(team);
|
||||||
|
currentMemberCount += teamMemberCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentGroup.isEmpty()) {
|
||||||
|
subGroups.add(currentGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
ScheduleGroupData group = ScheduleGroupData.builder()
|
||||||
.groupName(projectName + " " + categoryName + " 第" + (i + 1) + "组")
|
.groupName(projectName + " " + categoryName + " 第" + (i + 1) + "组")
|
||||||
.projectId(project.getId())
|
.projectId(project.getId())
|
||||||
.projectType(2)
|
.projectType(2)
|
||||||
.maxParticipants(maxTeamsPerGroup)
|
.maxParticipants(maxParticipants)
|
||||||
.displayOrder(displayOrder++)
|
.displayOrder(displayOrder++)
|
||||||
.category(categoryName)
|
.category(categoryName)
|
||||||
.totalParticipants(subGroupMembers.size())
|
.totalParticipants(subGroupMemberCount)
|
||||||
.totalTeams(subGroupTeamCount)
|
.totalTeams(subGroupTeamCount)
|
||||||
.athletes(subGroupMembers)
|
.athletes(subGroupTeams)
|
||||||
.estimatedDuration(subGroupTeamCount * durationPerTeam)
|
.estimatedDuration(subGroupTeamCount * durationPerTeam)
|
||||||
.build();
|
.build();
|
||||||
groups.add(group);
|
groups.add(group);
|
||||||
|
|||||||
Reference in New Issue
Block a user