YD_Event.Admin/YD_Event.Application/Event/EventRecordService.cs
马梓彭 2e615d43c6 赛事统一查询规则
- 线上支付 (PaymentMethod == 2) :只显示已支付 (PaymentStatus == 1) 的记录
- 线下支付 (PaymentMethod == 1) :显示待审核 (PaymentStatus == 0) 和已支付 (PaymentStatus == 1) 的记录
2026-05-12 10:51:12 +08:00

363 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using YD_Event.Application.Event.Dtos;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using AppUserEntity = YD_Event.Core.Entities.AppUser;
using SysUserEntity = YD_Event.Core.Entities.SysUser;
using MerchantEntity = YD_Event.Core.Entities.Merchant;
namespace YD_Event.Application.Event;
/// <summary>
/// 参赛记录管理
/// </summary>
public class EventRecordService : BaseService<EventRegistration>, ITransient
{
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]}名";
}
private readonly ISqlSugarRepository<EventRegistration> _registrationRep;
private readonly ISqlSugarRepository<EventInfo> _eventRep;
private readonly ISqlSugarRepository<EventCertificate> _certificateRep;
private readonly ISqlSugarRepository<AppUserEntity> _appUserRep;
private readonly ISqlSugarRepository<SysUserEntity> _sysUserRep;
private readonly ISqlSugarRepository<MerchantEntity> _merchantRep;
private readonly ISqlSugarRepository<EventRegistrationItem> _registrationItemRep;
private readonly ISqlSugarRepository<EventResult> _resultRep;
private readonly ISqlSugarRepository<EventRegistrationPerson> _registrationPersonRep;
private readonly ISqlSugarRepository<EventProject> _eventProjectRep;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRep;
public EventRecordService(ISqlSugarRepository<EventRegistration> registrationRep,
ISqlSugarRepository<EventInfo> eventRep,
ISqlSugarRepository<EventCertificate> certificateRep,
ISqlSugarRepository<AppUserEntity> appUserRep,
ISqlSugarRepository<SysUserEntity> sysUserRep,
ISqlSugarRepository<MerchantEntity> merchantRep,
ISqlSugarRepository<EventRegistrationItem> registrationItemRep,
ISqlSugarRepository<EventResult> resultRep,
ISqlSugarRepository<EventRegistrationPerson> registrationPersonRep,
ISqlSugarRepository<EventProject> eventProjectRep,
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRep) : base(registrationRep)
{
_registrationRep = registrationRep;
_eventRep = eventRep;
_certificateRep = certificateRep;
_appUserRep = appUserRep;
_sysUserRep = sysUserRep;
_merchantRep = merchantRep;
_registrationItemRep = registrationItemRep;
_resultRep = resultRep;
_registrationPersonRep = registrationPersonRep;
_eventProjectRep = eventProjectRep;
_projectRep = projectRep;
}
/// <summary>
/// 分页查询参赛记录
/// </summary>
[HttpGet("page")]
public async Task<PageResult<EventRecordOutput>> Page([FromQuery] EventRecordPageInput input)
{
// 参数验证
ValidateInput(input);
// 第一步:查询用户报名过的所有赛事(去重,按赛事分组)
var eventsQuery = _registrationRep.AsQueryable()
.LeftJoin<EventInfo>((r, e) => r.EventId == e.Id)
.LeftJoin<AppUserEntity>((r, e, u) => r.CreatedUserId == u.Id)
.LeftJoin<SysUserEntity>((r, e, u, s) => r.CreatedUserId == s.Id)
.LeftJoin<MerchantEntity>((r, e, u, s, m) => r.CreatedUserId == m.CreatedUserId)
// 线上支付只显示已支付(1),线下支付显示待审核(0)和已支付(1)
.Where((r, e, u, s, m) => (r.PaymentMethod == 2 && r.PaymentStatus == 1) || (r.PaymentMethod == 1 && (r.PaymentStatus == 0 || r.PaymentStatus == 1)))
.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)
.OrderByDescending((r, e, u, s, m) => e.CreatedTime)
.Select((r, e, u, s, m) => new
{
RegistrationId = r.Id,
EventId = e.Id,
EventName = e.EventName,
EventType = e.EventType.ToString(),
StartTime = e.MatchStartTime,
EndTime = e.MatchEndTime,
PaymentStatus = r.PaymentStatus,
ContactName = r.ContactName,
ContactPhone = r.ContactPhone,
TeamName = r.TeamName,
CreatedTime = r.CreatedTime,
EventCreatedTime = e.CreatedTime
});
var allEventRecords = await eventsQuery.ToListAsync();
// 第二步:按赛事分组并聚合
var groupedEvents = allEventRecords
.GroupBy(x => x.EventId)
.Select(g => {
// 获取该赛事最早的报名记录作为主记录
var firstRecord = g.OrderBy(x => x.CreatedTime).First();
// 收集该赛事的所有报名记录ID(去重)
var registrationIds = g.Select(x => x.RegistrationId).Distinct().ToList();
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,
RegistrationIds = registrationIds, // 保存所有报名ID用于查询证书
// 保存赛事创建时间用于排序
EventCreatedTime = g.First().EventCreatedTime
};
})
.OrderByDescending(x => x.EventCreatedTime)
.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();
// 第五步:获取所有证书
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());
// 第六步:获取所有相关数据用于补充证书信息
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();
var eventProjects = await _eventProjectRep.GetListAsync(x => allEventProjectIds.Contains(x.Id));
var allProjectCoreIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
var projects = await _projectRep.GetListAsync(x => allProjectCoreIds.Contains(x.Id));
var eventProjectDict = eventProjects.ToDictionary(x => x.Id, x => x.ProjectId);
var projectDict = projects.ToDictionary(x => x.Id, x => x);
// 第七步:为每个赛事填充证书列表
foreach (var eventRecord in pagedEvents)
{
// 获取该赛事所有报名记录的证书
var allCerts = new List<EventCertificate>();
foreach (var regId in eventRecord.RegistrationIds)
{
if (certsByRegistration.TryGetValue(regId, out var certs))
{
allCerts.AddRange(certs);
}
}
// 构造证书输出列表
foreach (var cert in allCerts)
{
var certOutput = new EventCertificateItemOutput
{
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
};
// 获取项目名称
if (cert.ProjectId > 0)
{
// 查找对应的报名项目
if (registrationItemDict.TryGetValue(cert.RegistrationId, out var items))
{
var item = items.FirstOrDefault(i => i.ProjectId == cert.ProjectId);
if (item != null)
{
certOutput.ProjectName = item.ProjectName ?? string.Empty;
// 查找项目信息
if (eventProjectDict.TryGetValue(item.ProjectId, out var coreProjectId) &&
projectDict.TryGetValue(coreProjectId, out var coreProject))
{
certOutput.ProjectName = coreProject.Name ?? item.ProjectName ?? string.Empty;
}
}
}
}
// 获取角色信息
if (cert.ParticipationRecordId > 0 && registrationPersonDict.TryGetValue(cert.ParticipationRecordId, out var person))
{
certOutput.Role = person.PersonType switch
{
PersonType.Coach => "教练",
PersonType.Leader => "领队",
_ => "运动员"
};
}
// 获取成绩和名次
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)
{
certOutput.Score = result.Score;
certOutput.Rank = result.Rank;
}
}
eventRecord.Items.Add(certOutput);
}
}
// 第八步:构造分页结果
var pageResult = new PageResult<EventRecordOutput>
{
Total = total,
PageNo = pageIndex,
PageSize = pageSize,
Pages = pages,
Rows = pagedEvents
};
return pageResult;
}
/// <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无效");
}
}
/// <summary>
/// 获取证书列表
/// </summary>
[HttpGet("certificates")]
public async Task<List<CertificateOutput>> GetCertificates([FromQuery] List<long> registrationIds)
{
var certs = await _certificateRep.AsQueryable()
.Where(c => registrationIds.Contains(c.RegistrationId))
.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 => "参与证书",
_ => "未知"
};
}
}