From 9bca004d2b893e9af2daa9b094551ad3a60e8701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E6=A2=93=E5=BD=AD?= Date: Wed, 15 Apr 2026 10:41:07 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B5=9B=E4=BA=8B=E6=8A=A5=E5=90=8D=E9=99=90?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Certificate/CertificateTemplateService.cs | 2 +- .../Event/EventScoreService.cs | 41 +++- .../Event/UnifiedRegistrationService.cs | 195 +++++++++++++----- 3 files changed, 181 insertions(+), 57 deletions(-) diff --git a/YD_Event.Application/Certificate/CertificateTemplateService.cs b/YD_Event.Application/Certificate/CertificateTemplateService.cs index f24dc10..a4027dd 100644 --- a/YD_Event.Application/Certificate/CertificateTemplateService.cs +++ b/YD_Event.Application/Certificate/CertificateTemplateService.cs @@ -16,7 +16,7 @@ namespace YD_Event.Application.Certificate; /// /// 证书模板管理服务 /// -public class CertificateTemplateService : BaseService, ITransient +public class CertificateTemplateService : BaseService, IDynamicApiController, ITransient { private readonly ISqlSugarRepository _templateRepository; private readonly ISqlSugarRepository _placeholderRepository; diff --git a/YD_Event.Application/Event/EventScoreService.cs b/YD_Event.Application/Event/EventScoreService.cs index 17c2012..d6f0a98 100644 --- a/YD_Event.Application/Event/EventScoreService.cs +++ b/YD_Event.Application/Event/EventScoreService.cs @@ -95,16 +95,42 @@ public class EventScoreService : BaseService, ITransient [DisplayName("运动员成绩列表")] public async Task> PageAthlete(ScoreQueryInput input) { - var pageResult = await _personRepository.AsQueryable() + _logger.LogInformation("开始查询运动员成绩列表,赛事ID={EventId}, 项目ID={ProjectId}, 组别={GroupName}", + input.EventId, input.ProjectId, input.GroupName); + + var query = _personRepository.AsQueryable() .LeftJoin((p, r) => p.RegistrationId == r.Id) .LeftJoin((p, r, res) => p.Id == res.RegistrationPersonId) .LeftJoin((p, r, res, c) => res.Id == c.ResultId) - .Where((p, r, res, c) => r.EventId == input.EventId && (p.PersonType == PersonType.Athlete || p.PersonType == PersonType.User)) - .WhereIF(input.ProjectId.HasValue, p => p.ProjectId == input.ProjectId) - .WhereIF(!string.IsNullOrWhiteSpace(input.GroupName), p => p.GroupName == input.GroupName) - .WhereIF(!string.IsNullOrWhiteSpace(input.Name), p => p.Name.Contains(input.Name)) - .WhereIF(!string.IsNullOrWhiteSpace(input.UnitName), (p, r) => r.TeamName.Contains(input.UnitName)) - .WhereIF(input.Gender.HasValue, p => p.Gender == input.Gender.Value) + .Where((p, r, res, c) => r.EventId == input.EventId && (p.PersonType == PersonType.Athlete || p.PersonType == PersonType.User)); + + // 应用筛选条件 + if (input.ProjectId.HasValue) + { + query = query.Where(p => p.ProjectId == input.ProjectId.Value); + } + + if (!string.IsNullOrWhiteSpace(input.GroupName)) + { + query = query.Where(p => p.GroupName == input.GroupName); + } + + if (!string.IsNullOrWhiteSpace(input.Name)) + { + query = query.Where(p => p.Name.Contains(input.Name)); + } + + if (!string.IsNullOrWhiteSpace(input.UnitName)) + { + query = query.Where((p, r) => r.TeamName.Contains(input.UnitName)); + } + + if (input.Gender.HasValue) + { + query = query.Where(p => p.Gender == input.Gender.Value); + } + + var pageResult = await query .Select((p, r, res, c) => new ScoreListOutput { Id = p.Id, @@ -123,6 +149,7 @@ public class EventScoreService : BaseService, ITransient item.RankText = ConvertRankToChinese(item.Rank); } + _logger.LogInformation("查询运动员成绩列表完成,共{Count}条记录", pageResult.Total); return pageResult; } diff --git a/YD_Event.Application/Event/UnifiedRegistrationService.cs b/YD_Event.Application/Event/UnifiedRegistrationService.cs index 1ebff60..8cab486 100644 --- a/YD_Event.Application/Event/UnifiedRegistrationService.cs +++ b/YD_Event.Application/Event/UnifiedRegistrationService.cs @@ -1154,11 +1154,33 @@ public class UnifiedRegistrationService : ITransient _logger.LogInformation("获取到赛事报名记录: {RegistrationCount},报名人员: {PersonCount},报名项目: {ItemCount}", eventRegistrations.Count, registrationPersons.Count, allRegistrationItems.Count); - // 检查各类型限制 + // ==================== 报名限制检查流程 ==================== + // 检查顺序说明: + // 1. 先检查团队组别容量限制 - 检查是否超过组别限报的团队数量 + // 2. 再检查成员项目限报 - 检查队员是否超过个人/团队项目的限报数量 + // 3. 然后检查人员项目组别重复 - 检查同一人员是否在同一项目的多个组别中重复报名 + // 4. 再检查人员是否已报名 - 检查人员是否已在其他报名记录中报过同一项目组别 + // 5. 最后检查项目完成人数 - 确保人数与项目的完成人数完全一致 + + // 检查1:团队组别容量限制 + // 验证是否有组别超过了限报的团队数量(例如:某组别限报2队,但本次报了3队) CheckTeamGroupCapacityLimitAsync(input, result, projectLimits, eventProjects, projects, eventProjectGroups); + + // 检查2:成员项目限报 + // 验证队员是否超过了个人项目或团队项目的限报数量(例如:某队员已报2个个人项目,限报2个,又报第3个) await CheckMemberProjectLimitAsync(input, result, limit, eventProjects, projects, registrationPersons, allRegistrationItems); + + // 检查3:人员项目组别重复 + // 验证同一人员是否在本次报名中重复选择了同一项目的多个组别(例如:同一队员在同一项目的男子组和女子组都报了名) CheckPersonProjectDuplicateAsync(input, result, eventProjects, projects, eventProjectGroups); + + // 检查4:人员是否已在其他报名中报名 + // 验证人员是否已在其他已支付的报名记录中报过同一项目组别(避免重复占用名额) CheckPersonAlreadyRegisteredAsync(input, result, eventProjects, projects, eventProjectGroups, registrationPersons, allRegistrationItems, eventRegistrationIds); + + // 检查5:项目完成人数验证 + // 验证用户选择的参与人数必须与项目的"完成人数"完全一致(例如:项目要求完成3人,但只选了2人或选了4人) + CheckProjectCompletionAsync(input, result, eventProjects, projects, eventProjectGroups); _logger.LogInformation("报名限制检查完成,是否存在限制: {HasLimit}, 限制详情数: {LimitCount}", result.HasLimit, result.LimitDetails.Count); @@ -1194,7 +1216,7 @@ public class UnifiedRegistrationService : ITransient } /// - /// 检查团队组别容量限制 + /// 检查团队组别容量限制(项目组别限报设置) /// private void CheckTeamGroupCapacityLimitAsync( UnifiedRegistrationInput input, @@ -1204,59 +1226,69 @@ public class UnifiedRegistrationService : ITransient List projects, List eventProjectGroups) { - var teamGroupCountDict = new Dictionary(); - foreach (var item in input.Items) { if (!item.GroupId.HasValue) continue; - var teams = GetTeamsFromItem(item); - var teamCountForItem = teams.Count; - - if (teamCountForItem > 0) - { - if (teamGroupCountDict.ContainsKey(item.GroupId.Value)) - { - teamGroupCountDict[item.GroupId.Value] += teamCountForItem; - } - else - { - teamGroupCountDict[item.GroupId.Value] = teamCountForItem; - } - } - } - - if (!teamGroupCountDict.Any()) - { - return; - } - - foreach (var groupId in teamGroupCountDict.Keys) - { - var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == groupId); + // 获取组别信息 + var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == item.GroupId.Value); if (eventProjectGroup == null) continue; - + + // 获取赛事项目信息 var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId); if (eventProject == null) continue; - + + // 获取基础项目信息 + var project = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId); + if (project == null) continue; + + // 判断是否为团队项目 + var isTeamProject = project.IsTeamProject(); + + // 获取项目限报配置 var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == eventProject.Id); if (projectLimit == null) continue; - - var submittedTeamCount = teamGroupCountDict[groupId]; - if (submittedTeamCount <= projectLimit.LimitCount) continue; - - var project = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId); - var projectName = project?.Name ?? "未知项目"; - _logger.LogWarning("组别容量超限,项目: {ProjectName},组别: {GroupName},限报: {LimitCount},提交: {SubmittedCount}", - projectName, eventProjectGroup.GroupName, projectLimit.LimitCount, submittedTeamCount); - - result.HasLimit = true; - result.LimitDetails.Add(CreateLimitDetail( - 0, - eventProjectGroup.GroupName ?? "未知组别", - "ProjectGroupCapacity", - $"项目\"{projectName}\"的组别\"{eventProjectGroup.GroupName}\"限报{projectLimit.LimitCount}队,本次提交{submittedTeamCount}队,超出限报配置")); + if (isTeamProject) + { + // ==================== 团队项目验证 ==================== + // 检查该组别申报的团队数量是否超过限报配置 + var teams = GetTeamsFromItem(item); + var submittedTeamCount = teams.Count; + + if (submittedTeamCount > projectLimit.LimitCount) + { + _logger.LogWarning("团队项目组别容量超限,项目: {ProjectName},组别: {GroupName},限报: {LimitCount}队,提交: {SubmittedCount}队", + project.Name, eventProjectGroup.GroupName, projectLimit.LimitCount, submittedTeamCount); + + result.HasLimit = true; + result.LimitDetails.Add(CreateLimitDetail( + 0, + eventProjectGroup.GroupName ?? "未知组别", + "ProjectGroupCapacity", + $"项目组别限报设置:项目\"{project.Name}\"的组别\"{eventProjectGroup.GroupName}\"限报{projectLimit.LimitCount}队,本次提交{submittedTeamCount}队,超出限报配置")); + } + } + else + { + // ==================== 个人项目验证 ==================== + // 检查该组别申报的人员数量是否超过限报配置 + var persons = GetAllPersonsFromItem(item); + var submittedPersonCount = persons.Count; + + if (submittedPersonCount > projectLimit.LimitCount) + { + _logger.LogWarning("个人项目组别容量超限,项目: {ProjectName},组别: {GroupName},限报: {LimitCount}人,提交: {SubmittedCount}人", + project.Name, eventProjectGroup.GroupName, projectLimit.LimitCount, submittedPersonCount); + + result.HasLimit = true; + result.LimitDetails.Add(CreateLimitDetail( + 0, + eventProjectGroup.GroupName ?? "未知组别", + "ProjectGroupCapacity", + $"项目组别限报设置:项目\"{project.Name}\"的组别\"{eventProjectGroup.GroupName}\"限报{projectLimit.LimitCount}人,本次提交{submittedPersonCount}人,超出限报配置")); + } + } } } @@ -1402,7 +1434,7 @@ public class UnifiedRegistrationService : ITransient member.Id, member.Name ?? member.NickName ?? "未知", "IndividualProjectLimit", - $"队员{member.Name}已报名{individualProjectCount}个个人项目,达到个人项目限报{limit.IndividualProjectLimit}个")); + $"成员项目限报设置:队员\"{member.Name}\"已报名{individualProjectCount}个个人项目,个人项目限报{limit.IndividualProjectLimit}个,请减少个人项目报名数量")); } /// @@ -1427,7 +1459,7 @@ public class UnifiedRegistrationService : ITransient member.Id, member.Name ?? member.NickName ?? "未知", "TeamProjectLimit", - $"队员{member.Name}已参与{teamProjectCount}个团队项目,达到团队项目限报{limit.TeamProjectLimit}个")); + $"成员项目限报设置:队员\"{member.Name}\"已参与{teamProjectCount}个团队项目,团队项目限报{limit.TeamProjectLimit}个,请减少团队项目报名数量")); } /// @@ -1499,7 +1531,7 @@ public class UnifiedRegistrationService : ITransient 0, personName, "PersonProjectDuplicate", - $"人员\"{personName}\"在项目\"{groupInfo.ProjectName}\"的组别\"{groupInfo.GroupName}\"中重复出现,同一人员只能在一个团队中报名同一项目组别")); + $"人员项目重复检测:人员\"{personName}\"在项目\"{groupInfo.ProjectName}\"的组别\"{groupInfo.GroupName}\"中重复出现,同一人员只能在一个团队中报名同一项目组别")); } } } @@ -1558,7 +1590,72 @@ public class UnifiedRegistrationService : ITransient 0, person.Name, "PersonAlreadyRegistered", - $"人员\"{person.Name}\"已在项目\"{projectName}\"的组别\"{eventProjectGroup.GroupName}\"中报名过,不能重复报名")); + $"人员已报名检测:人员\"{person.Name}\"已在项目\"{projectName}\"的组别\"{eventProjectGroup.GroupName}\"中报名过,不能重复报名,避免占用多个名额")); + } + } + } + + /// + /// 检查项目完成人数 + /// + private void CheckProjectCompletionAsync( + UnifiedRegistrationInput input, + RegistrationLimitCheckOutput result, + List eventProjects, + List projects, + List eventProjectGroups) + { + foreach (var item in input.Items) + { + if (!item.GroupId.HasValue) continue; + + // 获取组别信息 + var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == item.GroupId.Value); + if (eventProjectGroup == null) continue; + + // 获取赛事项目信息 + var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId); + if (eventProject == null) continue; + + // 获取基础项目信息 + var project = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId); + if (project == null) continue; + + // 判断是否为团队项目 + var isTeamProject = project.IsTeamProject(); + + // 计算分配的人数(去重) + var assignedPersons = GetAllPersonsFromItem(item); + var totalAssignedMembers = assignedPersons + .Where(p => p.UserId.HasValue) + .Select(p => p.UserId.Value) + .Distinct() + .Count(); + + // ==================== 项目完成人数验证 ==================== + // 验证用户选择的参与人数必须与项目的"完成人数"完全一致 + if (project.CompletionCount > 0 && totalAssignedMembers != project.CompletionCount) + { + string errorMessage; + if (totalAssignedMembers < project.CompletionCount) + { + errorMessage = isTeamProject + ? $"项目完成人数限制:项目\"{project.Name}\"的组别\"{eventProjectGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前只分配了{totalAssignedMembers}人,请补充队员" + : $"项目完成人数限制:项目\"{project.Name}\"的组别\"{eventProjectGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前只分配了{totalAssignedMembers}人,请补充队员"; + } + else + { + errorMessage = isTeamProject + ? $"项目完成人数限制:项目\"{project.Name}\"的组别\"{eventProjectGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前分配了{totalAssignedMembers}人,人数超额,请移除多余队员" + : $"项目完成人数限制:项目\"{project.Name}\"的组别\"{eventProjectGroup.GroupName}\"要求完成人数为{project.CompletionCount}人,当前分配了{totalAssignedMembers}人,人数超额,请移除多余队员"; + } + + result.HasLimit = true; + result.LimitDetails.Add(CreateLimitDetail( + 0, + eventProjectGroup.GroupName ?? "未知组别", + "ProjectCompletion", + errorMessage)); } } }