diff --git a/YD_Event.Application/Event/Dtos/ArrangementDtos.cs b/YD_Event.Application/Event/Dtos/ArrangementDtos.cs index 34179c9..2693fd8 100644 --- a/YD_Event.Application/Event/Dtos/ArrangementDtos.cs +++ b/YD_Event.Application/Event/Dtos/ArrangementDtos.cs @@ -119,6 +119,11 @@ public class ArrangementPreviewOutput /// public string ProjectName { get; set; } + /// + /// 项目类型 + /// + public string ProjectType { get; set; } + /// /// 组别名称 /// diff --git a/YD_Event.Application/Event/Dtos/UnifiedRegistrationDtos.cs b/YD_Event.Application/Event/Dtos/UnifiedRegistrationDtos.cs index a459339..e488d2e 100644 --- a/YD_Event.Application/Event/Dtos/UnifiedRegistrationDtos.cs +++ b/YD_Event.Application/Event/Dtos/UnifiedRegistrationDtos.cs @@ -81,7 +81,39 @@ public class UnifiedRegistrationItemInput public string? GroupName { get; set; } /// - /// 参赛人员列表 + /// 团队列表(支持多个团队报名同一项目组别) + /// + public List Teams { get; set; } = new(); + + /// + /// 参赛人员列表(单个团队报名时使用,向后兼容) + /// + public List Persons { get; set; } = new(); +} + +/// +/// 统一团队输入DTO +/// +public class UnifiedTeamInput +{ + /// + /// 团队ID(可选,已有团队时使用) + /// + public long? TeamId { get; set; } + + /// + /// 团队名称 + /// + [MaxLength(100, ErrorMessage = "团队名称不能超过100个字符")] + public string? TeamName { get; set; } + + /// + /// 团队序号(用于显示,如"团队一"、"团队二") + /// + public int TeamIndex { get; set; } + + /// + /// 该团队的参赛人员列表 /// [Required(ErrorMessage = "参赛人员列表不能为空")] [MinLength(1, ErrorMessage = "请至少填写一个参赛人员")] diff --git a/YD_Event.Application/Event/EventArrangementService.cs b/YD_Event.Application/Event/EventArrangementService.cs index 56f0721..9b8d3e3 100644 --- a/YD_Event.Application/Event/EventArrangementService.cs +++ b/YD_Event.Application/Event/EventArrangementService.cs @@ -177,6 +177,7 @@ public class EventArrangementService : BaseService, 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, 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 diff --git a/YD_Event.Application/Event/UnifiedRegistrationService.cs b/YD_Event.Application/Event/UnifiedRegistrationService.cs index 3b352ca..6bf105f 100644 --- a/YD_Event.Application/Event/UnifiedRegistrationService.cs +++ b/YD_Event.Application/Event/UnifiedRegistrationService.cs @@ -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(); + + 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 /// /// 验证队员资质(年龄和性别) /// - private RegistrationValidationResult ValidatePersonQualifications(List persons, EventProjectGroup group) + public RegistrationValidationResult ValidatePersonQualifications(List 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(); + + 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]; diff --git a/YD_Event.Application/Event/UserEventRegistrationService.cs b/YD_Event.Application/Event/UserEventRegistrationService.cs index a05a9bb..cc5b56d 100644 --- a/YD_Event.Application/Event/UserEventRegistrationService.cs +++ b/YD_Event.Application/Event/UserEventRegistrationService.cs @@ -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 { - 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 diff --git a/YD_Event.Application/YD_Event.Application.xml b/YD_Event.Application/YD_Event.Application.xml index 98471d9..40f1f1f 100644 --- a/YD_Event.Application/YD_Event.Application.xml +++ b/YD_Event.Application/YD_Event.Application.xml @@ -4020,6 +4020,11 @@ 项目名称 + + + 项目类型 + + 组别名称 @@ -5635,9 +5640,39 @@ 组别名称 + + + 团队列表(支持多个团队报名同一项目组别) + + - 参赛人员列表 + 参赛人员列表(单个团队报名时使用,向后兼容) + + + + + 统一团队输入DTO + + + + + 团队ID(可选,已有团队时使用) + + + + + 团队名称 + + + + + 团队序号(用于显示,如"团队一"、"团队二") + + + + + 该团队的参赛人员列表 diff --git a/YD_Event.Core/Entities/EventProjectRegistrationLimit.cs b/YD_Event.Core/Entities/EventProjectRegistrationLimit.cs index 15fe19a..99c0951 100644 --- a/YD_Event.Core/Entities/EventProjectRegistrationLimit.cs +++ b/YD_Event.Core/Entities/EventProjectRegistrationLimit.cs @@ -12,7 +12,7 @@ public class EventProjectRegistrationLimit : Entity, ISoftDelete, ICreated public long EventId { get; set; } /// - /// 关联的项目ID + /// 关联的赛事项目ID /// public long ProjectId { get; set; } diff --git a/YD_Event.Core/Entities/EventRegistrationPerson.cs b/YD_Event.Core/Entities/EventRegistrationPerson.cs index 64439ab..52cf8fd 100644 --- a/YD_Event.Core/Entities/EventRegistrationPerson.cs +++ b/YD_Event.Core/Entities/EventRegistrationPerson.cs @@ -33,6 +33,19 @@ public class EventRegistrationPerson : Entity, ISoftDelete, ICreatedUserId /// public PersonType PersonType { get; set; } + /// + /// 关联的用户ID(必填) + /// + [SugarColumn(IsNullable = false)] + public long UserId { get; set; } + + /// + /// 关联的用户信息(导航属性) + /// + [SugarColumn(IsIgnore = true)] + [Navigate(NavigateType.OneToOne, nameof(UserId))] + public AppUser? User { get; set; } + /// /// 姓名 /// diff --git a/YD_Event.Core/YD_Event.Core.xml b/YD_Event.Core/YD_Event.Core.xml index fad338e..b74de19 100644 --- a/YD_Event.Core/YD_Event.Core.xml +++ b/YD_Event.Core/YD_Event.Core.xml @@ -1626,7 +1626,7 @@ - 关联的项目ID + 关联的赛事项目ID @@ -2059,6 +2059,11 @@ 人员类型:1-运动员,2-教练,3-领队 + + + 关联的用户ID(可选) + + 姓名