赛事编排:导出编排,报名ID不重复

This commit is contained in:
马梓彭 2026-04-28 00:02:01 +08:00
parent 6c152e5cd6
commit d9de064fc4
6 changed files with 119 additions and 33 deletions

View File

@ -49,6 +49,11 @@ public class ArrangementProjectListOutput
/// 总时长(分钟,向上取整)
/// </summary>
public int TotalDurationMinutes { get; set; }
/// <summary>
/// 团队总数(仅团队项目有效)
/// </summary>
public int TotalTeams { get; set; }
}
/// <summary>

View File

@ -202,7 +202,7 @@ public class RegistrationListOutput
public class RegistrationItemDetailDto
{
/// <summary>
/// 报名项目ID(EventRegistrationItem的ID)
/// 报名项目ID(EventRegistrationItem的ID 或 EventRegistrationPerson的ID,根据项目类型决定)
/// </summary>
public long Id { get; set; }

View File

@ -230,6 +230,7 @@ namespace YD_Event.Application.Event;
// 设置总时长秒数和分钟数
dto.TotalDurationSeconds = totalDurationSeconds;
dto.TotalDurationMinutes = (int)Math.Ceiling((double)totalDurationSeconds / 60);
dto.TotalTeams = totalTeams;
result.Add(dto);
}
@ -330,6 +331,7 @@ namespace YD_Event.Application.Event;
.OrderBy(p => p.GroupName)
.OrderBy(p => p.Id)
.Select((p, r, u, m) => new {
Id = p.Id, // 添加 Id 属性
RegistrationItemId = p.RegistrationItemId,
p.UserId,
p.Name,
@ -401,7 +403,7 @@ namespace YD_Event.Application.Event;
result.Add(new ArrangementPreviewOutput
{
RegistrationItemId = firstPerson.RegistrationItemId,
RegistrationItemId = firstPerson.RegistrationItemId, // 团队项目保持使用 EventRegistrationItem.Id
TimeRange = $"{proj.GetTotalDurationInSeconds()}''",
ProjectName = proj.Name,
ProjectType = proj.ProjectType.Description(),
@ -432,7 +434,7 @@ namespace YD_Event.Application.Event;
result.Add(new ArrangementPreviewOutput
{
RegistrationItemId = person.RegistrationItemId,
RegistrationItemId = person.Id, // 个人项目使用 EventRegistrationPerson.Id
TimeRange = $"{proj.GetTotalDurationInSeconds()}''",
ProjectName = proj.Name,
ProjectType = proj.ProjectType.Description(),

View File

@ -21,6 +21,8 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _appUserRepository;
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
private readonly ISqlSugarRepository<EventInfo> _eventInfoRepository;
private readonly ISqlSugarRepository<EventProject> _eventProjectRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
private readonly IIdGenerator _idGenerator;
private readonly OrderNoGeneratorService _orderNoGeneratorService;
private readonly AuthManager _authManager;
@ -35,6 +37,8 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
ISqlSugarRepository<EventInfo> eventInfoRepository,
ISqlSugarRepository<EventProject> eventProjectRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
IIdGenerator idGenerator,
OrderNoGeneratorService orderNoGeneratorService,
AuthManager authManager) : base(repository)
@ -48,6 +52,8 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
_appUserRepository = appUserRepository;
_merchantCertificationRepository = merchantCertificationRepository;
_eventInfoRepository = eventInfoRepository;
_eventProjectRepository = eventProjectRepository;
_projectRepository = projectRepository;
_idGenerator = idGenerator;
_orderNoGeneratorService = orderNoGeneratorService;
_authManager = authManager;
@ -102,6 +108,16 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
.Includes(p => p.User) // 关联查询用户信息
.Where(x => regIds.Contains(x.RegistrationId))
.ToListAsync();
// 获取所有赛事项目和项目信息,用于判断项目类型
var eventProjectIds = items.Select(x => x.ProjectId).Distinct().ToList();
var eventProjects = await _eventProjectRepository.GetListAsync(x => eventProjectIds.Contains(x.Id));
var projectIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
var projects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
// 创建映射字典
var eventProjectToProjectDict = eventProjects.ToDictionary(x => x.Id, x => x.ProjectId);
Dictionary<long, YD_Event.Core.Entities.Project> projectDict = projects.ToDictionary(x => x.Id, x => x);
// 获取所有报名记录的 CreatedUserId
var createdUserIds = result.Rows.Select(x => x.CreatedUserId).Distinct().ToList();
@ -230,33 +246,70 @@ public class EventRegistrationService : BaseService<EventRegistration>, ITransie
{
var myPersons = persons.Where(p => p.RegistrationItemId == item.Id).ToList();
// 构造参与人信息字符串,统一格式为:姓名/性别/年龄/手机号
var personInfo = string.Join(";", myPersons.Select(p =>
// 判断项目类型
bool isIndividualProject = false;
if (eventProjectToProjectDict.TryGetValue(item.ProjectId, out var projectId) &&
projectDict.TryGetValue(projectId, out var project))
{
// 将性别转换为中文显示
var genderText = p.Gender switch
isIndividualProject = YD_Event.Core.Extensions.ProjectTypeExtensions.IsIndividualProject(project);
}
if (isIndividualProject)
{
// 个人项目:为每个运动员创建一个条目,使用 EventRegistrationPerson.Id
foreach (var person in myPersons.Where(p => p.PersonType == PersonType.Athlete))
{
YD_Event.Core.Enum.Gender.Male => "男",
YD_Event.Core.Enum.Gender.Female => "女",
_ => "未知"
};
// 获取手机号,优先使用关联用户信息,否则使用自身的 Phone 字段
var mobile = p.User?.Mobile ?? p.Phone ?? "";
// 统一格式:姓名/性别/年龄/手机号
return $"{p.Name}/{genderText}/{p.Age}/{mobile}";
}));
if (personInfo.Length > 100) personInfo = personInfo.Substring(0, 97) + "..."; // 截断
// 构造参与人信息字符串
var genderText = person.Gender switch
{
YD_Event.Core.Enum.Gender.Male => "男",
YD_Event.Core.Enum.Gender.Female => "女",
_ => "未知"
};
var mobile = person.User?.Mobile ?? person.Phone ?? "";
var personInfo = $"{person.Name}/{genderText}/{person.Age}/{mobile}";
var detail = new RegistrationItemDetailDto
var detail = new RegistrationItemDetailDto
{
Id = person.Id, // 使用 EventRegistrationPerson.Id
ProjectName = item.ProjectName,
GroupName = item.GroupName,
ParticipantInfo = personInfo
};
dto.Items.Add(detail);
}
}
else
{
Id = item.Id,
ProjectName = item.ProjectName,
GroupName = item.GroupName,
ParticipantInfo = personInfo
};
dto.Items.Add(detail);
// 团队项目:保持原有逻辑,使用 EventRegistrationItem.Id
// 构造参与人信息字符串,统一格式为:姓名/性别/年龄/手机号
var personInfo = string.Join(";", myPersons.Select(p =>
{
// 将性别转换为中文显示
var genderText = p.Gender switch
{
YD_Event.Core.Enum.Gender.Male => "男",
YD_Event.Core.Enum.Gender.Female => "女",
_ => "未知"
};
// 获取手机号,优先使用关联用户信息,否则使用自身的 Phone 字段
var mobile = p.User?.Mobile ?? p.Phone ?? "";
// 统一格式:姓名/性别/年龄/手机号
return $"{p.Name}/{genderText}/{p.Age}/{mobile}";
}));
if (personInfo.Length > 100) personInfo = personInfo.Substring(0, 97) + "..."; // 截断
var detail = new RegistrationItemDetailDto
{
Id = item.Id, // 使用 EventRegistrationItem.Id
ProjectName = item.ProjectName,
GroupName = item.GroupName,
ParticipantInfo = personInfo
};
dto.Items.Add(detail);
}
}
// 设置领队信息和教练信息 - 从团队中获取

View File

@ -47,6 +47,7 @@ public class EventScoreService : BaseService<EventResult>, ITransient
private readonly ISqlSugarRepository<EventResult> _resultRepository;
private readonly ISqlSugarRepository<EventCertificate> _certificateRepository;
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
private readonly ISqlSugarRepository<EventRegistrationItem> _itemRepository;
private readonly ISqlSugarRepository<CertificateTemplate> _templateRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Certificate> _certificateMasterRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
@ -66,6 +67,7 @@ public class EventScoreService : BaseService<EventResult>, ITransient
ISqlSugarRepository<EventResult> resultRepository,
ISqlSugarRepository<EventCertificate> certificateRepository,
ISqlSugarRepository<EventRegistration> registrationRepository,
ISqlSugarRepository<EventRegistrationItem> itemRepository,
ISqlSugarRepository<CertificateTemplate> templateRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Certificate> certificateMasterRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
@ -84,6 +86,7 @@ public class EventScoreService : BaseService<EventResult>, ITransient
_resultRepository = resultRepository;
_certificateRepository = certificateRepository;
_registrationRepository = registrationRepository;
_itemRepository = itemRepository;
_templateRepository = templateRepository;
_certificateMasterRepository = certificateMasterRepository;
_projectRepository = projectRepository;
@ -275,7 +278,7 @@ public class EventScoreService : BaseService<EventResult>, ITransient
/// 教练/领队列表
/// </summary>
/// <remarks>
/// 通过运动员报名记录 -&gt; TeamMember团队关联 -&gt; 找到同团队的教练/领队
/// 通过运动员报名记录 -> TeamMember团队关联 -> 找到同团队的教练/领队
/// </remarks>
[DisplayName("教练领队列表")]
public async Task<PageResult<CoachLeaderListOutput>> PageCoachLeader(CoachLeaderQueryInput input)
@ -515,12 +518,21 @@ public class EventScoreService : BaseService<EventResult>, ITransient
var allProjects = await _projectRepository.GetListAsync(x => allProjectIds.Contains(x.Id));
var allProjectDict = allProjects.ToDictionary(p => p.Id, p => p);
// 步骤4:使用字典建立人员索引(按RegistrationItemId索引,取第一个人作为代表)
// 注意:团队项目中一个 RegistrationItemId 可能对应多个运动员,但只需要第一个作为代表
// 步骤4:使用字典建立人员索引
// 1. 按 EventRegistrationPerson.Id 索引(用于个人项目)
var personByIdDict = allPersonsWithTeam
.ToDictionary(p => p.Id, p => p);
// 2. 按 RegistrationItemId 索引(用于团队项目)
var personByItemIdDict = allPersonsWithTeam
.GroupBy(p => p.RegistrationItemId)
.ToDictionary(g => g.Key, g => g.First());
// 同时预加载所有 EventRegistrationItem 信息
var registrationItemIds = allPersonsWithTeam.Select(p => p.RegistrationItemId).Distinct().ToList();
var registrationItems = await _itemRepository.GetListAsync(x => registrationItemIds.Contains(x.Id));
var registrationItemDict = registrationItems.ToDictionary(x => x.Id, x => x);
// 预加载 EventRegistration 信息,用于获取归属单位 ID
var registrationIdsList = allPersonsWithTeam.Select(p => p.RegistrationId).Distinct().ToList();
var registrations = await _registrationRepository.GetListAsync(r => registrationIdsList.Contains(r.Id));
@ -541,8 +553,17 @@ public class EventScoreService : BaseService<EventResult>, ITransient
foreach(var row in validationResult.ValidRows)
{
// 使用报名ID进行匹配
if (!personByItemIdDict.TryGetValue(row.RegistrationItemId.Value, out var person))
// 首先尝试用 ID 查找 EventRegistrationPerson(用于个人项目)
if (personByIdDict.TryGetValue(row.RegistrationItemId.Value, out var person))
{
// 找到了个人项目的记录,继续处理
}
// 如果没找到,再尝试用 RegistrationItemId 查找(用于团队项目)
else if (personByItemIdDict.TryGetValue(row.RegistrationItemId.Value, out person))
{
// 找到了团队项目的记录,继续处理
}
else
{
notFoundPersons.Add($"报名ID:{row.RegistrationItemId.Value}");
continue;

View File

@ -4189,6 +4189,11 @@
总时长(分钟,向上取整)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.ArrangementProjectListOutput.TotalTeams">
<summary>
团队总数(仅团队项目有效)
</summary>
</member>
<member name="T:YD_Event.Application.Event.Dtos.ArrangementGroupDto">
<summary>
编排组别DTO
@ -5831,7 +5836,7 @@
</member>
<member name="P:YD_Event.Application.Event.Dtos.RegistrationItemDetailDto.Id">
<summary>
报名项目ID(EventRegistrationItem的ID)
报名项目ID(EventRegistrationItem的ID 或 EventRegistrationPerson的ID,根据项目类型决定)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.RegistrationItemDetailDto.ParticipantInfo">
@ -8040,7 +8045,7 @@
教练/领队列表
</summary>
<remarks>
通过运动员报名记录 -&gt; TeamMember团队关联 -&gt; 找到同团队的教练/领队
通过运动员报名记录 -> TeamMember团队关联 -> 找到同团队的教练/领队
</remarks>
</member>
<!-- Badly formed XML comment ignored for member "M:YD_Event.Application.Event.EventScoreService.ImportScore(System.Int64,Microsoft.AspNetCore.Http.IFormFile)" -->