using YD_Event.Application.Event.Dtos;
using YD_Event.Application.Auth;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using MiniExcelLibs;
using YD_Event.Core.Domain;
namespace YD_Event.Application.Event;
///
/// 报名管理服务
///
public class EventRegistrationService : BaseService, ITransient
{
private readonly ISqlSugarRepository _repository;
private readonly ISqlSugarRepository _itemRepository;
private readonly ISqlSugarRepository _personRepository;
private readonly ISqlSugarRepository _paymentRepository;
private readonly ISqlSugarRepository _orderRepository;
private readonly ISqlSugarRepository _teamMemberRepository;
private readonly ISqlSugarRepository _appUserRepository;
private readonly ISqlSugarRepository _merchantCertificationRepository;
private readonly ISqlSugarRepository _eventInfoRepository;
private readonly ISqlSugarRepository _eventProjectRepository;
private readonly ISqlSugarRepository _projectRepository;
private readonly IIdGenerator _idGenerator;
private readonly OrderNoGeneratorService _orderNoGeneratorService;
private readonly AuthManager _authManager;
public EventRegistrationService(
ISqlSugarRepository repository,
ISqlSugarRepository itemRepository,
ISqlSugarRepository personRepository,
ISqlSugarRepository paymentRepository,
ISqlSugarRepository orderRepository,
ISqlSugarRepository teamMemberRepository,
ISqlSugarRepository appUserRepository,
ISqlSugarRepository merchantCertificationRepository,
ISqlSugarRepository eventInfoRepository,
ISqlSugarRepository eventProjectRepository,
ISqlSugarRepository projectRepository,
IIdGenerator idGenerator,
OrderNoGeneratorService orderNoGeneratorService,
AuthManager authManager) : base(repository)
{
_repository = repository;
_itemRepository = itemRepository;
_personRepository = personRepository;
_paymentRepository = paymentRepository;
_orderRepository = orderRepository;
_teamMemberRepository = teamMemberRepository;
_appUserRepository = appUserRepository;
_merchantCertificationRepository = merchantCertificationRepository;
_eventInfoRepository = eventInfoRepository;
_eventProjectRepository = eventProjectRepository;
_projectRepository = projectRepository;
_idGenerator = idGenerator;
_orderNoGeneratorService = orderNoGeneratorService;
_authManager = authManager;
}
///
/// 分页查询报名
///
[DisplayName("分页查询报名")]
public async Task> Page(RegistrationQueryInput input)
{
var query = _repository.AsQueryable()
.Where(x => x.PaymentStatus != 4) // 过滤掉已取消的报名记录
.WhereIF(input.EventId.HasValue, x => x.EventId == input.EventId)
.WhereIF(input.RegistrationMode.HasValue, x => x.RegistrationMode == input.RegistrationMode)
.WhereIF(!string.IsNullOrWhiteSpace(input.UnitName), x => x.TeamName.Contains(input.UnitName))
.WhereIF(!string.IsNullOrWhiteSpace(input.UserName), x => x.ContactName.Contains(input.UserName))
.WhereIF(!string.IsNullOrWhiteSpace(input.ContactPhone), x => x.ContactPhone.Contains(input.ContactPhone))
.WhereIF(input.IsUploadMaterial.HasValue, x => (input.IsUploadMaterial.Value ? !string.IsNullOrEmpty(x.MaterialUrl) : string.IsNullOrEmpty(x.MaterialUrl)))
.WhereIF(input.PaymentMethod.HasValue, x => x.PaymentMethod == input.PaymentMethod)
.WhereIF(input.PaymentStatus.HasValue, x => x.PaymentStatus == input.PaymentStatus)
.WhereIF(input.RegistrationTimeStart.HasValue, x => x.RegistrationTime >= input.RegistrationTimeStart)
.WhereIF(input.RegistrationTimeEnd.HasValue, x => x.RegistrationTime <= input.RegistrationTimeEnd)
.OrderByDescending(x => x.RegistrationTime)
.Select(x => new RegistrationListOutput
{
Id = x.Id,
RegistrationMode = x.RegistrationMode,
CreatedUserId = x.CreatedUserId, // 保存创建用户ID以便后续查询单位名称
MerchantCertificationId = x.MerchantCertificationId, // 保存单位认证ID以便后续查询单位名称
UnitName = null, // 不初始化为 TeamName,只从单位认证信息中获取
ContactName = x.ContactName,
ContactPhone = x.ContactPhone,
TotalPersonCount = x.TotalPersonCount,
IsUploadMaterial = !string.IsNullOrEmpty(x.MaterialUrl),
PaymentMethod = x.PaymentMethod,
PaymentMethodType = x.PaymentMethodType,
OrderNo = x.OrderNo,
TotalAmount = x.TotalAmount,
PaymentStatus = x.PaymentStatus,
PaymentProofUrl = x.PaymentProofUrl,
RegistrationTime = x.RegistrationTime
});
var result = await query.ToPagedListAsync(input);
// 获取所有报名记录的 MerchantCertificationId
var regIds = result.Rows.Select(x => x.Id).ToList();
var merchantCertIds = result.Rows
.Where(r => r.MerchantCertificationId.HasValue)
.Select(r => r.MerchantCertificationId.Value)
.Distinct()
.ToList();
// 获取单位认证信息
Dictionary merchantNameDict = new Dictionary();
if (merchantCertIds.Any())
{
var merchantCerts = await _merchantCertificationRepository.AsQueryable()
.Where(mc => merchantCertIds.Contains(mc.Id) && !mc.DeleteMark)
.ToListAsync();
merchantNameDict = merchantCerts.ToDictionary(mc => mc.Id, mc => mc.MerchantName);
}
// 填充下拉详情
if (result.Rows.Any())
{
var items = await _itemRepository.GetListAsync(x => regIds.Contains(x.RegistrationId) && !x.DeleteMark);
// 使用关联查询获取报名人员及其关联的用户信息
var persons = await _personRepository.AsQueryable()
.Includes(p => p.User) // 关联查询用户信息
.Where(x => regIds.Contains(x.RegistrationId) && !x.DeleteMark)
.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 projectDict = projects.ToDictionary(x => x.Id, x => x);
// 获取所有报名人员的 UserId
var allAthleteUserIds = persons
.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Athlete && p.UserId > 0)
.Select(p => p.UserId)
.Distinct()
.ToList();
// 步骤1:通过运动员的 UserId 找到对应的 TeamId
var teamIds = new List();
if (allAthleteUserIds.Any())
{
teamIds = await _teamMemberRepository.AsQueryable()
.Where(tm => allAthleteUserIds.Contains(tm.UserId) && !tm.DeleteMark)
.Select(tm => tm.TeamId)
.Distinct()
.ToListAsync();
}
// 步骤2:通过 TeamId 获取同团队的教练/领队的 UserId,并获取用户详情
Dictionary> teamLeadersDict = new();
Dictionary> teamCoachesDict = new();
if (teamIds.Any())
{
var teamMembers = await _teamMemberRepository.AsQueryable()
.Where(tm => teamIds.Contains(tm.TeamId) && !tm.DeleteMark)
.ToListAsync();
var coachLeaderUserIds = teamMembers
.Where(tm => tm.PersonType == YD_Event.Core.Enum.PersonType.Leader ||
tm.PersonType == YD_Event.Core.Enum.PersonType.Coach)
.Select(tm => tm.UserId)
.Distinct()
.ToList();
if (coachLeaderUserIds.Any())
{
var coachLeaderUsers = await _appUserRepository.AsQueryable()
.Where(u => coachLeaderUserIds.Contains(u.Id) && !u.DeleteMark)
.ToListAsync();
var userDict = coachLeaderUsers.ToDictionary(u => u.Id);
// 按团队分组
foreach (var teamId in teamIds)
{
var teamLeaders = teamMembers
.Where(tm => tm.TeamId == teamId && tm.PersonType == YD_Event.Core.Enum.PersonType.Leader)
.Select(tm => userDict.TryGetValue(tm.UserId, out var user) ? user : null)
.Where(u => u != null)
.Cast()
.ToList();
var teamCoaches = teamMembers
.Where(tm => tm.TeamId == teamId && tm.PersonType == YD_Event.Core.Enum.PersonType.Coach)
.Select(tm => userDict.TryGetValue(tm.UserId, out var user) ? user : null)
.Where(u => u != null)
.Cast()
.ToList();
if (teamLeaders.Any())
teamLeadersDict[teamId] = teamLeaders;
if (teamCoaches.Any())
teamCoachesDict[teamId] = teamCoaches;
}
}
}
foreach (var dto in result.Rows)
{
// 获取当前报名的所有人员
var regPersons = persons.Where(p => p.RegistrationId == dto.Id).ToList();
// 根据报名记录的 MerchantCertificationId 获取单位名称
if (dto.MerchantCertificationId.HasValue &&
merchantNameDict.TryGetValue(dto.MerchantCertificationId.Value, out var unitName))
{
dto.UnitName = unitName;
}
else
{
dto.UnitName = null;
}
var myItems = items.Where(x => x.RegistrationId == dto.Id).ToList();
dto.Items = new List();
foreach (var item in myItems)
{
var myPersons = persons.Where(p => p.RegistrationItemId == item.Id).ToList();
// 判断项目类型
bool isIndividualProject = false;
if (eventProjectToProjectDict.TryGetValue(item.ProjectId, out var projectId) &&
projectDict.TryGetValue(projectId, out var project))
{
isIndividualProject = YD_Event.Core.Extensions.ProjectTypeExtensions.IsIndividualProject(project);
}
if (isIndividualProject)
{
// 个人项目:为每个运动员创建一个条目,使用 EventRegistrationPerson.Id
foreach (var person in myPersons.Where(p => p.PersonType == PersonType.Athlete))
{
// 构造参与人信息字符串
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
{
Id = person.Id, // 使用 EventRegistrationPerson.Id
ProjectName = item.ProjectName,
GroupName = item.GroupName,
ParticipantInfo = personInfo
};
dto.Items.Add(detail);
}
}
else
{
// 团队项目:保持原有逻辑,使用 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);
}
}
// 设置领队信息和教练信息 - 从团队中获取
var regAthleteUserIds = regPersons
.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Athlete && p.UserId > 0)
.Select(p => p.UserId)
.Distinct()
.ToList();
// 获取这些运动员所属的团队
var regTeamIds = new List();
if (regAthleteUserIds.Any())
{
regTeamIds = await _teamMemberRepository.AsQueryable()
.Where(tm => regAthleteUserIds.Contains(tm.UserId) && !tm.DeleteMark)
.Select(tm => tm.TeamId)
.Distinct()
.ToListAsync();
}
// 收集所有相关团队的领队和教练
var allLeaders = new List();
var allCoaches = new List();
foreach (var teamId in regTeamIds)
{
if (teamLeadersDict.TryGetValue(teamId, out var leaders))
allLeaders.AddRange(leaders);
if (teamCoachesDict.TryGetValue(teamId, out var coaches))
allCoaches.AddRange(coaches);
}
// 去重
allLeaders = allLeaders.GroupBy(l => l.Id).Select(g => g.First()).ToList();
allCoaches = allCoaches.GroupBy(c => c.Id).Select(g => g.First()).ToList();
// 如果从团队中没有找到,再回退到从报名人员中查找
if (!allLeaders.Any())
{
var regLeaders = regPersons.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Leader).ToList();
allLeaders = regLeaders.Select(l => new YD_Event.Core.Entities.AppUser
{
Name = l.Name,
Mobile = l.Phone,
Gender = l.Gender
}).ToList();
}
if (!allCoaches.Any())
{
var regCoaches = regPersons.Where(p => p.PersonType == YD_Event.Core.Enum.PersonType.Coach).ToList();
allCoaches = regCoaches.Select(c => new YD_Event.Core.Entities.AppUser
{
Name = c.Name,
Mobile = c.Phone,
Gender = c.Gender
}).ToList();
}
// 辅助函数:获取性别文本
Func getGenderText = (gender) =>
{
return gender switch
{
YD_Event.Core.Enum.Gender.Male => "男",
YD_Event.Core.Enum.Gender.Female => "女",
_ => "未知"
};
};
dto.TeamLeaderInfo = allLeaders.Any()
? string.Join(",", allLeaders.Select(l => $"{l.Name}/{getGenderText(l.Gender)}/{l.Mobile}"))
: null;
dto.CoachInfo = allCoaches.Any()
? string.Join(",", allCoaches.Select(c => $"{c.Name}/{getGenderText(c.Gender)}/{c.Mobile}"))
: null;
}
}
return result;
}
///
/// 为报名记录创建订单(如果不存在)
///
private async Task CreateOrderIfNotExistsAsync(EventRegistration registration, DateTime now)
{
// 检查是否已存在订单
var existingOrder = await _orderRepository.GetFirstAsync(o =>
o.RegistrationId == registration.Id && !o.DeleteMark);
if (existingOrder == null)
{
// 不存在订单,先检查是否有订单号
if (string.IsNullOrEmpty(registration.OrderNo))
{
registration.OrderNo = await _orderNoGeneratorService.GenerateOrderNoAsync();
await _repository.UpdateAsync(registration);
}
// 获取赛事信息
var eventInfo = await _eventInfoRepository.GetByIdAsync(registration.EventId);
// 获取用户单位认证信息
var certification = await _merchantCertificationRepository.GetFirstAsync(c =>
c.UserId == registration.CreatedUserId && c.AuditStatus == AuditStatus.Pass && !c.DeleteMark);
// 创建订单
var order = new YD_Event.Core.Entities.Order
{
Id = _idGenerator.NewLong(),
OrderNo = registration.OrderNo,
OrderType = 1,
MerchantId = eventInfo?.MerchantId,
MerchantCertificationId = certification?.Id,
EventId = registration.EventId,
RegistrationId = registration.Id,
OriginalAmount = registration.TotalAmount,
ActualAmount = registration.TotalAmount,
DiscountAmount = 0,
CouponId = null,
OrderStatus = OrderStatus.Paid,
PaymentTime = now,
CreatedTime = now,
CreatedUserId = _authManager.UserId,
UpdatedTime = now,
UpdatedUserId = _authManager.UserId,
Status = AvailabilityStatus.Enable
};
await _orderRepository.InsertAsync(order);
}
else
{
// 订单已存在,只更新状态为已支付
if (existingOrder.OrderStatus != OrderStatus.Paid)
{
existingOrder.OrderStatus = OrderStatus.Paid;
existingOrder.PaymentTime = now;
existingOrder.UpdatedTime = now;
existingOrder.UpdatedUserId = _authManager.UserId;
await _orderRepository.UpdateAsync(existingOrder);
}
}
}
///
/// 批量审核
///
[UnitOfWork]
[DisplayName("批量审核")]
public async Task BatchAudit(BatchAuditInput input)
{
if (input.Ids == null || !input.Ids.Any()) return;
var now = DateTime.Now;
// 如果审核通过(PaymentStatus = 1),先处理订单创建和更新
if (input.PaymentStatus == 1)
{
// 获取需要更新的报名记录
var registrations = await _repository.GetListAsync(x => input.Ids.Contains(x.Id));
foreach (var registration in registrations)
{
// 确保订单存在
await CreateOrderIfNotExistsAsync(registration, now);
// 更新报名记录的支付状态
registration.PaymentStatus = input.PaymentStatus;
registration.ReviewRemark = input.ReviewRemark;
registration.ReviewTime = now;
registration.PaymentTime = now;
registration.UpdatedTime = now;
registration.UpdatedUserId = _authManager.UserId;
await _repository.UpdateAsync(registration);
}
}
else
{
// 审核不通过,只更新状态
await _repository.UpdateAsync(x => new EventRegistration
{
PaymentStatus = input.PaymentStatus,
ReviewRemark = input.ReviewRemark,
ReviewTime = now
}, x => input.Ids.Contains(x.Id));
}
}
///
/// 导出报名
///
[DisplayName("导出报名")]
public async Task Export(RegistrationQueryInput input)
{
var registrations = await _repository.AsQueryable()
.WhereIF(input.EventId.HasValue, x => x.EventId == input.EventId)
.WhereIF(input.RegistrationMode.HasValue, x => x.RegistrationMode == input.RegistrationMode)
.WhereIF(!string.IsNullOrWhiteSpace(input.UnitName), x => x.TeamName.Contains(input.UnitName))
.WhereIF(!string.IsNullOrWhiteSpace(input.UserName), x => x.ContactName.Contains(input.UserName))
.WhereIF(!string.IsNullOrWhiteSpace(input.ContactPhone), x => x.ContactPhone.Contains(input.ContactPhone))
.WhereIF(input.PaymentStatus.HasValue, x => x.PaymentStatus == input.PaymentStatus)
.ToListAsync();
// 获取所有报名记录的 CreatedUserId
var createdUserIds = registrations.Select(x => x.CreatedUserId).Distinct().ToList();
// 获取这些创建用户的信息
var createdUsers = await _appUserRepository.AsQueryable()
.Where(u => createdUserIds.Contains(u.Id) && !u.DeleteMark)
.ToListAsync();
// 获取这些用户的 MerchantCertificationId
var merchantCertIds = createdUsers
.Where(u => u.MerchantCertificationId.HasValue)
.Select(u => u.MerchantCertificationId.Value)
.Distinct()
.ToList();
// 获取单位认证信息
Dictionary merchantNameDict = new Dictionary();
if (merchantCertIds.Any())
{
var merchantCerts = await _merchantCertificationRepository.AsQueryable()
.Where(mc => merchantCertIds.Contains(mc.Id) && !mc.DeleteMark)
.ToListAsync();
merchantNameDict = merchantCerts.ToDictionary(mc => mc.Id, mc => mc.MerchantName);
}
// 创建 CreatedUserId 到单位名称的映射
Dictionary userIdToUnitNameDict = new Dictionary();
foreach (var user in createdUsers)
{
if (user.MerchantCertificationId.HasValue &&
merchantNameDict.TryGetValue(user.MerchantCertificationId.Value, out var unitName))
{
userIdToUnitNameDict[user.Id] = unitName;
}
}
// 构建导出列表
var list = registrations.Select(x =>
{
// 根据报名记录的 CreatedUserId 获取单位名称
string? unitName = null;
if (userIdToUnitNameDict.TryGetValue(x.CreatedUserId, out var name))
{
unitName = name;
}
return new
{
x.RegistrationMode,
UnitName = unitName,
TeamName = x.TeamName,
x.ContactName,
x.ContactPhone,
x.TotalPersonCount,
x.PaymentMethod,
x.TotalAmount,
x.PaymentStatus,
x.RegistrationTime
};
}).ToList();
using var stream = new MemoryStream();
stream.SaveAs(list);
return stream.ToArray();
}
///
/// 获取素材下载链接
///
[DisplayName("获取素材下载链接")]
public async Task> MaterialUrls(List ids)
{
if (ids == null || !ids.Any()) return new List();
return await _repository.AsQueryable()
.Where(x => ids.Contains(x.Id) && !string.IsNullOrEmpty(x.MaterialUrl))
.Select(x => x.MaterialUrl)
.ToListAsync();
}
///
/// 单条支付审核
///
[UnitOfWork]
[DisplayName("单条支付审核")]
public async Task PaymentAudit(PaymentAuditInput input)
{
var registration = await _repository.GetByIdAsync(input.RegistrationId);
if (registration == null) throw Oops.Bah("报名记录不存在");
if (registration.PaymentStatus != 0)
{
throw Oops.Bah("该记录已完成支付审核,无需重复操作");
}
var now = DateTime.Now;
registration.PaymentStatus = input.PaymentStatus;
registration.ReviewRemark = input.ReviewRemark;
registration.ReviewTime = now;
registration.Reviewer = _authManager.Account;
registration.UpdatedTime = now;
registration.UpdatedUserId = _authManager.UserId;
if (input.PaymentStatus == 1)
{
registration.PaymentTime = now;
// 确保订单存在
await CreateOrderIfNotExistsAsync(registration, now);
}
await _repository.UpdateAsync(registration);
}
///
/// 批量支付审核
///
[UnitOfWork]
[DisplayName("批量支付审核")]
public async Task BatchPaymentAudit(BatchPaymentAuditInput input)
{
if (input.Ids == null || !input.Ids.Any()) return;
var registrations = await _repository.GetListAsync(x => input.Ids.Contains(x.Id));
if (!registrations.Any()) throw Oops.Bah("未找到有效的报名记录");
var invalidRegistrations = registrations
.Where(r => r.PaymentStatus != 0)
.ToList();
if (invalidRegistrations.Any())
{
throw Oops.Bah("存在非待审核状态的记录,无法进行支付审核");
}
var now = DateTime.Now;
// 如果审核通过(PaymentStatus = 1),先处理订单创建
if (input.PaymentStatus == 1)
{
foreach (var registration in registrations)
{
// 确保订单存在
await CreateOrderIfNotExistsAsync(registration, now);
}
}
// 更新报名记录的状态
await _repository.UpdateAsync(x => new EventRegistration
{
PaymentStatus = input.PaymentStatus,
ReviewRemark = input.ReviewRemark,
ReviewTime = now,
Reviewer = _authManager.Account,
UpdatedTime = now,
UpdatedUserId = _authManager.UserId,
PaymentTime = input.PaymentStatus == 1 ? now : x.PaymentTime
}, x => input.Ids.Contains(x.Id));
}
}