fix: 修复generateInitialScheduleResult方法,根据maxParticipants拆分单人项目分组

- 当单人项目参赛人数超过maxParticipants时,自动拆分成多个分组
- 添加createSingleGroup辅助方法
- 修复Issue #1: 编排分组算法使用错误字段导致分组数计算不准确

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
2026-01-15 15:56:29 +08:00
co-authored by factory-droid[bot]
parent 106e563f2e
commit 868b4be356
11 changed files with 151 additions and 41 deletions
Binary file not shown.
@@ -4,6 +4,8 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.io.Serializable; import java.io.Serializable;
import java.util.List;
import java.util.Map;
/** /**
* 参赛人员DTO * 参赛人员DTO
@@ -52,4 +54,10 @@ public class ParticipantDTO implements Serializable {
@Schema(description = "选手姓名") @Schema(description = "选手姓名")
private String playerName; private String playerName;
/**
* 队员列表(集体项目)
*/
@Schema(description = "队员列表(集体项目)")
private List<Map<String, Object>> members;
} }
@@ -49,6 +49,8 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
private final MartialScheduleDetailMapper scheduleDetailMapper; private final MartialScheduleDetailMapper scheduleDetailMapper;
private final MartialScheduleParticipantMapper scheduleParticipantMapper; private final MartialScheduleParticipantMapper scheduleParticipantMapper;
private final MartialAthleteMapper athleteMapper; private final MartialAthleteMapper athleteMapper;
private final MartialTeamMapper teamMapper;
private final MartialTeamMemberMapper teamMemberMapper;
private final MartialCompetitionMapper competitionMapper; private final MartialCompetitionMapper competitionMapper;
private final MartialVenueMapper venueMapper; private final MartialVenueMapper venueMapper;
private final MartialProjectMapper projectMapper; private final MartialProjectMapper projectMapper;
@@ -213,7 +215,7 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
List<MartialScheduleParticipant> participants = scheduleParticipantMapper.selectList(participantWrapper); List<MartialScheduleParticipant> participants = scheduleParticipantMapper.selectList(participantWrapper);
if (group.getProjectType() == 2) { if (group.getProjectType() == 2) {
// 集体项目:按单位分组 // 集体项目:按单位分组,并获取队员列表
Map<String, List<MartialScheduleParticipant>> orgGroupMap = participants.stream() Map<String, List<MartialScheduleParticipant>> orgGroupMap = participants.stream()
.collect(Collectors.groupingBy(MartialScheduleParticipant::getOrganization)); .collect(Collectors.groupingBy(MartialScheduleParticipant::getOrganization));
@@ -225,7 +227,39 @@ public class MartialScheduleArrangeServiceImpl implements IMartialScheduleArrang
List<Map<String, Object>> orgParticipants = new ArrayList<>(); List<Map<String, Object>> orgParticipants = new ArrayList<>();
for (MartialScheduleParticipant p : entry.getValue()) { for (MartialScheduleParticipant p : entry.getValue()) {
Map<String, Object> pData = new HashMap<>(); Map<String, Object> pData = new HashMap<>();
pData.put("id", p.getParticipantId());
pData.put("playerName", p.getPlayerName()); pData.put("playerName", p.getPlayerName());
pData.put("schoolUnit", p.getOrganization());
// 查询队伍信息和队员列表
String teamName = p.getPlayerName();
LambdaQueryWrapper<MartialTeam> teamWrapper = new LambdaQueryWrapper<>();
teamWrapper.eq(MartialTeam::getTeamName, teamName)
.eq(MartialTeam::getIsDeleted, 0)
.last("LIMIT 1");
MartialTeam team = teamMapper.selectOne(teamWrapper);
List<Map<String, Object>> memberList = new ArrayList<>();
if (team != null) {
// 获取队员列表
LambdaQueryWrapper<MartialTeamMember> memberWrapper = new LambdaQueryWrapper<>();
memberWrapper.eq(MartialTeamMember::getTeamId, team.getId())
.eq(MartialTeamMember::getIsDeleted, 0);
List<MartialTeamMember> members = teamMemberMapper.selectList(memberWrapper);
for (MartialTeamMember member : members) {
// 获取队员详情
MartialAthlete athlete = athleteMapper.selectById(member.getAthleteId());
if (athlete != null) {
Map<String, Object> memberData = new HashMap<>();
memberData.put("id", athlete.getId());
memberData.put("playerName", athlete.getPlayerName());
memberData.put("organization", athlete.getOrganization());
memberList.add(memberData);
}
}
}
pData.put("members", memberList);
orgParticipants.add(pData); orgParticipants.add(pData);
} }
orgGroup.put("participants", orgParticipants); orgGroup.put("participants", orgParticipants);
@@ -322,9 +322,10 @@ public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMappe
Long defaultVenueId = venues.isEmpty() ? null : venues.get(0).getId(); Long defaultVenueId = venues.isEmpty() ? null : venues.get(0).getId();
String defaultVenueName = venues.isEmpty() ? "未分配" : venues.get(0).getVenueName(); String defaultVenueName = venues.isEmpty() ? "未分配" : venues.get(0).getVenueName();
// 4. 为每个项目生成一个分组 // 4. 为每个项目生成分组(单人项目根据maxParticipants拆分)
List<CompetitionGroupDTO> groupDTOs = new ArrayList<>(); List<CompetitionGroupDTO> groupDTOs = new ArrayList<>();
int displayOrder = 0; int displayOrder = 0;
long tempIdCounter = 1; // Counter for generating unique negative IDs for sub-groups
for (MartialProject project : projects) { for (MartialProject project : projects) {
List<MartialAthlete> projectAthletes = athletesByProject.getOrDefault(project.getId(), new ArrayList<>()); List<MartialAthlete> projectAthletes = athletesByProject.getOrDefault(project.getId(), new ArrayList<>());
@@ -334,47 +335,65 @@ public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMappe
continue; continue;
} }
CompetitionGroupDTO groupDTO = new CompetitionGroupDTO(); // 判断项目类型
// 使用负数ID表示这是临时生成的分组(未保存到数据库)
groupDTO.setId(-project.getId());
groupDTO.setProjectId(project.getId());
groupDTO.setTitle(project.getProjectName() + " 未分组");
groupDTO.setCode(project.getProjectCode());
// 设置类型
Integer projectType = project.getType(); Integer projectType = project.getType();
String typeStr;
if (projectType != null) { if (projectType != null) {
switch (projectType) { switch (projectType) {
case 1: case 1:
groupDTO.setType("单人"); typeStr = "单人";
break; break;
case 2: case 2:
groupDTO.setType("集体");
break;
case 3: case 3:
groupDTO.setType("集体"); typeStr = "集体";
break; break;
default: default:
groupDTO.setType("其他"); typeStr = "其他";
break; break;
} }
} else { } else {
groupDTO.setType("单人"); typeStr = "单人";
} }
// 设置数量 // 单人项目:根据maxParticipants拆分
groupDTO.setCount(projectAthletes.size() + ""); if ("".equals(typeStr)) {
Integer maxParticipants = project.getMaxParticipants();
int athleteCount = projectAthletes.size();
// 设置默认场地和时间段 // 如果maxParticipants有效且人数超过限制,需要拆分
if (maxParticipants != null && maxParticipants > 0 && athleteCount > maxParticipants) {
int numGroups = (int) Math.ceil((double) athleteCount / maxParticipants);
log.info("项目 '{}' 有{}人,maxParticipants={},将拆分成{}组",
project.getProjectName(), athleteCount, maxParticipants, numGroups);
for (int i = 0; i < numGroups; i++) {
int startIdx = i * maxParticipants;
int endIdx = Math.min(startIdx + maxParticipants, athleteCount);
List<MartialAthlete> subGroupAthletes = projectAthletes.subList(startIdx, endIdx);
CompetitionGroupDTO groupDTO = new CompetitionGroupDTO();
// 使用唯一的负数ID
groupDTO.setId(-project.getId() - tempIdCounter++);
groupDTO.setProjectId(project.getId());
// 如果只有一组,不加组号后缀
if (numGroups == 1) {
groupDTO.setTitle(project.getProjectName() + " 未分组");
} else {
groupDTO.setTitle(project.getProjectName() + "" + (i + 1) + "");
}
groupDTO.setCode("C" + competitionId + "-P" + String.format("%03d", displayOrder + 1));
groupDTO.setType(typeStr);
groupDTO.setCount(subGroupAthletes.size() + "");
groupDTO.setVenueId(defaultVenueId); groupDTO.setVenueId(defaultVenueId);
groupDTO.setVenueName(defaultVenueName); groupDTO.setVenueName(defaultVenueName);
groupDTO.setTimeSlotIndex(0); // 默认放在第一个时间段 groupDTO.setTimeSlotIndex(0);
groupDTO.setTimeSlot(null); groupDTO.setTimeSlot(null);
// 生成参赛者列表 // 生成参赛者列表
List<ParticipantDTO> participantDTOs = new ArrayList<>(); List<ParticipantDTO> participantDTOs = new ArrayList<>();
int sortOrder = 1; int sortOrder = 1;
for (MartialAthlete athlete : projectAthletes) { for (MartialAthlete athlete : subGroupAthletes) {
ParticipantDTO dto = new ParticipantDTO(); ParticipantDTO dto = new ParticipantDTO();
dto.setId(athlete.getId()); dto.setId(athlete.getId());
dto.setPlayerName(athlete.getPlayerName()); dto.setPlayerName(athlete.getPlayerName());
@@ -389,11 +408,60 @@ public class MartialScheduleServiceImpl extends ServiceImpl<MartialScheduleMappe
groupDTOs.add(groupDTO); groupDTOs.add(groupDTO);
displayOrder++; displayOrder++;
} }
} else {
// 不需要拆分,生成单个分组
CompetitionGroupDTO groupDTO = createSingleGroup(project, projectAthletes, typeStr,
defaultVenueId, defaultVenueName, competitionId, displayOrder);
groupDTOs.add(groupDTO);
displayOrder++;
}
} else {
// 集体项目:不拆分
CompetitionGroupDTO groupDTO = createSingleGroup(project, projectAthletes, typeStr,
defaultVenueId, defaultVenueName, competitionId, displayOrder);
groupDTOs.add(groupDTO);
displayOrder++;
}
}
result.setCompetitionGroups(groupDTOs); result.setCompetitionGroups(groupDTOs);
return result; return result;
} }
/**
* 创建单个分组DTO
*/
private CompetitionGroupDTO createSingleGroup(MartialProject project, List<MartialAthlete> athletes,
String typeStr, Long venueId, String venueName, Long competitionId, int displayOrder) {
CompetitionGroupDTO groupDTO = new CompetitionGroupDTO();
groupDTO.setId(-project.getId());
groupDTO.setProjectId(project.getId());
groupDTO.setTitle(project.getProjectName() + " 未分组");
groupDTO.setCode("C" + competitionId + "-P" + String.format("%03d", displayOrder + 1));
groupDTO.setType(typeStr);
groupDTO.setCount(athletes.size() + "");
groupDTO.setVenueId(venueId);
groupDTO.setVenueName(venueName);
groupDTO.setTimeSlotIndex(0);
groupDTO.setTimeSlot(null);
List<ParticipantDTO> participantDTOs = new ArrayList<>();
int sortOrder = 1;
for (MartialAthlete athlete : athletes) {
ParticipantDTO dto = new ParticipantDTO();
dto.setId(athlete.getId());
dto.setPlayerName(athlete.getPlayerName());
dto.setSchoolUnit(athlete.getOrganization());
dto.setTeamName(athlete.getTeamName());
dto.setStatus("未签到");
dto.setSortOrder(sortOrder++);
participantDTOs.add(dto);
}
groupDTO.setParticipants(participantDTOs);
return groupDTO;
}
/** /**
* 保存编排草稿 * 保存编排草稿
* *