|
|
@@ -531,7 +531,8 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
}
|
|
|
|
|
|
// 只有明确表示"选择"的值才返回true
|
|
|
- return value.equals("是") || value.equals("yes") || value.equals("true") || value.equals("1") || value.contains("✔") || value.contains("✅") || value.contains("√");
|
|
|
+ return value.equals("是") || value.equals("yes") || value.equals("true") || value.equals("1")
|
|
|
+ || value.contains("✔") || value.contains("✅") || value.contains("√");
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -638,6 +639,14 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
Map<String, Long> existingAthleteMap = existingAthletes.stream()
|
|
|
.collect(Collectors.toMap(GameAthlete::getAthleteCode, GameAthlete::getAthleteId, (v1, v2) -> v1));
|
|
|
|
|
|
+ // 1.5 预加载项目分类信息,用于区分个人/团体项目
|
|
|
+ List<GameEventProject> allProjectConfigs = gameEventProjectMapper
|
|
|
+ .selectList(new LambdaQueryWrapper<GameEventProject>()
|
|
|
+ .select(GameEventProject::getProjectId, GameEventProject::getClassification)
|
|
|
+ .eq(GameEventProject::getEventId, eventId));
|
|
|
+ Map<Long, String> projectIdToClassification = allProjectConfigs.stream()
|
|
|
+ .collect(Collectors.toMap(GameEventProject::getProjectId, GameEventProject::getClassification));
|
|
|
+
|
|
|
// 2. 处理每个队伍
|
|
|
for (Map.Entry<String, List<EnrollProjectVo>> entry : groupedByTeam.entrySet()) {
|
|
|
String teamName = entry.getKey();
|
|
|
@@ -697,6 +706,20 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 3.5 汇总该队伍在本次导入中所选的【团体项目】
|
|
|
+ Set<Long> teamProjectIds = new HashSet<>();
|
|
|
+ for (EnrollProjectVo athlete : athletes) {
|
|
|
+ for (Map.Entry<String, Boolean> pEntry : athlete.getProjectSelections().entrySet()) {
|
|
|
+ if (pEntry.getValue()) {
|
|
|
+ Long pId = projectList.get(pEntry.getKey());
|
|
|
+ // 只保存团体项目(Classification = '1')
|
|
|
+ if (pId != null && "1".equals(projectIdToClassification.get(pId))) {
|
|
|
+ teamProjectIds.add(pId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 4. 处理队伍信息(内存合并,最后批量提交)
|
|
|
if (isNewTeam) {
|
|
|
// 构建新队伍
|
|
|
@@ -707,6 +730,7 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
team.setLeader(athletes.get(0).getLeader());
|
|
|
team.setAthleteValue(JSONUtil.toJsonStr(athletesId));
|
|
|
team.setAthleteNum(athletes.size());
|
|
|
+ team.setProjectValue(JSONUtil.toJsonStr(teamProjectIds));
|
|
|
team.setNumberRange(numberRange);
|
|
|
team.setStatus("0");
|
|
|
team.setCreateTime(DateTime.now());
|
|
|
@@ -715,6 +739,18 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
// 合并现有队伍信息
|
|
|
existingTeam.setNumberRange(numberRange);
|
|
|
|
|
|
+ // 合并项目列表
|
|
|
+ Set<Long> existingProjectIds = new HashSet<>();
|
|
|
+ if (StringUtils.isNotBlank(existingTeam.getProjectValue())) {
|
|
|
+ try {
|
|
|
+ existingProjectIds.addAll(JSONUtil.toList(existingTeam.getProjectValue(), Long.class));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("解析队伍{}的原有项目ID失败", teamName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ existingProjectIds.addAll(teamProjectIds);
|
|
|
+ existingTeam.setProjectValue(JSONUtil.toJsonStr(existingProjectIds));
|
|
|
+
|
|
|
// 合并运动员 ID 列表
|
|
|
List<Long> currentAthleteIds = new ArrayList<>();
|
|
|
if (StringUtils.isNotBlank(existingTeam.getAthleteValue())) {
|
|
|
@@ -1076,7 +1112,6 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
return gameAthleteBo;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* 验证号码段格式是否有效
|
|
|
* 号码段格式为:xxxx-yyyy(如:0001-0300)
|
|
|
@@ -1125,7 +1160,7 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
.map(EnrollProjectVo::getAthleteCode)
|
|
|
.filter(StringUtils::isNotBlank)
|
|
|
.map(String::trim)
|
|
|
- .filter(code -> StringUtils.isNotBlank(code))
|
|
|
+ .filter(StringUtils::isNotBlank)
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
if (!providedCodes.isEmpty()) {
|
|
|
@@ -1155,7 +1190,7 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
String[] rangeParts = numberRange.split("-");
|
|
|
if (rangeParts.length == 2) {
|
|
|
try {
|
|
|
- int startNumber = Integer.valueOf(rangeParts[0]);
|
|
|
+ int startNumber = Integer.parseInt(rangeParts[0]);
|
|
|
return new AtomicInteger(startNumber);
|
|
|
} catch (NumberFormatException e) {
|
|
|
log.warn("解析号码段起始号码失败: {}, 使用默认值", numberRange);
|
|
|
@@ -1188,12 +1223,12 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
try {
|
|
|
// 查询队伍的最大队员编号
|
|
|
String maxNumberStr = gameAthleteService.queryMaxNumber(teamId);
|
|
|
- Integer maxNumber = Integer.valueOf(maxNumberStr);
|
|
|
+ int maxNumber = Integer.parseInt(maxNumberStr);
|
|
|
|
|
|
// 如果最大编号为0,从号码段起始开始
|
|
|
if (maxNumber == 0) {
|
|
|
String[] rangeParts = numberRange.split("-");
|
|
|
- int startNumber = Integer.valueOf(rangeParts[0]);
|
|
|
+ int startNumber = Integer.parseInt(rangeParts[0]);
|
|
|
return new AtomicInteger(startNumber);
|
|
|
} else {
|
|
|
// 续着分配
|
|
|
@@ -1202,29 +1237,11 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
} catch (Exception e) {
|
|
|
log.warn("查询队伍{}的号码失败: {},从号码段起始开始", teamName, numberRange, e);
|
|
|
String[] rangeParts = numberRange.split("-");
|
|
|
- int startNumber = Integer.valueOf(rangeParts[0]);
|
|
|
+ int startNumber = Integer.parseInt(rangeParts[0]);
|
|
|
return new AtomicInteger(startNumber);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 检查号码是否已存在
|
|
|
- *
|
|
|
- * @param athleteCode 运动员号码
|
|
|
- * @param eventId 赛事ID
|
|
|
- * @return 是否已存在
|
|
|
- */
|
|
|
- private boolean isAthleteCodeExists(String athleteCode, Long eventId) {
|
|
|
- if (StringUtils.isBlank(athleteCode)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- Long count = gameAthleteMapper.selectCount(new LambdaQueryWrapper<GameAthlete>()
|
|
|
- .eq(GameAthlete::getEventId, eventId)
|
|
|
- .eq(GameAthlete::getAthleteCode, athleteCode));
|
|
|
- return count > 0;
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 校验基本数据完整性
|
|
|
*/
|
|
|
@@ -1295,23 +1312,25 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
Integer maxProjectsPerPerson = (event != null) ? event.getLimitApplication() : 0;
|
|
|
|
|
|
// 2. 获取项目限制信息:仅查询必要的核心字段
|
|
|
- List<GameEventProject> projects = gameEventProjectMapper.selectList(new LambdaQueryWrapper<GameEventProject>()
|
|
|
- .select(GameEventProject::getProjectId, GameEventProject::getProjectName,
|
|
|
- GameEventProject::getLimitMale, GameEventProject::getLimitFemale,
|
|
|
- GameEventProject::getLimitTeam, GameEventProject::getClassification)
|
|
|
- .eq(GameEventProject::getEventId, eventId));
|
|
|
+ List<GameEventProject> projects = gameEventProjectMapper
|
|
|
+ .selectList(new LambdaQueryWrapper<GameEventProject>()
|
|
|
+ .select(GameEventProject::getProjectId, GameEventProject::getProjectName,
|
|
|
+ GameEventProject::getLimitMale, GameEventProject::getLimitFemale,
|
|
|
+ GameEventProject::getLimitTeam, GameEventProject::getClassification)
|
|
|
+ .eq(GameEventProject::getEventId, eventId));
|
|
|
|
|
|
// 提前构建项目 ID -> 对象缓存 (用于内部统计方法)
|
|
|
Map<Long, GameEventProject> projectIdMap = projects.stream()
|
|
|
- .collect(Collectors.toMap(GameEventProject::getProjectId, Function.identity()));
|
|
|
+ .collect(Collectors.toMap(GameEventProject::getProjectId, Function.identity()));
|
|
|
// 构建项目名 -> 对象缓存 (供 validateAthlete 逻辑兼容使用)
|
|
|
Map<String, GameEventProjectVo> projectLimits = projects.stream()
|
|
|
- .map(p -> BeanUtil.copyProperties(p, GameEventProjectVo.class))
|
|
|
- .collect(Collectors.toMap(GameEventProjectVo::getProjectName, Function.identity()));
|
|
|
+ .map(p -> BeanUtil.copyProperties(p, GameEventProjectVo.class))
|
|
|
+ .collect(Collectors.toMap(GameEventProjectVo::getProjectName, Function.identity()));
|
|
|
|
|
|
// 3. 预加载所有参与校验的数据:仅查询统计所需的最小字段集
|
|
|
- List<GameAthlete> allAthletes = gameAthleteMapper.selectList(new LambdaQueryWrapper<GameAthlete>()
|
|
|
- .select(GameAthlete::getGender, GameAthlete::getProjectValue, GameAthlete::getTeamId)
|
|
|
+ List<GameAthlete> allAthletesFromDb = gameAthleteMapper.selectList(new LambdaQueryWrapper<GameAthlete>()
|
|
|
+ .select(GameAthlete::getGender, GameAthlete::getProjectValue, GameAthlete::getTeamId,
|
|
|
+ GameAthlete::getAthleteCode)
|
|
|
.eq(GameAthlete::getEventId, eventId));
|
|
|
|
|
|
List<GameTeam> allTeams = gameTeamMapper.selectList(new LambdaQueryWrapper<GameTeam>()
|
|
|
@@ -1320,13 +1339,31 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
|
|
|
// 构建队伍 ID -> 名称的映射,供运动员统计使用
|
|
|
Map<Long, String> teamIdToNameMap = allTeams.stream()
|
|
|
- .filter(t -> t.getTeamId() != null)
|
|
|
- .collect(Collectors.toMap(GameTeam::getTeamId, GameTeam::getTeamName));
|
|
|
-
|
|
|
- // 4. 统计状态(使用精简后的数据在内存中计算)
|
|
|
- Map<String, Map<String, Integer>> projectGenderCounts = calculateProjectGenderCounts(allAthletes, projectIdMap);
|
|
|
- Map<String, Set<String>> projectTeams = calculateProjectTeams(allTeams, projectIdMap);
|
|
|
- Map<String, Map<String, Map<String, Integer>>> teamProjectGenderCounts = calculateTeamProjectGenderCounts(allAthletes, projectIdMap, teamIdToNameMap);
|
|
|
+ .filter(t -> t.getTeamId() != null)
|
|
|
+ .collect(Collectors.toMap(GameTeam::getTeamId, GameTeam::getTeamName));
|
|
|
+
|
|
|
+ // --- 核心优化:实现覆盖式校验统计 ---
|
|
|
+ // 获取导入列表中所有的运动员编号
|
|
|
+ Set<String> importAthleteCodes = enrollList.stream()
|
|
|
+ .map(EnrollProjectVo::getAthleteCode)
|
|
|
+ .filter(StringUtils::isNotBlank)
|
|
|
+ .collect(Collectors.toSet());
|
|
|
+
|
|
|
+ // 初始统计时,排除掉这些即将在导入中被覆盖的运动员
|
|
|
+ List<GameAthlete> initialStatAthletes = allAthletesFromDb.stream()
|
|
|
+ .filter(a -> !importAthleteCodes.contains(a.getAthleteCode()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 初始统计时,暂时保留所有队伍(因为队伍是合并逻辑,后面会处理)
|
|
|
+ // ---------------------------------
|
|
|
+
|
|
|
+ // 4. 统计状态(使用排除后的数据在内存中计算,此时统计的是“除导入列表外”的存量数据)
|
|
|
+ Map<String, Map<String, Integer>> projectGenderCounts = calculateProjectGenderCounts(initialStatAthletes,
|
|
|
+ projectIdMap);
|
|
|
+ Map<String, Set<String>> projectTeams = calculateProjectTeams(initialStatAthletes, projectIdMap,
|
|
|
+ teamIdToNameMap);
|
|
|
+ Map<String, Map<String, Map<String, Integer>>> teamProjectGenderCounts = calculateTeamProjectGenderCounts(
|
|
|
+ initialStatAthletes, projectIdMap, teamIdToNameMap);
|
|
|
|
|
|
// 4. 校验每个运动员
|
|
|
for (int i = 0; i < enrollList.size(); i++) {
|
|
|
@@ -1389,11 +1426,11 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* 内存统计项目报名人数和性别
|
|
|
*/
|
|
|
- private Map<String, Map<String, Integer>> calculateProjectGenderCounts(List<GameAthlete> athletes, Map<Long, GameEventProject> projectMap) {
|
|
|
+ private Map<String, Map<String, Integer>> calculateProjectGenderCounts(List<GameAthlete> athletes,
|
|
|
+ Map<Long, GameEventProject> projectMap) {
|
|
|
Map<String, Map<String, Integer>> counts = new HashMap<>();
|
|
|
for (GameAthlete athlete : athletes) {
|
|
|
if (StringUtils.isNotBlank(athlete.getProjectValue())) {
|
|
|
@@ -1401,7 +1438,8 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
for (Long projectId : projectIds) {
|
|
|
GameEventProject project = projectMap.get(projectId);
|
|
|
if (project != null) {
|
|
|
- Map<String, Integer> genderMap = counts.computeIfAbsent(project.getProjectName(), k -> new HashMap<>());
|
|
|
+ Map<String, Integer> genderMap = counts.computeIfAbsent(project.getProjectName(),
|
|
|
+ k -> new HashMap<>());
|
|
|
genderMap.merge(athlete.getGender(), 1, Integer::sum);
|
|
|
genderMap.merge("TOTAL", 1, Integer::sum);
|
|
|
}
|
|
|
@@ -1412,18 +1450,21 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 内存统计项目已报名的队伍列表
|
|
|
+ * 内存统计项目已报名的队伍列表(通过运动员数据汇总,确保绝对准确)
|
|
|
*/
|
|
|
- private Map<String, Set<String>> calculateProjectTeams(List<GameTeam> teams, Map<Long, GameEventProject> projectMap) {
|
|
|
+ private Map<String, Set<String>> calculateProjectTeams(List<GameAthlete> athletes,
|
|
|
+ Map<Long, GameEventProject> projectMap, Map<Long, String> teamIdToNameMap) {
|
|
|
Map<String, Set<String>> projectTeams = new HashMap<>();
|
|
|
- for (GameTeam team : teams) {
|
|
|
- if (StringUtils.isNotBlank(team.getProjectValue())) {
|
|
|
- List<Long> projectIds = JSONUtil.toList(team.getProjectValue(), Long.class);
|
|
|
+ for (GameAthlete athlete : athletes) {
|
|
|
+ String teamName = teamIdToNameMap.get(athlete.getTeamId());
|
|
|
+ if (StringUtils.isNotBlank(athlete.getProjectValue()) && StringUtils.isNotBlank(teamName)) {
|
|
|
+ List<Long> projectIds = JSONUtil.toList(athlete.getProjectValue(), Long.class);
|
|
|
for (Long projectId : projectIds) {
|
|
|
GameEventProject project = projectMap.get(projectId);
|
|
|
- if (project != null) {
|
|
|
+ // 只有团体项目(Classification = '1')才将队伍计入项目参与名单
|
|
|
+ if (project != null && "1".equals(project.getClassification())) {
|
|
|
projectTeams.computeIfAbsent(project.getProjectName(), k -> new HashSet<>())
|
|
|
- .add(team.getTeamName());
|
|
|
+ .add(teamName);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1444,7 +1485,8 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
for (Long projectId : projectIds) {
|
|
|
GameEventProject project = projectMap.get(projectId);
|
|
|
if (project != null) {
|
|
|
- Map<String, Map<String, Integer>> teamMap = counts.computeIfAbsent(project.getProjectName(), k -> new HashMap<>());
|
|
|
+ Map<String, Map<String, Integer>> teamMap = counts.computeIfAbsent(project.getProjectName(),
|
|
|
+ k -> new HashMap<>());
|
|
|
Map<String, Integer> genderMap = teamMap.computeIfAbsent(teamName, k -> new HashMap<>());
|
|
|
genderMap.merge(athlete.getGender(), 1, Integer::sum);
|
|
|
}
|
|
|
@@ -1466,19 +1508,6 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
|
|
|
List<EnrollValidationError> errors = new ArrayList<>();
|
|
|
|
|
|
- // 号码冲突检查
|
|
|
- if (StringUtils.isNotBlank(enroll.getAthleteCode())) {
|
|
|
- if (isAthleteCodeExists(enroll.getAthleteCode(), eventId)) {
|
|
|
- EnrollValidationError error = new EnrollValidationError();
|
|
|
- error.setRowIndex(rowIndex + 1);
|
|
|
- error.setAthleteName(enroll.getName());
|
|
|
- error.setTeamName(enroll.getTeamName());
|
|
|
- error.setErrorType("DUPLICATE_ATHLETE_CODE");
|
|
|
- error.setErrorMessage(String.format("运动员号码'%s'已存在", enroll.getAthleteCode()));
|
|
|
- errors.add(error);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
// 检查项目选择数据是否为空
|
|
|
if (enroll.getProjectSelections() == null || enroll.getProjectSelections().isEmpty()) {
|
|
|
EnrollValidationError error = new EnrollValidationError();
|
|
|
@@ -1520,7 +1549,8 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
int current = genderCounts.getOrDefault("1", 0);
|
|
|
if (current >= project.getLimitMale()) {
|
|
|
errors.add(createError(rowIndex, enroll, "GENDER_LIMIT_EXCEEDED", projectName,
|
|
|
- String.format("项目'%s'男生限报人数:%d,当前报名人数已满", projectName, project.getLimitMale())));
|
|
|
+ String.format("项目'%s'男生限报人数:%d,当前报名人数已满", projectName,
|
|
|
+ project.getLimitMale())));
|
|
|
}
|
|
|
}
|
|
|
} else if ("2".equals(gender)) { // 女
|
|
|
@@ -1542,7 +1572,8 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
if (project.getLimitTeam() != null && project.getLimitTeam() > 0) {
|
|
|
if (teams.size() >= project.getLimitTeam()) {
|
|
|
errors.add(createError(rowIndex, enroll, "TEAM_LIMIT_EXCEEDED", projectName,
|
|
|
- String.format("项目'%s'队伍限报数:%d,当前报名队伍数已满", projectName, project.getLimitTeam())));
|
|
|
+ String.format("项目'%s'队伍限报数:%d,当前报名队伍数已满", projectName,
|
|
|
+ project.getLimitTeam())));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1635,7 +1666,8 @@ public class IEnrollServiceImpl implements IEnrollService {
|
|
|
if (!result.isValid()) {
|
|
|
// 如果校验不通过,抛出第一个错误信息
|
|
|
if (!result.getErrors().isEmpty()) {
|
|
|
- throw new org.dromara.common.core.exception.ServiceException(result.getErrors().get(0).getErrorMessage());
|
|
|
+ throw new org.dromara.common.core.exception.ServiceException(
|
|
|
+ result.getErrors().get(0).getErrorMessage());
|
|
|
}
|
|
|
}
|
|
|
}
|