赛事报名

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>
public string ProjectName { get; set; }
/// <summary>
/// 项目类型
/// </summary>
public string ProjectType { get; set; }
/// <summary>
/// 组别名称
/// </summary>

View File

@ -81,7 +81,39 @@ public class UnifiedRegistrationItemInput
public string? GroupName { get; set; }
/// <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>
[Required(ErrorMessage = "参赛人员列表不能为空")]
[MinLength(1, ErrorMessage = "请至少填写一个参赛人员")]

View File

@ -177,6 +177,7 @@ public class EventArrangementService : BaseService<YD_Event.Core.Entities.Projec
{
TimeRange = $"{proj.DurationSeconds}''",
ProjectName = proj.Name,
ProjectType = proj.ProjectType.Description(),
GroupName = person.GroupName,
TeamName = person.TeamName,
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 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)
{
@ -83,8 +87,16 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
{
var myPersons = persons.Where(p => p.RegistrationItemId == item.Id).ToList();
// 构造参与人信息字符串
var personInfo = string.Join(";", myPersons.Select(p => $"{p.Name}/{p.Gender}/{p.Age}/{p.IdCard}"));
// 构造参与人信息字符串,可以使用关联的 User 信息
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) + "..."; // 截断
var detail = new RegistrationItemDetailDto

View File

@ -412,16 +412,63 @@ public class UnifiedRegistrationService : ITransient
return result;
}
// 2.1 验证队员资质(年龄和性别)
var qualificationResult = ValidatePersonQualifications(item.Persons, eventProjectGroup);
if (!qualificationResult.IsValid)
// 处理团队列表(支持多团队报名)
var teamsToValidate = new List<UnifiedTeamInput>();
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.ErrorMessage = qualificationResult.ErrorMessage;
result.Details.AddRange(qualificationResult.Details);
result.ErrorMessage = $"请为项目添加至少一个团队和队员";
result.Details.Add(new ValidationDetail
{
ItemName = "团队验证",
IsPassed = false,
Message = result.ErrorMessage
});
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;
@ -430,7 +477,7 @@ public class UnifiedRegistrationService : ITransient
/// <summary>
/// 验证队员资质(年龄和性别)
/// </summary>
private RegistrationValidationResult ValidatePersonQualifications(List<UnifiedPersonInput> persons, EventProjectGroup group)
public RegistrationValidationResult ValidatePersonQualifications(List<UnifiedPersonInput> persons, EventProjectGroup group)
{
var result = new RegistrationValidationResult
{
@ -565,51 +612,74 @@ public class UnifiedRegistrationService : ITransient
var baseProject = baseProjects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
var groupName = GetGroupName(item, eventProjectGroups);
// 计算费用
var (unitPrice, totalPrice) = CalculateItemPrice(feeConfig, eventProject, item.Persons.Count);
var regItemId = _idGenerator.NewLong();
var regItem = new EventRegistrationItem
// 处理团队列表(支持多团队报名)
var teamsToProcess = new List<UnifiedTeamInput>();
if (item.Teams != null && item.Teams.Any())
{
Id = regItemId,
RegistrationId = registration.Id,
ProjectId = item.ProjectId,
EventProjectGroupId = item.GroupId,
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)
// 使用新的团队列表格式
teamsToProcess.AddRange(item.Teams);
}
else if (item.Persons != null && item.Persons.Any())
{
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,
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,
GroupName = groupName,
ProjectTypeName = baseProject?.ProjectType.Description() ?? "",
UnitPrice = unitPrice,
PersonCount = team.Persons.Count,
TotalPrice = totalPrice,
CreatedUserId = input.CreatedUserId,
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);
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;
var submittedTeamCount = groupTeamCount[groupId];

View File

@ -922,26 +922,41 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
var eventProjectGroup = await _eventProjectGroupRepository.GetByIdAsync(input.GroupId);
if (eventProjectGroup != null)
{
var projectGroup = await _projectGroupRepository.GetByIdAsync(eventProjectGroup.ProjectGroupId);
// 优先使用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)
// 创建单个队员的验证输入
var persons = new List<UnifiedPersonInput>
{
result.AgeValid = false;
result.IsValid = false;
result.Message = "队员年龄不符合组别要求";
}
new UnifiedPersonInput
{
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.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);
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;
// 统计该项目下已报名的队伍数量
@ -1101,7 +1116,7 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
// 查找该组别的项目限报配置
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队
result.Add(new GroupTeamLimitOutput

View File

@ -4020,6 +4020,11 @@
项目名称
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.ArrangementPreviewOutput.ProjectType">
<summary>
项目类型
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.ArrangementPreviewOutput.GroupName">
<summary>
组别名称
@ -5635,9 +5640,39 @@
组别名称
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedRegistrationItemInput.Teams">
<summary>
团队列表(支持多个团队报名同一项目组别)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedRegistrationItemInput.Persons">
<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>
</member>
<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; }
/// <summary>
/// 关联的项目ID
/// 关联的赛事项目ID
/// </summary>
public long ProjectId { get; set; }

View File

@ -33,6 +33,19 @@ public class EventRegistrationPerson : Entity<long>, ISoftDelete, ICreatedUserId
/// </summary>
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>

View File

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