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:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-1
@@ -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);
|
||||||
|
|||||||
+108
-40
@@ -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,66 +335,133 @@ 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();
|
||||||
groupDTO.setVenueId(defaultVenueId);
|
|
||||||
groupDTO.setVenueName(defaultVenueName);
|
// 如果maxParticipants有效且人数超过限制,需要拆分
|
||||||
groupDTO.setTimeSlotIndex(0); // 默认放在第一个时间段
|
if (maxParticipants != null && maxParticipants > 0 && athleteCount > maxParticipants) {
|
||||||
groupDTO.setTimeSlot(null);
|
int numGroups = (int) Math.ceil((double) athleteCount / maxParticipants);
|
||||||
|
log.info("项目 '{}' 有{}人,maxParticipants={},将拆分成{}组",
|
||||||
// 生成参赛者列表
|
project.getProjectName(), athleteCount, maxParticipants, numGroups);
|
||||||
List<ParticipantDTO> participantDTOs = new ArrayList<>();
|
|
||||||
int sortOrder = 1;
|
for (int i = 0; i < numGroups; i++) {
|
||||||
for (MartialAthlete athlete : projectAthletes) {
|
int startIdx = i * maxParticipants;
|
||||||
ParticipantDTO dto = new ParticipantDTO();
|
int endIdx = Math.min(startIdx + maxParticipants, athleteCount);
|
||||||
dto.setId(athlete.getId());
|
List<MartialAthlete> subGroupAthletes = projectAthletes.subList(startIdx, endIdx);
|
||||||
dto.setPlayerName(athlete.getPlayerName());
|
|
||||||
dto.setSchoolUnit(athlete.getOrganization());
|
CompetitionGroupDTO groupDTO = new CompetitionGroupDTO();
|
||||||
dto.setTeamName(athlete.getTeamName());
|
// 使用唯一的负数ID
|
||||||
dto.setStatus("未签到");
|
groupDTO.setId(-project.getId() - tempIdCounter++);
|
||||||
dto.setSortOrder(sortOrder++);
|
groupDTO.setProjectId(project.getId());
|
||||||
participantDTOs.add(dto);
|
|
||||||
|
// 如果只有一组,不加组号后缀
|
||||||
|
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.setVenueName(defaultVenueName);
|
||||||
|
groupDTO.setTimeSlotIndex(0);
|
||||||
|
groupDTO.setTimeSlot(null);
|
||||||
|
|
||||||
|
// 生成参赛者列表
|
||||||
|
List<ParticipantDTO> participantDTOs = new ArrayList<>();
|
||||||
|
int sortOrder = 1;
|
||||||
|
for (MartialAthlete athlete : subGroupAthletes) {
|
||||||
|
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);
|
||||||
|
groupDTOs.add(groupDTO);
|
||||||
|
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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
groupDTO.setParticipants(participantDTOs);
|
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存编排草稿
|
* 保存编排草稿
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user