赛事报名限制

This commit is contained in:
马梓彭 2026-04-15 10:41:07 +08:00
parent 1cdb2aec6c
commit 9bca004d2b
3 changed files with 181 additions and 57 deletions

View File

@ -16,7 +16,7 @@ namespace YD_Event.Application.Certificate;
/// <summary>
/// 证书模板管理服务
/// </summary>
public class CertificateTemplateService : BaseService<CertificateTemplate>, ITransient
public class CertificateTemplateService : BaseService<CertificateTemplate>, IDynamicApiController, ITransient
{
private readonly ISqlSugarRepository<CertificateTemplate> _templateRepository;
private readonly ISqlSugarRepository<CertificatePlaceholder> _placeholderRepository;

View File

@ -95,16 +95,42 @@ public class EventScoreService : BaseService<EventResult>, ITransient
[DisplayName("运动员成绩列表")]
public async Task<PageResult<ScoreListOutput>> 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<EventRegistration>((p, r) => p.RegistrationId == r.Id)
.LeftJoin<EventResult>((p, r, res) => p.Id == res.RegistrationPersonId)
.LeftJoin<EventCertificate>((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<EventResult>, ITransient
item.RankText = ConvertRankToChinese(item.Rank);
}
_logger.LogInformation("查询运动员成绩列表完成,共{Count}条记录", pageResult.Total);
return pageResult;
}

View File

@ -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
}
/// <summary>
/// 检查团队组别容量限制
/// 检查团队组别容量限制(项目组别限报设置)
/// </summary>
private void CheckTeamGroupCapacityLimitAsync(
UnifiedRegistrationInput input,
@ -1204,59 +1226,69 @@ public class UnifiedRegistrationService : ITransient
List<YD_Event.Core.Entities.Project> projects,
List<EventProjectGroup> eventProjectGroups)
{
var teamGroupCountDict = new Dictionary<long, int>();
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}个,请减少个人项目报名数量"));
}
/// <summary>
@ -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}个,请减少团队项目报名数量"));
}
/// <summary>
@ -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}\"中报名过,不能重复报名,避免占用多个名额"));
}
}
}
/// <summary>
/// 检查项目完成人数
/// </summary>
private void CheckProjectCompletionAsync(
UnifiedRegistrationInput input,
RegistrationLimitCheckOutput result,
List<EventProject> eventProjects,
List<YD_Event.Core.Entities.Project> projects,
List<EventProjectGroup> 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));
}
}
}