YD_Event.Admin/YD_Event.Application/Event/EventRecordService.cs

362 lines
15 KiB
C#
Raw Normal View History

2026-03-08 15:46:19 +08:00
using YD_Event.Application.Event.Dtos;
2026-03-06 12:10:09 +08:00
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
2026-03-06 12:29:07 +08:00
using AppUserEntity = YD_Event.Core.Entities.AppUser;
2026-04-13 06:58:21 +08:00
using SysUserEntity = YD_Event.Core.Entities.SysUser;
using MerchantEntity = YD_Event.Core.Entities.Merchant;
2026-03-06 12:10:09 +08:00
2026-03-08 15:46:19 +08:00
namespace YD_Event.Application.Event;
2026-03-06 12:10:09 +08:00
/// <summary>
/// 参赛记录管理
/// </summary>
2026-03-08 15:46:19 +08:00
public class EventRecordService : BaseService<EventRegistration>, ITransient
2026-03-06 12:10:09 +08:00
{
2026-05-07 08:33:53 +08:00
private static readonly string[] ChineseNumbers = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" };
/// <summary>
/// 将数字名次转换为汉字形式(与成绩与证书页面保持一致)
/// </summary>
private static string ConvertRankToChinese(int rank)
{
if (rank <= 0)
return string.Empty;
if (rank <= 10)
return $"第{ChineseNumbers[rank]}名";
if (rank < 20)
return $"第十{ChineseNumbers[rank % 10]}名";
if (rank % 10 == 0)
return $"第{ChineseNumbers[rank / 10]}十名";
return $"第{ChineseNumbers[rank / 10]}十{ChineseNumbers[rank % 10]}名";
}
2026-03-06 12:10:09 +08:00
private readonly ISqlSugarRepository<EventRegistration> _registrationRep;
private readonly ISqlSugarRepository<EventInfo> _eventRep;
private readonly ISqlSugarRepository<EventCertificate> _certificateRep;
2026-03-06 12:29:07 +08:00
private readonly ISqlSugarRepository<AppUserEntity> _appUserRep;
2026-04-13 06:58:21 +08:00
private readonly ISqlSugarRepository<SysUserEntity> _sysUserRep;
private readonly ISqlSugarRepository<MerchantEntity> _merchantRep;
2026-04-27 13:22:59 +08:00
private readonly ISqlSugarRepository<EventRegistrationItem> _registrationItemRep;
private readonly ISqlSugarRepository<EventResult> _resultRep;
2026-05-07 08:33:53 +08:00
private readonly ISqlSugarRepository<EventRegistrationPerson> _registrationPersonRep;
private readonly ISqlSugarRepository<EventProject> _eventProjectRep;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRep;
2026-03-06 12:10:09 +08:00
public EventRecordService(ISqlSugarRepository<EventRegistration> registrationRep,
ISqlSugarRepository<EventInfo> eventRep,
2026-03-06 12:29:07 +08:00
ISqlSugarRepository<EventCertificate> certificateRep,
ISqlSugarRepository<AppUserEntity> appUserRep,
2026-04-13 06:58:21 +08:00
ISqlSugarRepository<SysUserEntity> sysUserRep,
2026-04-27 13:22:59 +08:00
ISqlSugarRepository<MerchantEntity> merchantRep,
ISqlSugarRepository<EventRegistrationItem> registrationItemRep,
2026-05-07 08:33:53 +08:00
ISqlSugarRepository<EventResult> resultRep,
ISqlSugarRepository<EventRegistrationPerson> registrationPersonRep,
ISqlSugarRepository<EventProject> eventProjectRep,
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRep) : base(registrationRep)
2026-03-06 12:10:09 +08:00
{
_registrationRep = registrationRep;
_eventRep = eventRep;
_certificateRep = certificateRep;
2026-03-06 12:29:07 +08:00
_appUserRep = appUserRep;
2026-04-13 06:58:21 +08:00
_sysUserRep = sysUserRep;
2026-03-06 12:29:07 +08:00
_merchantRep = merchantRep;
2026-04-27 13:22:59 +08:00
_registrationItemRep = registrationItemRep;
_resultRep = resultRep;
2026-05-07 08:33:53 +08:00
_registrationPersonRep = registrationPersonRep;
_eventProjectRep = eventProjectRep;
_projectRep = projectRep;
2026-03-06 12:10:09 +08:00
}
/// <summary>
/// 分页查询参赛记录
/// </summary>
[HttpGet("page")]
public async Task<PageResult<EventRecordOutput>> Page([FromQuery] EventRecordPageInput input)
{
2026-04-13 06:58:21 +08:00
// 参数验证
ValidateInput(input);
2026-04-27 13:22:59 +08:00
// 第一步:查询用户报名过的所有赛事(去重,按赛事分组)
var eventsQuery = _registrationRep.AsQueryable()
2026-03-06 12:10:09 +08:00
.LeftJoin<EventInfo>((r, e) => r.EventId == e.Id)
.LeftJoin<AppUserEntity>((r, e, u) => r.CreatedUserId == u.Id)
2026-04-13 06:58:21 +08:00
.LeftJoin<SysUserEntity>((r, e, u, s) => r.CreatedUserId == s.Id)
.LeftJoin<MerchantEntity>((r, e, u, s, m) => r.CreatedUserId == m.CreatedUserId)
2026-05-11 14:56:42 +08:00
.Where((r, e, u, s, m) => r.PaymentStatus != 5) // 排除预报名记录
2026-04-13 06:58:21 +08:00
.WhereIF(!string.IsNullOrWhiteSpace(input.EventName), (r, e, u, s, m) => e.EventName.Contains(input.EventName))
.WhereIF(input.StartTime.HasValue, (r, e, u, s, m) => e.MatchStartTime >= input.StartTime)
.WhereIF(input.EndTime.HasValue, (r, e, u, s, m) => e.MatchStartTime <= input.EndTime)
.WhereIF(input.PaymentStatus.HasValue, (r, e, u, s, m) => r.PaymentStatus == input.PaymentStatus)
// Filter by User ID (AppUser, SysUser, or Merchant)
.WhereIF(input.AppUserId.HasValue, (r, e, u, s, m) => u.Id == input.AppUserId)
.WhereIF(input.SysUserId.HasValue, (r, e, u, s, m) => s.Id == input.SysUserId)
.WhereIF(input.MerchantId.HasValue, (r, e, u, s, m) => m.CreatedUserId == input.MerchantId)
2026-05-07 08:33:53 +08:00
.OrderByDescending((r, e, u, s, m) => e.CreatedTime)
2026-04-27 13:22:59 +08:00
.Select((r, e, u, s, m) => new
2026-03-06 12:10:09 +08:00
{
2026-04-27 13:22:59 +08:00
RegistrationId = r.Id,
EventId = e.Id,
2026-03-06 12:10:09 +08:00
EventName = e.EventName,
EventType = e.EventType.ToString(),
StartTime = e.MatchStartTime,
EndTime = e.MatchEndTime,
2026-04-09 23:12:51 +08:00
PaymentStatus = r.PaymentStatus,
2026-03-06 12:29:07 +08:00
ContactName = r.ContactName,
ContactPhone = r.ContactPhone,
2026-04-27 13:22:59 +08:00
TeamName = r.TeamName,
2026-05-07 08:33:53 +08:00
CreatedTime = r.CreatedTime,
EventCreatedTime = e.CreatedTime
2026-03-06 12:10:09 +08:00
});
2026-04-27 13:22:59 +08:00
var allEventRecords = await eventsQuery.ToListAsync();
// 第二步:按赛事分组并聚合
var groupedEvents = allEventRecords
.GroupBy(x => x.EventId)
.Select(g => {
// 获取该赛事最早的报名记录作为主记录
var firstRecord = g.OrderBy(x => x.CreatedTime).First();
2026-05-09 18:04:19 +08:00
// 收集该赛事的所有报名记录ID(去重)
var registrationIds = g.Select(x => x.RegistrationId).Distinct().ToList();
2026-04-27 13:22:59 +08:00
return new EventRecordOutput
{
// 用赛事ID作为主要ID(避免分页问题)
Id = firstRecord.EventId,
EventName = firstRecord.EventName,
EventType = firstRecord.EventType,
StartTime = firstRecord.StartTime,
EndTime = firstRecord.EndTime,
PaymentStatus = firstRecord.PaymentStatus,
EventId = firstRecord.EventId,
ContactName = firstRecord.ContactName,
ContactPhone = firstRecord.ContactPhone,
TeamName = firstRecord.TeamName,
2026-05-07 08:33:53 +08:00
RegistrationIds = registrationIds, // 保存所有报名ID用于查询证书
// 保存赛事创建时间用于排序
EventCreatedTime = g.First().EventCreatedTime
2026-04-27 13:22:59 +08:00
};
})
2026-05-07 08:33:53 +08:00
.OrderByDescending(x => x.EventCreatedTime)
2026-04-27 13:22:59 +08:00
.ToList();
// 第三步:手动分页
var pageIndex = input.PageNo;
var pageSize = input.PageSize;
var total = groupedEvents.Count;
var pages = (int)Math.Ceiling((double)total / pageSize);
var pagedEvents = groupedEvents
.Skip((pageIndex - 1) * pageSize)
.Take(pageSize)
.ToList();
// 第四步:获取所有需要的报名记录ID(包括所有页面的,避免数据不完整)
var allRegistrationIds = groupedEvents.SelectMany(x => x.RegistrationIds).Distinct().ToList();
2026-05-09 14:37:27 +08:00
// 第五步:获取所有证书
var certificates = await _certificateRep.GetListAsync(x =>
allRegistrationIds.Contains(x.RegistrationId) && x.CertificateStatus == 2);
// 按赛事ID和报名ID分组证书
var certsByRegistration = certificates.GroupBy(x => x.RegistrationId).ToDictionary(g => g.Key, g => g.ToList());
2026-04-27 13:22:59 +08:00
2026-05-09 14:37:27 +08:00
// 第六步:获取所有相关数据用于补充证书信息
var allCertIds = certificates.Select(x => x.Id).Distinct().ToList();
var registrationItems = await _registrationItemRep.GetListAsync(x => allRegistrationIds.Contains(x.RegistrationId));
var registrationItemDict = registrationItems.GroupBy(x => x.RegistrationId).ToDictionary(g => g.Key, g => g.ToList());
var registrationPersons = await _registrationPersonRep.GetListAsync(x => allRegistrationIds.Contains(x.RegistrationId));
var registrationPersonDict = registrationPersons.ToDictionary(x => x.Id, x => x);
var results = await _resultRep.GetListAsync(x => allRegistrationIds.Contains(x.RegistrationId));
var resultDict = results.GroupBy(x => x.RegistrationPersonId).ToDictionary(g => g.Key, g => g.ToList());
var allEventProjectIds = registrationItems.Select(x => x.ProjectId).Distinct().ToList();
2026-05-07 08:33:53 +08:00
var eventProjects = await _eventProjectRep.GetListAsync(x => allEventProjectIds.Contains(x.Id));
2026-05-09 14:37:27 +08:00
var allProjectCoreIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
var projects = await _projectRep.GetListAsync(x => allProjectCoreIds.Contains(x.Id));
2026-05-07 08:33:53 +08:00
var eventProjectDict = eventProjects.ToDictionary(x => x.Id, x => x.ProjectId);
var projectDict = projects.ToDictionary(x => x.Id, x => x);
2026-04-27 13:22:59 +08:00
2026-05-09 14:37:27 +08:00
// 第七步:为每个赛事填充证书列表
foreach (var eventRecord in pagedEvents)
2026-04-27 13:22:59 +08:00
{
2026-05-09 14:37:27 +08:00
// 获取该赛事所有报名记录的证书
var allCerts = new List<EventCertificate>();
foreach (var regId in eventRecord.RegistrationIds)
2026-04-27 13:22:59 +08:00
{
2026-05-09 14:37:27 +08:00
if (certsByRegistration.TryGetValue(regId, out var certs))
2026-05-07 08:33:53 +08:00
{
2026-05-09 14:37:27 +08:00
allCerts.AddRange(certs);
2026-05-07 08:33:53 +08:00
}
2026-05-09 14:37:27 +08:00
}
// 构造证书输出列表
foreach (var cert in allCerts)
{
var certOutput = new EventCertificateItemOutput
2026-05-07 08:33:53 +08:00
{
2026-05-09 14:37:27 +08:00
Id = cert.Id,
ParticipantName = cert.ParticipantName ?? string.Empty,
ProjectName = string.Empty,
GroupName = cert.GroupName ?? string.Empty,
Role = "运动员",
CertificateUrl = cert.CertificateUrl?.Trim() ?? string.Empty,
CertificateNo = cert.CertificateNo ?? string.Empty,
CertificateType = cert.CertificateType,
IssuedTime = cert.IssuedTime ?? DateTime.MinValue
};
2026-04-27 13:22:59 +08:00
2026-05-09 14:37:27 +08:00
// 获取项目名称
if (cert.ProjectId > 0)
{
// 查找对应的报名项目
if (registrationItemDict.TryGetValue(cert.RegistrationId, out var items))
{
var item = items.FirstOrDefault(i => i.ProjectId == cert.ProjectId);
if (item != null)
2026-05-07 08:33:53 +08:00
{
2026-05-09 14:37:27 +08:00
certOutput.ProjectName = item.ProjectName ?? string.Empty;
// 查找项目信息
if (eventProjectDict.TryGetValue(item.ProjectId, out var coreProjectId) &&
projectDict.TryGetValue(coreProjectId, out var coreProject))
2026-05-07 08:33:53 +08:00
{
2026-05-09 14:37:27 +08:00
certOutput.ProjectName = coreProject.Name ?? item.ProjectName ?? string.Empty;
}
}
2026-05-07 08:33:53 +08:00
}
}
2026-05-09 14:37:27 +08:00
// 获取角色信息
if (cert.ParticipationRecordId > 0 && registrationPersonDict.TryGetValue(cert.ParticipationRecordId, out var person))
2026-04-27 13:22:59 +08:00
{
2026-05-09 14:37:27 +08:00
certOutput.Role = person.PersonType switch
2026-05-07 08:33:53 +08:00
{
2026-05-09 14:37:27 +08:00
PersonType.Coach => "教练",
PersonType.Leader => "领队",
_ => "运动员"
};
}
2026-05-07 08:33:53 +08:00
2026-05-09 14:37:27 +08:00
// 获取成绩和名次
if (cert.ResultId > 0)
{
var result = results.FirstOrDefault(r => r.Id == cert.ResultId);
if (result != null)
{
certOutput.Score = result.Score;
certOutput.Rank = result.Rank;
}
}
else if (cert.ParticipationRecordId > 0 && resultDict.TryGetValue(cert.ParticipationRecordId, out var personResults))
{
var result = personResults.FirstOrDefault();
if (result != null)
2026-05-07 08:33:53 +08:00
{
2026-05-09 14:37:27 +08:00
certOutput.Score = result.Score;
certOutput.Rank = result.Rank;
}
2026-05-07 08:33:53 +08:00
}
2026-05-09 14:37:27 +08:00
eventRecord.Items.Add(certOutput);
2026-04-27 13:22:59 +08:00
}
}
// 第八步:构造分页结果
var pageResult = new PageResult<EventRecordOutput>
{
Total = total,
PageNo = pageIndex,
PageSize = pageSize,
Pages = pages,
Rows = pagedEvents
};
return pageResult;
}
2026-05-07 08:33:53 +08:00
2026-03-06 12:10:09 +08:00
2026-04-13 06:58:21 +08:00
/// <summary>
/// 验证输入参数
/// </summary>
private void ValidateInput(EventRecordPageInput input)
{
// 检查是否同时提供了多个用户ID类型
var userTypeCount = 0;
if (input.AppUserId.HasValue) userTypeCount++;
if (input.SysUserId.HasValue) userTypeCount++;
if (input.MerchantId.HasValue) userTypeCount++;
if (userTypeCount > 1)
{
throw Oops.Bah("只能提供一种用户类型ID参数(AppUserId、SysUserId或MerchantId)");
}
// 验证用户ID有效性
if (input.AppUserId.HasValue && input.AppUserId.Value <= 0)
{
throw Oops.Bah("普通用户ID无效");
}
if (input.SysUserId.HasValue && input.SysUserId.Value <= 0)
{
throw Oops.Bah("系统用户ID无效");
}
if (input.MerchantId.HasValue && input.MerchantId.Value <= 0)
{
throw Oops.Bah("商户ID无效");
}
}
2026-03-06 12:10:09 +08:00
/// <summary>
/// 获取证书列表
/// </summary>
2026-04-27 13:22:59 +08:00
[HttpGet("certificates")]
public async Task<List<CertificateOutput>> GetCertificates([FromQuery] List<long> registrationIds)
2026-03-06 12:10:09 +08:00
{
var certs = await _certificateRep.AsQueryable()
2026-04-27 13:22:59 +08:00
.Where(c => registrationIds.Contains(c.RegistrationId))
2026-03-06 12:10:09 +08:00
.ToListAsync();
return certs.Select(c => new CertificateOutput
{
Id = c.Id,
Name = c.ParticipantName,
AwardLevel = GetAwardLevelName(c.AwardLevel),
CertificateType = GetCertificateTypeName(c.CertificateType),
ImageUrl = c.CertificateUrl
}).ToList();
}
private string GetAwardLevelName(int level)
{
return level switch
{
1 => "特等奖",
2 => "一等奖",
3 => "二等奖",
4 => "三等奖",
5 => "优秀奖",
_ => "未知"
};
}
private string GetCertificateTypeName(int type)
{
return type switch
{
1 => "冠军证书",
2 => "亚军证书",
3 => "季军证书",
4 => "优胜证书",
5 => "参与证书",
_ => "未知"
};
}
}