YD_Event.Admin/YD_Event.Application/Event/EventRecordService.cs
2026-05-07 08:33:53 +08:00

374 lines
16 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)
.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).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 items = await _registrationItemRep.AsQueryable()
.Where(i => allRegistrationIds.Contains(i.RegistrationId))
.ToListAsync();
// 第六步:获取所有成绩
var results = await _resultRep.AsQueryable()
.Where(r => allRegistrationIds.Contains(r.RegistrationId))
.ToListAsync();
// 第七步:获取所有报名人员
var registrationPersons = await _registrationPersonRep.AsQueryable()
.Where(p => allRegistrationIds.Contains(p.RegistrationId))
.ToListAsync();
// 第八步:获取所有 EventProject 和 Project 信息,用于判断项目类型
var allEventProjectIds = items.Select(x => x.ProjectId).Distinct().ToList();
var eventProjects = await _eventProjectRep.GetListAsync(x => allEventProjectIds.Contains(x.Id));
var allProjectIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
var projects = await _projectRep.GetListAsync(x => allProjectIds.Contains(x.Id));
var eventProjectDict = eventProjects.ToDictionary(x => x.Id, x => x.ProjectId);
var projectDict = projects.ToDictionary(x => x.Id, x => x);
// 第九步:将项目、成绩和队员姓名数据填充到结果中
foreach (var row in pagedEvents)
{
// 获取该赛事所有报名记录的项目
var registrationItems = items.Where(i => row.RegistrationIds.Contains(i.RegistrationId)).ToList();
foreach (var item in registrationItems)
{
// 查找对应的成绩
var result = results.FirstOrDefault(r =>
r.RegistrationId == item.RegistrationId &&
r.ProjectId == item.ProjectId &&
r.GroupName == item.GroupName);
// 查找该项目的所有队员
var members = registrationPersons
.Where(p => p.RegistrationId == item.RegistrationId && p.ProjectId == item.ProjectId && p.GroupName == item.GroupName)
.ToList();
// 判断项目类型
bool isIndividualProject = false;
int projectType = 0;
if (eventProjectDict.TryGetValue(item.ProjectId, out var projectId) &&
projectDict.TryGetValue(projectId, out var project))
{
isIndividualProject = project.ProjectType == ProjectTypeEnum.PersonalSpeed ||
project.ProjectType == ProjectTypeEnum.PersonalFreestyle;
projectType = (int)project.ProjectType;
}
if (isIndividualProject)
{
// 个人项目:每个运动员单独一行
foreach (var member in members)
{
// 查找该成员对应的成绩
var memberResult = results.FirstOrDefault(r =>
r.RegistrationId == item.RegistrationId &&
r.ProjectId == item.ProjectId &&
r.GroupName == item.GroupName &&
r.RegistrationPersonId == member.Id);
row.Items.Add(new EventRegistrationItemOutput
{
Id = member.Id, // 使用队员ID作为条目ID
ProjectName = item.ProjectName,
ProjectTypeName = item.ProjectTypeName,
GroupName = item.GroupName,
PersonCount = item.PersonCount,
Score = memberResult?.Score,
Rank = memberResult?.Rank > 0 ? ConvertRankToChinese(memberResult.Rank) : null,
MemberNames = member.Name, // 单个姓名
MemberIds = new List<long> { member.Id }, // 单个队员ID
Members = new List<MemberInfo>
{
new MemberInfo
{
Id = member.Id,
Name = member.Name,
PersonType = (int)member.PersonType
}
},
IsIndividualProject = isIndividualProject,
ProjectType = projectType
});
}
}
else
{
// 团队项目:所有队员一行
string? memberNames = members.Count > 0 ? string.Join(", ", members.Select(m => m.Name)) : null;
List<long>? memberIds = members.Count > 0 ? members.Select(m => m.Id).ToList() : null;
var memberInfoList = members.Select(m => new MemberInfo
{
Id = m.Id,
Name = m.Name,
PersonType = (int)m.PersonType
}).ToList();
row.Items.Add(new EventRegistrationItemOutput
{
Id = item.Id,
ProjectName = item.ProjectName,
ProjectTypeName = item.ProjectTypeName,
GroupName = item.GroupName,
PersonCount = item.PersonCount,
Score = result?.Score,
Rank = result?.Rank > 0 ? ConvertRankToChinese(result.Rank) : null,
MemberNames = memberNames,
MemberIds = memberIds,
Members = memberInfoList,
IsIndividualProject = isIndividualProject,
ProjectType = projectType
});
}
}
}
// 第八步:构造分页结果
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 => "参与证书",
_ => "未知"
};
}
}