赛事报名

This commit is contained in:
马梓彭 2026-04-13 05:20:09 +08:00
parent 814c5672f2
commit 8bd8961896
10 changed files with 257 additions and 69 deletions

View File

@ -119,6 +119,11 @@ public class ArrangementPreviewOutput
/// </summary> /// </summary>
public string ProjectName { get; set; } public string ProjectName { get; set; }
/// <summary>
/// 项目类型
/// </summary>
public string ProjectType { get; set; }
/// <summary> /// <summary>
/// 组别名称 /// 组别名称
/// </summary> /// </summary>

View File

@ -81,7 +81,39 @@ public class UnifiedRegistrationItemInput
public string? GroupName { get; set; } public string? GroupName { get; set; }
/// <summary> /// <summary>
/// 参赛人员列表 /// 团队列表(支持多个团队报名同一项目组别)
/// </summary>
public List<UnifiedTeamInput> Teams { get; set; } = new();
/// <summary>
/// 参赛人员列表(单个团队报名时使用,向后兼容)
/// </summary>
public List<UnifiedPersonInput> Persons { get; set; } = new();
}
/// <summary>
/// 统一团队输入DTO
/// </summary>
public class UnifiedTeamInput
{
/// <summary>
/// 团队ID(可选,已有团队时使用)
/// </summary>
public long? TeamId { get; set; }
/// <summary>
/// 团队名称
/// </summary>
[MaxLength(100, ErrorMessage = "团队名称不能超过100个字符")]
public string? TeamName { get; set; }
/// <summary>
/// 团队序号(用于显示,如"团队一"、"团队二")
/// </summary>
public int TeamIndex { get; set; }
/// <summary>
/// 该团队的参赛人员列表
/// </summary> /// </summary>
[Required(ErrorMessage = "参赛人员列表不能为空")] [Required(ErrorMessage = "参赛人员列表不能为空")]
[MinLength(1, ErrorMessage = "请至少填写一个参赛人员")] [MinLength(1, ErrorMessage = "请至少填写一个参赛人员")]

View File

@ -177,6 +177,7 @@ public class EventArrangementService : BaseService<YD_Event.Core.Entities.Projec
{ {
TimeRange = $"{proj.DurationSeconds}''", TimeRange = $"{proj.DurationSeconds}''",
ProjectName = proj.Name, ProjectName = proj.Name,
ProjectType = proj.ProjectType.Description(),
GroupName = person.GroupName, GroupName = person.GroupName,
TeamName = person.TeamName, TeamName = person.TeamName,
AthleteName = person.Name, AthleteName = person.Name,

View File

@ -72,7 +72,11 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
{ {
var regIds = result.Rows.Select(x => x.Id).ToList(); var regIds = result.Rows.Select(x => x.Id).ToList();
var items = await _itemRepository.GetListAsync(x => regIds.Contains(x.RegistrationId)); var items = await _itemRepository.GetListAsync(x => regIds.Contains(x.RegistrationId));
var persons = await _personRepository.GetListAsync(x => regIds.Contains(x.RegistrationId)); // 使用关联查询获取报名人员及其关联的用户信息
var persons = await _personRepository.AsQueryable()
.Includes(p => p.User) // 关联查询用户信息
.Where(x => regIds.Contains(x.RegistrationId))
.ToListAsync();
foreach (var dto in result.Rows) foreach (var dto in result.Rows)
{ {
@ -83,8 +87,16 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
{ {
var myPersons = persons.Where(p => p.RegistrationItemId == item.Id).ToList(); var myPersons = persons.Where(p => p.RegistrationItemId == item.Id).ToList();
// 构造参与人信息字符串 // 构造参与人信息字符串,可以使用关联的 User 信息
var personInfo = string.Join(";", myPersons.Select(p => $"{p.Name}/{p.Gender}/{p.Age}/{p.IdCard}")); var personInfo = string.Join(";", myPersons.Select(p =>
{
// 如果有关联的用户信息,可以使用更完整的信息
if (p.User != null)
{
return $"{p.Name}({p.User.Mobile})/{p.Gender}/{p.Age}";
}
return $"{p.Name}/{p.Gender}/{p.Age}/{p.IdCard}";
}));
if (personInfo.Length > 100) personInfo = personInfo.Substring(0, 97) + "..."; // 截断 if (personInfo.Length > 100) personInfo = personInfo.Substring(0, 97) + "..."; // 截断
var detail = new RegistrationItemDetailDto var detail = new RegistrationItemDetailDto

View File

@ -412,16 +412,63 @@ public class UnifiedRegistrationService : ITransient
return result; return result;
} }
// 2.1 验证队员资质(年龄和性别) // 处理团队列表(支持多团队报名)
var qualificationResult = ValidatePersonQualifications(item.Persons, eventProjectGroup); var teamsToValidate = new List<UnifiedTeamInput>();
if (!qualificationResult.IsValid)
if (item.Teams != null && item.Teams.Any())
{
// 使用新的团队列表格式
teamsToValidate.AddRange(item.Teams);
}
else if (item.Persons != null && item.Persons.Any())
{
// 向后兼容:使用旧的单个团队格式
teamsToValidate.Add(new UnifiedTeamInput
{
TeamIndex = 1,
Persons = item.Persons
});
}
else
{ {
result.IsValid = false; result.IsValid = false;
result.ErrorMessage = qualificationResult.ErrorMessage; result.ErrorMessage = $"请为项目添加至少一个团队和队员";
result.Details.AddRange(qualificationResult.Details); result.Details.Add(new ValidationDetail
{
ItemName = "团队验证",
IsPassed = false,
Message = result.ErrorMessage
});
return result; return result;
} }
result.Details.AddRange(qualificationResult.Details);
// 验证每个团队
foreach (var team in teamsToValidate)
{
if (team.Persons == null || !team.Persons.Any())
{
result.IsValid = false;
result.ErrorMessage = $"团队{team.TeamIndex}请至少添加一个队员";
result.Details.Add(new ValidationDetail
{
ItemName = "团队验证",
IsPassed = false,
Message = result.ErrorMessage
});
return result;
}
// 验证队员资质(年龄和性别)
var qualificationResult = ValidatePersonQualifications(team.Persons, eventProjectGroup);
if (!qualificationResult.IsValid)
{
result.IsValid = false;
result.ErrorMessage = qualificationResult.ErrorMessage;
result.Details.AddRange(qualificationResult.Details);
return result;
}
result.Details.AddRange(qualificationResult.Details);
}
} }
return result; return result;
@ -430,7 +477,7 @@ public class UnifiedRegistrationService : ITransient
/// <summary> /// <summary>
/// 验证队员资质(年龄和性别) /// 验证队员资质(年龄和性别)
/// </summary> /// </summary>
private RegistrationValidationResult ValidatePersonQualifications(List<UnifiedPersonInput> persons, EventProjectGroup group) public RegistrationValidationResult ValidatePersonQualifications(List<UnifiedPersonInput> persons, EventProjectGroup group)
{ {
var result = new RegistrationValidationResult var result = new RegistrationValidationResult
{ {
@ -565,51 +612,74 @@ public class UnifiedRegistrationService : ITransient
var baseProject = baseProjects.FirstOrDefault(p => p.Id == eventProject.ProjectId); var baseProject = baseProjects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
var groupName = GetGroupName(item, eventProjectGroups); var groupName = GetGroupName(item, eventProjectGroups);
// 计算费用 // 处理团队列表(支持多团队报名)
var (unitPrice, totalPrice) = CalculateItemPrice(feeConfig, eventProject, item.Persons.Count); var teamsToProcess = new List<UnifiedTeamInput>();
var regItemId = _idGenerator.NewLong(); if (item.Teams != null && item.Teams.Any())
var regItem = new EventRegistrationItem
{ {
Id = regItemId, // 使用新的团队列表格式
RegistrationId = registration.Id, teamsToProcess.AddRange(item.Teams);
ProjectId = item.ProjectId, }
EventProjectGroupId = item.GroupId, else if (item.Persons != null && item.Persons.Any())
ProjectName = baseProject?.Name ?? "",
GroupName = groupName,
ProjectTypeName = baseProject?.ProjectType.Description() ?? "",
UnitPrice = unitPrice,
PersonCount = item.Persons.Count,
TotalPrice = totalPrice,
CreatedUserId = input.CreatedUserId,
CreatedTime = DateTime.Now
};
regItems.Add(regItem);
totalPersonCount += item.Persons.Count;
// 添加参赛人员
foreach (var pInput in item.Persons)
{ {
persons.Add(new EventRegistrationPerson // 向后兼容:使用旧的单个团队格式
teamsToProcess.Add(new UnifiedTeamInput
{ {
Id = _idGenerator.NewLong(), TeamIndex = 1,
Persons = item.Persons
});
}
// 为每个团队创建报名明细
foreach (var team in teamsToProcess)
{
// 计算费用
var (unitPrice, totalPrice) = CalculateItemPrice(feeConfig, eventProject, team.Persons.Count);
var regItemId = _idGenerator.NewLong();
var regItem = new EventRegistrationItem
{
Id = regItemId,
RegistrationId = registration.Id, RegistrationId = registration.Id,
RegistrationItemId = regItemId,
ProjectId = item.ProjectId, ProjectId = item.ProjectId,
EventProjectGroupId = item.GroupId, EventProjectGroupId = item.GroupId,
GroupName = groupName,
ProjectName = baseProject?.Name ?? "", ProjectName = baseProject?.Name ?? "",
Name = pInput.Name, GroupName = groupName,
Gender = pInput.Gender, ProjectTypeName = baseProject?.ProjectType.Description() ?? "",
IdCard = pInput.IdCardNumber, UnitPrice = unitPrice,
BirthDate = pInput.BirthDate, PersonCount = team.Persons.Count,
Phone = pInput.Mobile, TotalPrice = totalPrice,
Age = CalculateAge(pInput.BirthDate),
PersonType = PersonType.Athlete,
CreatedUserId = input.CreatedUserId, CreatedUserId = input.CreatedUserId,
CreatedTime = DateTime.Now CreatedTime = DateTime.Now
}); };
regItems.Add(regItem);
totalPersonCount += team.Persons.Count;
// 添加参赛人员
foreach (var pInput in team.Persons)
{
persons.Add(new EventRegistrationPerson
{
Id = _idGenerator.NewLong(),
RegistrationId = registration.Id,
RegistrationItemId = regItemId,
ProjectId = item.ProjectId,
EventProjectGroupId = item.GroupId,
GroupName = groupName,
ProjectName = baseProject?.Name ?? "",
Name = pInput.Name,
Gender = pInput.Gender,
IdCard = pInput.IdCardNumber,
BirthDate = pInput.BirthDate,
Phone = pInput.Mobile,
Age = CalculateAge(pInput.BirthDate),
PersonType = PersonType.Athlete,
UserId = pInput.UserId,
CreatedUserId = input.CreatedUserId,
CreatedTime = DateTime.Now
});
}
} }
} }
@ -874,7 +944,7 @@ public class UnifiedRegistrationService : ITransient
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId); var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);
if (eventProject == null) continue; if (eventProject == null) continue;
var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == eventProject.ProjectId); var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == eventProject.Id);
if (projectLimit == null) continue; if (projectLimit == null) continue;
var submittedTeamCount = groupTeamCount[groupId]; var submittedTeamCount = groupTeamCount[groupId];

View File

@ -922,26 +922,41 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
var eventProjectGroup = await _eventProjectGroupRepository.GetByIdAsync(input.GroupId); var eventProjectGroup = await _eventProjectGroupRepository.GetByIdAsync(input.GroupId);
if (eventProjectGroup != null) if (eventProjectGroup != null)
{ {
var projectGroup = await _projectGroupRepository.GetByIdAsync(eventProjectGroup.ProjectGroupId); // 创建单个队员的验证输入
var persons = new List<UnifiedPersonInput>
// 优先使用EventProjectGroup中保存的信息,如果没有则使用原始ProjectGroup的信息
var birthDateStart = eventProjectGroup.BirthDateStart != DateTime.MinValue ? eventProjectGroup.BirthDateStart : (projectGroup?.BirthDateStart ?? DateTime.MinValue);
var birthDateEnd = eventProjectGroup.BirthDateEnd != DateTime.MinValue ? eventProjectGroup.BirthDateEnd : (projectGroup?.BirthDateEnd ?? DateTime.MaxValue);
var gender = eventProjectGroup.Gender;
var birthDate = member.BirthDate.Value;
if (birthDate < birthDateStart || birthDate > birthDateEnd)
{ {
result.AgeValid = false; new UnifiedPersonInput
result.IsValid = false; {
result.Message = "队员年龄不符合组别要求"; UserId = member.Id,
} Name = member.Name ?? "",
IdCardNumber = member.IdCardNumber ?? "",
Gender = member.Gender,
Mobile = member.Mobile,
BirthDate = member.BirthDate.Value
}
};
if (gender != member.Gender && gender != Gender.Unknown && gender != Gender.All) // 使用统一的验证方法进行验证
var validationResult = _unifiedRegistrationService.ValidatePersonQualifications(persons, eventProjectGroup);
if (!validationResult.IsValid)
{ {
result.GenderValid = false;
result.IsValid = false; result.IsValid = false;
result.Message = "队员性别不符合组别要求"; result.Message = validationResult.ErrorMessage ?? "验证失败";
// 根据验证详情设置具体的验证状态
var ageDetail = validationResult.Details?.FirstOrDefault(d => d.ItemName == "年龄验证" || d.ItemName == "年龄跨组验证");
var genderDetail = validationResult.Details?.FirstOrDefault(d => d.ItemName == "性别验证");
if (ageDetail != null && !ageDetail.IsPassed)
{
result.AgeValid = false;
}
if (genderDetail != null && !genderDetail.IsPassed)
{
result.GenderValid = false;
}
} }
} }
@ -987,7 +1002,7 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId); var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);
if (eventProject == null) continue; if (eventProject == null) continue;
var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == eventProject.ProjectId); var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == eventProject.Id);
if (projectLimit == null) continue; if (projectLimit == null) continue;
// 统计该项目下已报名的队伍数量 // 统计该项目下已报名的队伍数量
@ -1101,7 +1116,7 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
// 查找该组别的项目限报配置 // 查找该组别的项目限报配置
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId); var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);
var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == eventProject?.ProjectId); var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == eventProject?.Id);
var maxTeams = projectLimit?.LimitCount ?? 2; // 默认2队 var maxTeams = projectLimit?.LimitCount ?? 2; // 默认2队
result.Add(new GroupTeamLimitOutput result.Add(new GroupTeamLimitOutput

View File

@ -4020,6 +4020,11 @@
项目名称 项目名称
</summary> </summary>
</member> </member>
<member name="P:YD_Event.Application.Event.Dtos.ArrangementPreviewOutput.ProjectType">
<summary>
项目类型
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.ArrangementPreviewOutput.GroupName"> <member name="P:YD_Event.Application.Event.Dtos.ArrangementPreviewOutput.GroupName">
<summary> <summary>
组别名称 组别名称
@ -5635,9 +5640,39 @@
组别名称 组别名称
</summary> </summary>
</member> </member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedRegistrationItemInput.Teams">
<summary>
团队列表(支持多个团队报名同一项目组别)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedRegistrationItemInput.Persons"> <member name="P:YD_Event.Application.Event.Dtos.UnifiedRegistrationItemInput.Persons">
<summary> <summary>
参赛人员列表 参赛人员列表(单个团队报名时使用,向后兼容)
</summary>
</member>
<member name="T:YD_Event.Application.Event.Dtos.UnifiedTeamInput">
<summary>
统一团队输入DTO
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedTeamInput.TeamId">
<summary>
团队ID(可选,已有团队时使用)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedTeamInput.TeamName">
<summary>
团队名称
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedTeamInput.TeamIndex">
<summary>
团队序号(用于显示,如"团队一"、"团队二")
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedTeamInput.Persons">
<summary>
该团队的参赛人员列表
</summary> </summary>
</member> </member>
<member name="T:YD_Event.Application.Event.Dtos.UnifiedPersonInput"> <member name="T:YD_Event.Application.Event.Dtos.UnifiedPersonInput">

View File

@ -12,7 +12,7 @@ public class EventProjectRegistrationLimit : Entity<long>, ISoftDelete, ICreated
public long EventId { get; set; } public long EventId { get; set; }
/// <summary> /// <summary>
/// 关联的项目ID /// 关联的赛事项目ID
/// </summary> /// </summary>
public long ProjectId { get; set; } public long ProjectId { get; set; }

View File

@ -33,6 +33,19 @@ public class EventRegistrationPerson : Entity<long>, ISoftDelete, ICreatedUserId
/// </summary> /// </summary>
public PersonType PersonType { get; set; } public PersonType PersonType { get; set; }
/// <summary>
/// 关联的用户ID(必填)
/// </summary>
[SugarColumn(IsNullable = false)]
public long UserId { get; set; }
/// <summary>
/// 关联的用户信息(导航属性)
/// </summary>
[SugarColumn(IsIgnore = true)]
[Navigate(NavigateType.OneToOne, nameof(UserId))]
public AppUser? User { get; set; }
/// <summary> /// <summary>
/// 姓名 /// 姓名
/// </summary> /// </summary>

View File

@ -1626,7 +1626,7 @@
</member> </member>
<member name="P:YD_Event.Core.Entities.EventProjectRegistrationLimit.ProjectId"> <member name="P:YD_Event.Core.Entities.EventProjectRegistrationLimit.ProjectId">
<summary> <summary>
关联的项目ID 关联的赛事项目ID
</summary> </summary>
</member> </member>
<member name="P:YD_Event.Core.Entities.EventProjectRegistrationLimit.LimitCount"> <member name="P:YD_Event.Core.Entities.EventProjectRegistrationLimit.LimitCount">
@ -2059,6 +2059,11 @@
人员类型:1-运动员,2-教练,3-领队 人员类型:1-运动员,2-教练,3-领队
</summary> </summary>
</member> </member>
<member name="P:YD_Event.Core.Entities.EventRegistrationPerson.UserId">
<summary>
关联的用户ID(可选)
</summary>
</member>
<member name="P:YD_Event.Core.Entities.EventRegistrationPerson.Name"> <member name="P:YD_Event.Core.Entities.EventRegistrationPerson.Name">
<summary> <summary>
姓名 姓名