1115 lines
47 KiB
C#
1115 lines
47 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using YD_Event.Application.Event.Dtos;
|
||
using YD_Event.Application.Common;
|
||
using YD_Event.Core.Entities;
|
||
using YD_Event.Core.Enum;
|
||
using YD_Event.Core.Extensions;
|
||
using Easy.Core;
|
||
using SqlSugar;
|
||
using Mapster;
|
||
using YD_Event.Application.Auth;
|
||
using Furion.DynamicApiController;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.AspNetCore.Http;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using System.ComponentModel;
|
||
using Furion.FriendlyException;
|
||
using Furion.DatabaseAccessor;
|
||
using Furion.DependencyInjection;
|
||
using YD_Event.Core.Infrastructure.WeChat;
|
||
using YD_Event.Core.Domain;
|
||
using Microsoft.Extensions.Logging;
|
||
|
||
namespace YD_Event.Application.Event;
|
||
|
||
/// <summary>
|
||
/// 用户端参赛记录管理
|
||
/// </summary>
|
||
public class UserEventRecordService : IDynamicApiController, ITransient
|
||
{
|
||
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
|
||
private readonly ISqlSugarRepository<EventRegistrationPerson> _registrationPersonRepository;
|
||
private readonly ISqlSugarRepository<EventRegistrationItem> _registrationItemRepository;
|
||
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
|
||
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _userRepository;
|
||
private readonly ISqlSugarRepository<EventCertificate> _certificateRepository;
|
||
private readonly ISqlSugarRepository<EventResult> _eventResultRepository;
|
||
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
|
||
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
|
||
private readonly ISqlSugarRepository<SysUser> _sysUserRepository;
|
||
private readonly ISqlSugarRepository<EventProject> _eventProjectRepository;
|
||
private readonly ISqlSugarRepository<EventProjectGroup> _eventProjectGroupRepository;
|
||
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
|
||
private readonly ISqlSugarClient _db;
|
||
private readonly AuthManager _authManager;
|
||
private readonly IIdGenerator _idGenerator;
|
||
private readonly FileService _fileService;
|
||
private readonly ILogger<UserEventRecordService> _logger;
|
||
private readonly UnifiedRegistrationService _unifiedRegistrationService;
|
||
|
||
public UserEventRecordService(
|
||
ISqlSugarRepository<EventRegistration> registrationRepository,
|
||
ISqlSugarRepository<EventRegistrationPerson> registrationPersonRepository,
|
||
ISqlSugarRepository<EventRegistrationItem> registrationItemRepository,
|
||
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
|
||
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> userRepository,
|
||
ISqlSugarRepository<EventCertificate> certificateRepository,
|
||
ISqlSugarRepository<EventResult> eventResultRepository,
|
||
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
||
ISqlSugarRepository<EventInfo> eventRepository,
|
||
ISqlSugarRepository<SysUser> sysUserRepository,
|
||
ISqlSugarRepository<EventProject> eventProjectRepository,
|
||
ISqlSugarRepository<EventProjectGroup> eventProjectGroupRepository,
|
||
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
|
||
ISqlSugarClient db,
|
||
AuthManager authManager,
|
||
IIdGenerator idGenerator,
|
||
FileService fileService,
|
||
ILogger<UserEventRecordService> logger,
|
||
UnifiedRegistrationService unifiedRegistrationService)
|
||
{
|
||
_registrationRepository = registrationRepository;
|
||
_registrationPersonRepository = registrationPersonRepository;
|
||
_registrationItemRepository = registrationItemRepository;
|
||
_projectRepository = projectRepository;
|
||
_userRepository = userRepository;
|
||
_certificateRepository = certificateRepository;
|
||
_eventResultRepository = eventResultRepository;
|
||
_orderRepository = orderRepository;
|
||
_eventRepository = eventRepository;
|
||
_sysUserRepository = sysUserRepository;
|
||
_eventProjectRepository = eventProjectRepository;
|
||
_eventProjectGroupRepository = eventProjectGroupRepository;
|
||
_merchantCertificationRepository = merchantCertificationRepository;
|
||
_db = db;
|
||
_authManager = authManager;
|
||
_idGenerator = idGenerator;
|
||
_fileService = fileService;
|
||
_logger = logger;
|
||
_unifiedRegistrationService = unifiedRegistrationService;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 1. 列表查询
|
||
/// </summary>
|
||
[DisplayName("参赛记录列表查询")]
|
||
[HttpGet("list")]
|
||
public async Task<PageResult<RegistrationItemDTO>> GetRegistrationList([FromQuery] RegistrationListQueryInput input)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
|
||
var list = await _registrationRepository.AsQueryable()
|
||
.InnerJoin<EventInfo>((r, e) => r.EventId == e.Id)
|
||
.LeftJoin<EventFeeConfig>((r, e, fc) => e.Id == fc.EventId)
|
||
.Where((r, e, fc) => r.CreatedUserId == userId && !r.DeleteMark)
|
||
// 支付状态筛选:
|
||
// - 免费赛事:直接显示
|
||
// - 线下支付时:显示未支付(0)和已支付(1)状态的数据
|
||
// - 线上支付时:仅显示已支付(1)状态的数据
|
||
// - 排除预报名记录(5),仅在支付成功后才可见
|
||
.Where((r, e, fc) =>
|
||
r.PaymentStatus != 3 && // 排除已退款的报名
|
||
r.PaymentStatus != 4 && // 排除已取消的报名
|
||
r.PaymentStatus != 5 && // 排除预报名记录
|
||
((e.ChargeMode == ChargeMode.Free) ||
|
||
(r.PaymentMethod == 1 && (r.PaymentStatus == 0 || r.PaymentStatus == 1)) ||
|
||
(r.PaymentMethod == 2 && r.PaymentStatus == 1)))
|
||
.WhereIF(input.EventId.HasValue, (r, e, fc) => r.EventId == input.EventId)
|
||
.WhereIF(!string.IsNullOrWhiteSpace(input.Keyword), (r, e, fc) => r.TeamName.Contains(input.Keyword) || e.EventName.Contains(input.Keyword))
|
||
.OrderByDescending((r, e, fc) => r.CreatedTime)
|
||
.Select((r, e, fc) => new
|
||
{
|
||
r.Id, r.EventId, e.EventName, r.TeamName, r.PaymentStatus, r.MaterialUrl, r.PaymentProofUrl,
|
||
r.CreatedTime, r.UpdatedTime, r.TotalPersonCount, e.MaterialUploadEndTime,
|
||
e.CoverImage, e.EventType, e.EventStatus, e.RegistrationStartTime, e.RegistrationEndTime,
|
||
e.MatchStartTime, e.MatchEndTime,
|
||
fc.ChargeMode,
|
||
r.PaymentMethod
|
||
})
|
||
.ToPagedListAsync(input);
|
||
|
||
var resultItems = list.Rows.Select(x => {
|
||
return new RegistrationItemDTO
|
||
{
|
||
RegistrationId = x.Id,
|
||
EventName = x.EventName,
|
||
CoverImage = x.CoverImage,
|
||
EventType = (int?)x.EventType,
|
||
ChargeMode = (int?)x.ChargeMode,
|
||
EventStatus = (int?)x.EventStatus,
|
||
RegistrationStartTime = x.RegistrationStartTime,
|
||
RegistrationEndTime = x.RegistrationEndTime,
|
||
MaterialDeadline = x.MaterialUploadEndTime,
|
||
MatchStartTime = x.MatchStartTime,
|
||
MatchEndTime = x.MatchEndTime,
|
||
TeamName = x.TeamName,
|
||
CreateTime = x.CreatedTime,
|
||
UpdateTime = x.UpdatedTime,
|
||
PayStatus = x.PaymentStatus,
|
||
PaymentMethod = x.PaymentMethod,
|
||
CertStatus = 0, // 未颁发
|
||
HasMaterial = !string.IsNullOrEmpty(x.MaterialUrl),
|
||
HasVoucher = !string.IsNullOrEmpty(x.PaymentProofUrl),
|
||
MemberCount = x.TotalPersonCount,
|
||
MaxMember = 10,
|
||
ExpireTime = x.MaterialUploadEndTime
|
||
};
|
||
}).ToList();
|
||
|
||
return new PageResult<RegistrationItemDTO>
|
||
{
|
||
PageNo = list.PageNo,
|
||
PageSize = list.PageSize,
|
||
Total = list.Total,
|
||
Pages = list.Pages,
|
||
Rows = resultItems
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 2. 取消报名
|
||
/// </summary>
|
||
[DisplayName("取消报名")]
|
||
[HttpPost("cancel/{registrationId}")]
|
||
[UnitOfWork]
|
||
public async Task<ResponseDTO<bool>> CancelRegistration([FromRoute] long registrationId)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
var registration = await _registrationRepository.GetFirstAsync(x => x.Id == registrationId && x.CreatedUserId == userId);
|
||
if (registration == null) throw Oops.Bah("报名记录不存在");
|
||
|
||
if (registration.PaymentStatus == 4) throw Oops.Bah("订单已取消");
|
||
|
||
// 校验状态:线下支付(1)仅当状态为未支付(0)或已支付(1)时允许取消,线上支付只有未支付状态可直接取消,已支付需走退款流程
|
||
if (registration.PaymentMethod == 1 && registration.PaymentStatus != 0 && registration.PaymentStatus != 1)
|
||
{
|
||
throw Oops.Bah("线下支付订单仅当状态为未支付或已支付时允许取消,当前状态不允许取消");
|
||
}
|
||
if (registration.PaymentMethod != 1 && registration.PaymentStatus == 1)
|
||
{
|
||
throw Oops.Bah("已支付订单请联系客服退款");
|
||
}
|
||
|
||
_logger.LogInformation("开始取消报名:报名ID={RegistrationId}, 操作人={UserId}, 当前状态={PaymentStatus}, 支付方式={PaymentMethod}",
|
||
registrationId, userId, registration.PaymentStatus, registration.PaymentMethod);
|
||
|
||
// 更新报名状态
|
||
registration.PaymentStatus = 4; // 已取消
|
||
registration.UpdatedUserId = userId;
|
||
registration.UpdatedTime = DateTime.Now;
|
||
await _registrationRepository.UpdateAsync(registration);
|
||
|
||
// 同步更新订单状态
|
||
if (!string.IsNullOrEmpty(registration.OrderNo))
|
||
{
|
||
var order = await _orderRepository.GetFirstAsync(o => o.OrderNo == registration.OrderNo);
|
||
if (order != null)
|
||
{
|
||
order.OrderStatus = OrderStatus.Cancelled;
|
||
order.UpdatedTime = DateTime.Now;
|
||
order.UpdatedUserId = userId;
|
||
await _orderRepository.UpdateAsync(order);
|
||
_logger.LogInformation("订单状态已同步更新:订单号={OrderNo}, 状态={OrderStatus}",
|
||
registration.OrderNo, OrderStatus.Cancelled);
|
||
}
|
||
else
|
||
{
|
||
_logger.LogWarning("未找到对应的订单记录:报名ID={RegistrationId}, 订单号={OrderNo}",
|
||
registrationId, registration.OrderNo);
|
||
}
|
||
}
|
||
|
||
_logger.LogInformation("取消报名成功:报名ID={RegistrationId}", registrationId);
|
||
return new ResponseDTO<bool> { Code = 200, Success = true, Data = true, Message = "取消成功" };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 3. 上传素材
|
||
/// </summary>
|
||
[DisplayName("上传素材")]
|
||
[HttpPost("material/{registrationId}")]
|
||
[UnitOfWork]
|
||
public async Task<ResponseDTO<List<MaterialDTO>>> UploadMaterial([FromRoute] long registrationId, [FromForm] UploadMaterialInput input)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
var registration = await _registrationRepository.GetFirstAsync(x => x.Id == registrationId && x.CreatedUserId == userId);
|
||
if (registration == null) throw Oops.Bah("报名记录不存在");
|
||
|
||
// 仅 status = 待上传素材 或 已上传素材 允许再次上传
|
||
// 即 PayStatus == 1 (已支付)
|
||
if (registration.PaymentStatus != 1) throw Oops.Bah("未支付或状态不正确,无法上传素材");
|
||
|
||
// 总素材大小累计不超过 100 MB (简化校验,假设单次)
|
||
long totalSize = input.Files.Sum(f => f.Length);
|
||
if (totalSize > 100 * 1024 * 1024) throw Oops.Bah("素材总大小超过100MB");
|
||
|
||
var result = new List<MaterialDTO>();
|
||
foreach (var file in input.Files)
|
||
{
|
||
var uploadResult = await _fileService.Upload(file);
|
||
var item = uploadResult.FirstOrDefault();
|
||
if (item != null)
|
||
{
|
||
result.Add(new MaterialDTO
|
||
{
|
||
MaterialId = _idGenerator.NewLong().ToString(),
|
||
OssUrl = item.Url,
|
||
FileName = item.Name,
|
||
FileSize = file.Length,
|
||
UploadTime = DateTime.Now
|
||
});
|
||
}
|
||
}
|
||
|
||
if (result.Any())
|
||
{
|
||
registration.MaterialUrl = result.First().OssUrl; // 更新主表
|
||
await _registrationRepository.UpdateAsync(registration);
|
||
}
|
||
|
||
return new ResponseDTO<List<MaterialDTO>> { Code = 200, Success = true, Data = result, Message = "上传成功" };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 4. 上传付款凭证
|
||
/// </summary>
|
||
[DisplayName("上传付款凭证")]
|
||
[HttpPost("voucher/{registrationId}")]
|
||
[UnitOfWork]
|
||
public async Task<ResponseDTO<VoucherDTO>> UploadVoucher([FromRoute] long registrationId, [FromForm] UploadVoucherInput input)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
var registration = await _registrationRepository.GetFirstAsync(x => x.Id == registrationId && x.CreatedUserId == userId);
|
||
if (registration == null) throw Oops.Bah("报名记录不存在");
|
||
|
||
// 允许的文件类型
|
||
var allowedFileTypes = new[] { "image/jpeg", "image/jpg", "image/png", "application/pdf" };
|
||
// 允许的文件扩展名
|
||
var allowedFileExtensions = new[] { ".jpg", ".jpeg", ".png", ".pdf" };
|
||
// 最大文件大小 5MB
|
||
const long maxFileSize = 5 * 1024 * 1024;
|
||
|
||
// 验证文件类型
|
||
var fileName = input.File.FileName.ToLower();
|
||
var hasValidExtension = allowedFileExtensions.Any(ext => fileName.EndsWith(ext));
|
||
var hasValidType = allowedFileTypes.Contains(input.File.ContentType);
|
||
if (!hasValidExtension && !hasValidType)
|
||
{
|
||
throw Oops.Bah("仅支持jpg、jpeg、png、pdf格式的文件");
|
||
}
|
||
|
||
// 验证文件大小
|
||
if (input.File.Length > maxFileSize)
|
||
{
|
||
throw Oops.Bah("文件大小不能超过5MB");
|
||
}
|
||
|
||
// 验证支付金额
|
||
if (input.PayAmount <= 0)
|
||
{
|
||
throw Oops.Bah("支付金额必须大于0");
|
||
}
|
||
|
||
var uploadResult = await _fileService.Upload(input.File);
|
||
var fileInfo = uploadResult.First();
|
||
|
||
registration.PaymentProofUrl = fileInfo.Url;
|
||
registration.UpdatedTime = DateTime.Now;
|
||
await _registrationRepository.UpdateAsync(registration);
|
||
|
||
return new ResponseDTO<VoucherDTO>
|
||
{
|
||
Code = 200, Success = true, Message = "上传成功",
|
||
Data = new VoucherDTO
|
||
{
|
||
VoucherId = _idGenerator.NewLong().ToString(),
|
||
OssUrl = fileInfo.Url,
|
||
AuditStatus = "待审核"
|
||
}
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 5.1 查看队员列表
|
||
/// </summary>
|
||
[DisplayName("查看队员列表")]
|
||
[HttpGet("members/{registrationId}")]
|
||
public async Task<List<TeamMemberDTO>> GetTeamMembers([FromRoute] long registrationId)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
var registration = await _registrationRepository.GetFirstAsync(x => x.Id == registrationId && x.CreatedUserId == userId);
|
||
if (registration == null) throw Oops.Bah("报名记录不存在");
|
||
|
||
var persons = await _registrationPersonRepository.GetListAsync(x => x.RegistrationId == registrationId);
|
||
|
||
// 需要关联 AppUser 获取更多信息 (Email 等)
|
||
// 这里简化处理
|
||
return persons.Select(p => new TeamMemberDTO
|
||
{
|
||
UserId = 0, // 需关联 AppUser
|
||
RealName = p.Name,
|
||
Phone = p.Phone,
|
||
Email = "", // 暂无
|
||
Role = p.PersonType == PersonType.Leader ? "队长" : "队员", // 假设
|
||
InviteStatus = "已加入",
|
||
JoinTime = p.CreatedTime
|
||
}).ToList();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 5.2 更换队员
|
||
/// </summary>
|
||
[DisplayName("更换队员")]
|
||
[HttpPost("members/replace/{registrationId}/{userId}")]
|
||
[UnitOfWork]
|
||
public async Task<ResponseDTO<bool>> ReplaceMember([FromRoute] long registrationId, [FromRoute] long userId, [FromBody] ReplaceMemberInput input)
|
||
{
|
||
var currentUserId = _authManager.UserId;
|
||
// 校验队长权限: 仅创建者(队长)可操作
|
||
var registration = await _registrationRepository.GetFirstAsync(x => x.Id == registrationId && x.CreatedUserId == currentUserId);
|
||
if (registration == null) throw Oops.Bah("无权操作或记录不存在");
|
||
|
||
var person = await _registrationPersonRepository.GetFirstAsync(x => x.RegistrationId == registrationId && x.Id == userId);
|
||
if (person == null) throw Oops.Bah("队员不存在");
|
||
|
||
// 被替换人 role ≠ 队长
|
||
if (person.PersonType == PersonType.Leader) throw Oops.Bah("不能替换队长");
|
||
|
||
// 检查新队员
|
||
var newUser = await _userRepository.GetByIdAsync(input.NewUserId);
|
||
if (newUser == null) throw Oops.Bah("新队员不存在");
|
||
|
||
long oldUserId = person.Id;
|
||
|
||
// 更新信息
|
||
person.Name = newUser.Name;
|
||
person.IdCard = newUser.IdCardNumber;
|
||
person.Phone = newUser.Mobile;
|
||
person.UpdatedTime = DateTime.Now;
|
||
await _registrationPersonRepository.UpdateAsync(person);
|
||
|
||
return new ResponseDTO<bool> { Code = 200, Success = true, Data = true, Message = "更换成功" };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 6. 查看证书
|
||
/// </summary>
|
||
[DisplayName("查看证书")]
|
||
[HttpGet("certificate/{registrationId}")]
|
||
public async Task<ResponseDTO<CertificateDTO>> GetCertificate([FromRoute] long registrationId)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
|
||
// 2-已发放
|
||
var cert = await _certificateRepository.GetFirstAsync(x => x.RegistrationId == registrationId && x.CertificateStatus == 2);
|
||
if (cert == null) return new ResponseDTO<CertificateDTO> { Code = 404, Message = "证书未颁发", Success = false };
|
||
|
||
// 权限验证
|
||
bool hasPermission = await CheckCertificatePermissionAsync(userId, cert);
|
||
if (!hasPermission)
|
||
{
|
||
return new ResponseDTO<CertificateDTO> { Code = 403, Message = "无权查看该证书", Success = false };
|
||
}
|
||
|
||
return new ResponseDTO<CertificateDTO>
|
||
{
|
||
Code = 200, Success = true,
|
||
Data = new CertificateDTO
|
||
{
|
||
CertUrl = cert.CertificateUrl,
|
||
CertNumber = cert.CertificateNo,
|
||
IssueTime = cert.IssuedTime ?? DateTime.MinValue,
|
||
ExpireTime = null
|
||
}
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查证书查看权限
|
||
/// </summary>
|
||
private async Task<bool> CheckCertificatePermissionAsync(long currentUserId, EventCertificate cert)
|
||
{
|
||
// 如果是自己的证书,直接允许
|
||
if (cert.UserId == currentUserId)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
// 先尝试获取 appuser
|
||
var appUser = await _userRepository.GetByIdAsync(currentUserId);
|
||
// 如果 appuser 为空,尝试获取 sysuser
|
||
var sysUser = appUser == null ? await _sysUserRepository.GetByIdAsync(currentUserId) : null;
|
||
|
||
// 两个用户都不存在,返回 false
|
||
if (appUser == null && sysUser == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
long? merchantCertificationId = null;
|
||
|
||
// 优先使用 appuser 的认证信息
|
||
if (appUser != null && appUser.MerchantCertificationId.HasValue)
|
||
{
|
||
merchantCertificationId = appUser.MerchantCertificationId.Value;
|
||
}
|
||
// 如果 appuser 没有认证,尝试通过 sysuser 获取认证信息
|
||
else if (sysUser != null)
|
||
{
|
||
// 查找该 sysuser 关联的单位认证
|
||
var merchantCert = await _merchantCertificationRepository.GetFirstAsync(m =>
|
||
m.UserId == currentUserId &&
|
||
!m.DeleteMark &&
|
||
m.AuditStatus == AuditStatus.Pass &&
|
||
m.Status == AvailabilityStatus.Enable);
|
||
if (merchantCert != null)
|
||
{
|
||
merchantCertificationId = merchantCert.Id;
|
||
}
|
||
}
|
||
|
||
// 如果没有单位认证,返回 false
|
||
if (!merchantCertificationId.HasValue)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// 查询该单位认证下的团队成员
|
||
// 首先查询该单位认证创建的所有团队
|
||
var teamMemberUserIds = await _db.Queryable<YD_Event.Core.Entities.Team>()
|
||
.InnerJoin<TeamMember>((t, tm) => t.Id == tm.TeamId)
|
||
.Where((t, tm) =>
|
||
t.MerchantCertificationId == merchantCertificationId &&
|
||
!t.DeleteMark &&
|
||
!tm.DeleteMark)
|
||
.Select((t, tm) => tm.UserId)
|
||
.ToListAsync();
|
||
|
||
// 检查证书用户是否在团队成员列表中
|
||
if (teamMemberUserIds.Contains(cert.UserId))
|
||
{
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 6.1 获取所有证书列表
|
||
/// </summary>
|
||
[DisplayName("获取所有证书列表")]
|
||
[HttpGet("certificates/{registrationId}")]
|
||
public async Task<ResponseDTO<List<CertificateListItemDTO>>> GetCertificates([FromRoute] long registrationId)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
|
||
// 2-已发放
|
||
var certs = await _certificateRepository.GetListAsync(x => x.RegistrationId == registrationId && x.CertificateStatus == 2);
|
||
if (certs == null || certs.Count == 0)
|
||
return new ResponseDTO<List<CertificateListItemDTO>> { Code = 200, Success = true, Data = new List<CertificateListItemDTO>() };
|
||
|
||
// 权限验证 - 筛选有权限查看的证书
|
||
var allowedCerts = new List<EventCertificate>();
|
||
foreach (var cert in certs)
|
||
{
|
||
bool hasPermission = await CheckCertificatePermissionAsync(userId, cert);
|
||
if (hasPermission)
|
||
{
|
||
allowedCerts.Add(cert);
|
||
}
|
||
}
|
||
|
||
// 获取报名项目信息
|
||
var registrationItems = await _registrationItemRepository.GetListAsync(x => x.RegistrationId == registrationId);
|
||
|
||
// 获取所有相关项目信息
|
||
var projectIds = registrationItems.Select(x => x.ProjectId).Distinct().ToList();
|
||
var certProjectIds = allowedCerts.Where(x => x.ProjectId > 0).Select(x => x.ProjectId).Distinct().ToList();
|
||
var allProjectIds = projectIds.Concat(certProjectIds).Distinct().ToList();
|
||
var projects = await _projectRepository.GetListAsync(x => allProjectIds.Contains(x.Id));
|
||
var projectDict = projects.ToDictionary(x => x.Id, x => x);
|
||
|
||
// 获取报名人员信息
|
||
var personIds = allowedCerts.Where(x => x.ParticipationRecordId > 0).Select(x => x.ParticipationRecordId).Distinct().ToList();
|
||
var persons = await _registrationPersonRepository.GetListAsync(x => personIds.Contains(x.Id));
|
||
var personDict = persons.ToDictionary(x => x.Id, x => x);
|
||
|
||
// 获取成绩信息
|
||
var resultIds = allowedCerts.Where(x => x.ResultId > 0).Select(x => x.ResultId).Distinct().ToList();
|
||
var results = await _eventResultRepository.GetListAsync(x => resultIds.Contains(x.Id));
|
||
var resultDict = results.ToDictionary(x => x.Id, x => x);
|
||
|
||
var result = allowedCerts.Select(cert =>
|
||
{
|
||
// 查找对应的报名项目 - 只通过项目ID查找
|
||
var registrationItem = registrationItems.FirstOrDefault(x => cert.ProjectId > 0 && x.ProjectId == cert.ProjectId);
|
||
|
||
var registrationItemId = registrationItem?.Id ?? 0;
|
||
|
||
// 获取项目名称
|
||
string projectName = "未知项目";
|
||
if (registrationItem != null && projectDict.TryGetValue(registrationItem.ProjectId, out var p))
|
||
{
|
||
projectName = p.Name;
|
||
}
|
||
|
||
var groupName = cert.GroupName ?? "未知组别";
|
||
|
||
// 获取人员角色
|
||
var person = personDict.TryGetValue(cert.ParticipationRecordId, out var rp) ? rp : null;
|
||
var role = "运动员";
|
||
if (person != null)
|
||
{
|
||
if (person.PersonType == PersonType.Coach)
|
||
{
|
||
role = "教练";
|
||
}
|
||
else if (person.PersonType == PersonType.Leader)
|
||
{
|
||
role = "领队";
|
||
}
|
||
}
|
||
|
||
// 获取成绩和名次
|
||
var resultEntity = resultDict.TryGetValue(cert.ResultId, out var r) ? r : null;
|
||
|
||
return new CertificateListItemDTO
|
||
{
|
||
CertificateId = cert.Id,
|
||
CertificateUrl = cert.CertificateUrl?.Trim() ?? string.Empty,
|
||
CertificateNo = cert.CertificateNo,
|
||
ParticipantName = cert.ParticipantName,
|
||
ProjectName = projectName,
|
||
GroupName = groupName,
|
||
RegistrationItemId = registrationItemId,
|
||
Role = role,
|
||
CertificateType = cert.CertificateType,
|
||
IssuedTime = cert.IssuedTime ?? DateTime.MinValue,
|
||
Score = resultEntity?.Score,
|
||
Rank = resultEntity?.Rank
|
||
};
|
||
}).ToList();
|
||
|
||
return new ResponseDTO<List<CertificateListItemDTO>>
|
||
{
|
||
Code = 200,
|
||
Success = true,
|
||
Data = result
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 7. 取消报名预览
|
||
/// </summary>
|
||
[DisplayName("取消报名预览")]
|
||
[HttpGet("cancel/preview/{registrationId}")]
|
||
public async Task<CancelPreviewOutput> CancelPreview([FromRoute] long registrationId)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
_logger.LogInformation("调用统一取消报名预览:报名ID={RegistrationId}, 操作人={UserId}", registrationId, userId);
|
||
return await _unifiedRegistrationService.GetCancelPreviewAsync(registrationId, userId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 8. 执行取消报名
|
||
/// </summary>
|
||
[DisplayName("执行取消报名")]
|
||
[HttpPost("cancel/execute")]
|
||
[UnitOfWork]
|
||
public async Task<CancelExecuteOutput> CancelExecute([FromBody] CancelExecuteInput input)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
_logger.LogInformation("调用统一执行取消报名:报名ID={RegistrationId}, 操作人={UserId}", input.RegistrationId, userId);
|
||
return await _unifiedRegistrationService.ExecuteCancelRegistrationAsync(input, userId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 9. 计算退款金额
|
||
/// </summary>
|
||
[DisplayName("计算退款金额")]
|
||
[HttpPost("cancel/calculate-refund")]
|
||
public async Task<CalculateRefundAmountOutput> CalculateRefund([FromBody] CalculateRefundAmountInput input)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
_logger.LogInformation("调用计算退款金额:报名ID={RegistrationId}, 操作人={UserId}", input.RegistrationId, userId);
|
||
return await _unifiedRegistrationService.CalculateRefundAmountAsync(input, userId);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 9. 获取参赛记录详情(包含项目和队员)
|
||
/// </summary>
|
||
[DisplayName("获取参赛记录详情(包含项目和队员)")]
|
||
[HttpGet("detail-with-items/{registrationId}")]
|
||
public async Task<RegistrationDetailWithItemsDto> GetRegistrationDetailWithItems([FromRoute] long registrationId)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
var registration = await _registrationRepository.GetFirstAsync(x => x.Id == registrationId && x.CreatedUserId == userId);
|
||
if (registration == null) throw Oops.Bah("报名记录不存在");
|
||
|
||
// 预报名记录不可见
|
||
if (registration.PaymentStatus == 5) throw Oops.Bah("报名记录不存在");
|
||
|
||
// 获取赛事信息
|
||
var eventInfo = await _eventRepository.GetByIdAsync(registration.EventId);
|
||
|
||
// 获取报名项目
|
||
var items = await _registrationItemRepository.GetListAsync(x => x.RegistrationId == registrationId);
|
||
|
||
// 获取所有队员
|
||
var persons = await _registrationPersonRepository.GetListAsync(x => x.RegistrationId == registrationId);
|
||
|
||
// 获取组别信息(年龄和性别限制)
|
||
var groupIds = items.Where(x => x.EventProjectGroupId.HasValue).Select(x => x.EventProjectGroupId.Value).Distinct().ToList();
|
||
var groups = new Dictionary<long, EventProjectGroup>();
|
||
if (groupIds.Any())
|
||
{
|
||
var groupList = await _db.Queryable<EventProjectGroup>()
|
||
.Where(x => groupIds.Contains(x.Id))
|
||
.ToListAsync();
|
||
groups = groupList.ToDictionary(x => x.Id);
|
||
}
|
||
|
||
// 获取项目信息(从Project表查询项目名称和完成人数)
|
||
var projectIds = items.Select(x => x.ProjectId).Distinct().ToList();
|
||
var projects = new Dictionary<long, YD_Event.Core.Entities.Project>();
|
||
if (projectIds.Any())
|
||
{
|
||
var projectList = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
|
||
projects = projectList.ToDictionary(x => x.Id);
|
||
}
|
||
|
||
var result = new RegistrationDetailWithItemsDto
|
||
{
|
||
RegistrationId = registration.Id,
|
||
EventId = registration.EventId,
|
||
EventName = eventInfo?.EventName ?? "",
|
||
TeamName = registration.TeamName ?? "",
|
||
RegistrationEndTime = eventInfo.RegistrationEndTime,
|
||
EventStatus = (int)eventInfo.EventStatus
|
||
};
|
||
|
||
foreach (var item in items)
|
||
{
|
||
var itemMembers = persons.Where(p => p.RegistrationItemId == item.Id).ToList();
|
||
|
||
// 获取组别信息
|
||
EventProjectGroup? group = null;
|
||
if (item.EventProjectGroupId.HasValue && groups.TryGetValue(item.EventProjectGroupId.Value, out var foundGroup))
|
||
{
|
||
group = foundGroup;
|
||
}
|
||
|
||
// 获取项目信息
|
||
YD_Event.Core.Entities.Project? project = null;
|
||
if (projects.TryGetValue(item.ProjectId, out var foundProject))
|
||
{
|
||
project = foundProject;
|
||
}
|
||
|
||
result.Items.Add(new RegistrationItemWithMembersDto
|
||
{
|
||
RegistrationItemId = item.Id,
|
||
ProjectId = item.ProjectId,
|
||
EventProjectGroupId = item.EventProjectGroupId ?? 0,
|
||
// 优先从Project表获取项目名称,如果没有则使用报名时保存的名称
|
||
ProjectName = project?.Name ?? item.ProjectName ?? "",
|
||
GroupName = item.GroupName ?? "",
|
||
ProjectTypeName = item.ProjectTypeName ?? "",
|
||
PersonCount = item.PersonCount,
|
||
// 从Project表获取完成人数
|
||
ProjectCompletionCount = project?.CompletionCount ?? 0,
|
||
BirthDateStart = group?.BirthDateStart,
|
||
BirthDateEnd = group?.BirthDateEnd,
|
||
GenderRequirement = group?.Gender,
|
||
Members = itemMembers.Select(p => new RegistrationPersonDto
|
||
{
|
||
Id = p.Id,
|
||
UserId = p.UserId, // 这里是关键!设置正确的用户ID
|
||
Name = p.Name,
|
||
IdCardNumber = p.IdCard,
|
||
Gender = p.Gender,
|
||
Mobile = p.Phone,
|
||
BirthDate = p.BirthDate
|
||
}).ToList()
|
||
});
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 10. 更新参赛队员
|
||
/// </summary>
|
||
[DisplayName("更新参赛队员")]
|
||
[HttpPost("update-members")]
|
||
[UnitOfWork]
|
||
public async Task<ResponseDTO<bool>> UpdateRegistrationMembers([FromBody] UpdateRegistrationMembersInput input)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
var registration = await _registrationRepository.GetFirstAsync(x => x.Id == input.RegistrationId && x.CreatedUserId == userId);
|
||
if (registration == null) throw Oops.Bah("报名记录不存在或无权操作");
|
||
|
||
_logger.LogInformation("开始更新参赛队员:报名ID={RegistrationId}, 操作人={UserId}", input.RegistrationId, userId);
|
||
|
||
// 获取赛事信息
|
||
var eventInfo = await _eventRepository.GetFirstAsync(x => x.Id == registration.EventId);
|
||
if (eventInfo == null) throw Oops.Bah("赛事信息不存在");
|
||
|
||
// 检查报名时间是否已结束
|
||
var now = DateTime.Now;
|
||
if (now > eventInfo.RegistrationEndTime)
|
||
{
|
||
throw Oops.Bah("赛事报名时间已结束,无法更换参赛队员");
|
||
}
|
||
|
||
// 检查赛事状态,只有报名中状态才允许更换队员
|
||
if (eventInfo.EventStatus != EventStatus.Enrolling)
|
||
{
|
||
throw Oops.Bah("当前赛事状态不允许更换参赛队员");
|
||
}
|
||
|
||
// 获取报名项目信息
|
||
var registrationItems = await _registrationItemRepository.GetListAsync(x => x.RegistrationId == input.RegistrationId);
|
||
var eventProjectIds = registrationItems.Select(x => x.ProjectId).Distinct().ToList();
|
||
var eventProjects = await _eventProjectRepository.GetListAsync(x => eventProjectIds.Contains(x.Id));
|
||
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => eventProjectIds.Contains(x.EventProjectId));
|
||
var projectIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
|
||
var projects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
|
||
|
||
// 获取所有需要查询的用户ID
|
||
var userIds = input.Items.SelectMany(x => x.Members).Where(x => x.UserId.HasValue).Select(x => x.UserId.Value).Distinct().ToList();
|
||
var users = new Dictionary<long, YD_Event.Core.Entities.AppUser>();
|
||
if (userIds.Any())
|
||
{
|
||
var userList = await _userRepository.GetListAsync(x => userIds.Contains(x.Id));
|
||
users = userList.ToDictionary(x => x.Id);
|
||
}
|
||
|
||
foreach (var item in input.Items)
|
||
{
|
||
// 验证项目是否属于该报名
|
||
var regItem = await _registrationItemRepository.GetFirstAsync(x => x.Id == item.RegistrationItemId && x.RegistrationId == input.RegistrationId);
|
||
if (regItem == null) throw Oops.Bah($"项目记录不存在:{item.RegistrationItemId}");
|
||
|
||
// 软删旧的队员
|
||
await _registrationPersonRepository.SoftDeleteAsync(x => x.RegistrationItemId == item.RegistrationItemId);
|
||
|
||
// 添加新的队员
|
||
var newPersons = new List<EventRegistrationPerson>();
|
||
foreach (var member in item.Members)
|
||
{
|
||
// 如果有UserId,从用户表获取完整信息
|
||
YD_Event.Core.Entities.AppUser? user = null;
|
||
if (member.UserId.HasValue && users.TryGetValue(member.UserId.Value, out var foundUser))
|
||
{
|
||
user = foundUser;
|
||
}
|
||
|
||
var name = member.Name ?? user?.Name;
|
||
var gender = member.Gender ?? user?.Gender ?? Gender.Male;
|
||
var idCardNumber = member.IdCardNumber ?? user?.IdCardNumber;
|
||
var birthDate = member.BirthDate ?? user?.BirthDate ?? DateTime.Now.AddYears(-20);
|
||
var mobile = member.Mobile ?? user?.Mobile;
|
||
|
||
// 验证必填字段
|
||
if (string.IsNullOrWhiteSpace(name)) throw Oops.Bah("队员姓名不能为空");
|
||
if (string.IsNullOrWhiteSpace(idCardNumber)) throw Oops.Bah("队员身份证号不能为空");
|
||
if (string.IsNullOrWhiteSpace(mobile)) throw Oops.Bah("队员手机号不能为空");
|
||
|
||
newPersons.Add(new EventRegistrationPerson
|
||
{
|
||
Id = _idGenerator.NewLong(),
|
||
RegistrationId = input.RegistrationId,
|
||
RegistrationItemId = item.RegistrationItemId,
|
||
ProjectId = regItem.ProjectId,
|
||
EventProjectGroupId = regItem.EventProjectGroupId,
|
||
GroupName = regItem.GroupName,
|
||
ProjectName = regItem.ProjectName,
|
||
Name = name,
|
||
Gender = gender,
|
||
IdCard = idCardNumber,
|
||
BirthDate = birthDate,
|
||
Phone = mobile,
|
||
Age = CalculateAge(birthDate),
|
||
PersonType = PersonType.Athlete,
|
||
UserId = member.UserId ?? 0,
|
||
CreatedUserId = userId,
|
||
CreatedTime = DateTime.Now
|
||
});
|
||
}
|
||
|
||
await _registrationPersonRepository.InsertRangeAsync(newPersons);
|
||
|
||
// 更新项目的人数
|
||
regItem.PersonCount = item.Members.Count;
|
||
regItem.UpdatedTime = DateTime.Now;
|
||
regItem.UpdatedUserId = userId;
|
||
await _registrationItemRepository.UpdateAsync(regItem);
|
||
}
|
||
|
||
// 更新报名总人数
|
||
var allPersonsCount = await _registrationPersonRepository.CountAsync(x => x.RegistrationId == input.RegistrationId);
|
||
registration.TotalPersonCount = allPersonsCount;
|
||
registration.UpdatedTime = DateTime.Now;
|
||
registration.UpdatedUserId = userId;
|
||
await _registrationRepository.UpdateAsync(registration);
|
||
|
||
_logger.LogInformation("更新参赛队员成功:报名ID={RegistrationId}", input.RegistrationId);
|
||
|
||
return new ResponseDTO<bool> { Code = 200, Success = true, Data = true, Message = "更新成功" };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算年龄
|
||
/// </summary>
|
||
private int CalculateAge(DateTime birthDate)
|
||
{
|
||
var today = DateTime.Today;
|
||
var age = today.Year - birthDate.Year;
|
||
if (birthDate.Date > today.AddYears(-age)) age--;
|
||
return age;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证队员是否符合报名条件
|
||
/// </summary>
|
||
[DisplayName("验证队员")]
|
||
[HttpPost("member/validate")]
|
||
public async Task<MemberValidateOutput> ValidateMember([FromBody] MemberValidateInput input)
|
||
{
|
||
var result = new MemberValidateOutput
|
||
{
|
||
IsValid = true,
|
||
AgeValid = true,
|
||
GenderValid = true,
|
||
RegistrationLimitValid = true,
|
||
Message = "验证通过"
|
||
};
|
||
|
||
// 添加调试:查看接收的ID
|
||
var memberId = input.MemberId;
|
||
var member = await _userRepository.GetByIdAsync(memberId);
|
||
|
||
// 添加调试:如果找不到,尝试不删除标记查询
|
||
if (member == null)
|
||
{
|
||
var memberWithoutDelete = await _db.Queryable<YD_Event.Core.Entities.AppUser>()
|
||
.FirstAsync(m => m.Id == memberId);
|
||
|
||
if (memberWithoutDelete != null)
|
||
{
|
||
// 队员存在但被标记删除
|
||
result.IsValid = false;
|
||
result.Message = "队员不存在(已删除)";
|
||
return result;
|
||
}
|
||
|
||
result.IsValid = false;
|
||
result.Message = "队员不存在";
|
||
return result;
|
||
}
|
||
|
||
if (!member.BirthDate.HasValue)
|
||
{
|
||
result.IsValid = false;
|
||
result.AgeValid = false;
|
||
result.Message = "队员缺少出生日期信息";
|
||
return result;
|
||
}
|
||
|
||
var eventProjectGroup = await _eventProjectGroupRepository.GetByIdAsync(input.GroupId);
|
||
if (eventProjectGroup != null)
|
||
{
|
||
// 获取项目类型信息
|
||
var eventProject = await _eventProjectRepository.GetByIdAsync(eventProjectGroup.EventProjectId);
|
||
var baseProject = eventProject != null ? await _projectRepository.GetByIdAsync(eventProject.ProjectId) : null;
|
||
var isTeamProject = baseProject?.IsTeamProject() ?? false;
|
||
|
||
// 创建单个队员的验证输入
|
||
var persons = new List<UnifiedPersonInput>
|
||
{
|
||
new UnifiedPersonInput
|
||
{
|
||
UserId = member.Id,
|
||
Name = member.Name ?? "",
|
||
IdCardNumber = member.IdCardNumber ?? "",
|
||
Gender = member.Gender,
|
||
Mobile = member.Mobile,
|
||
BirthDate = member.BirthDate.Value
|
||
}
|
||
};
|
||
|
||
// 使用统一的验证方法进行验证
|
||
var validationResult = _unifiedRegistrationService.ValidatePersonQualifications(persons, eventProjectGroup, isTeamProject);
|
||
|
||
if (!validationResult.IsValid)
|
||
{
|
||
result.IsValid = false;
|
||
result.Message = validationResult.ErrorMessage ?? "验证失败";
|
||
|
||
// 根据验证详情设置具体的验证状态
|
||
var ageDetail = validationResult.Details?.FirstOrDefault(d => d.ItemName == "年龄验证" || d.ItemName == "年龄跨组验证");
|
||
var genderDetail = validationResult.Details?.FirstOrDefault(d => d.ItemName == "性别验证");
|
||
|
||
if (ageDetail != null && !ageDetail.IsPassed)
|
||
{
|
||
result.AgeValid = false;
|
||
}
|
||
|
||
if (genderDetail != null && !genderDetail.IsPassed)
|
||
{
|
||
result.GenderValid = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查报名限制
|
||
/// </summary>
|
||
[DisplayName("检查报名限制")]
|
||
[HttpPost("registration-limit/check")]
|
||
public async Task<RegistrationLimitCheckOutput> CheckRegistrationLimit([FromBody] RegistrationLimitCheckInput input)
|
||
{
|
||
var userId = _authManager.UserId;
|
||
|
||
// 获取当前用户信息
|
||
var sysUser = await _sysUserRepository.GetByIdAsync(userId);
|
||
if (sysUser == null) throw Oops.Bah("用户不存在");
|
||
string userName = sysUser.Name;
|
||
string userMobile = sysUser.Mobile ?? "";
|
||
|
||
// 获取团队信息 - 优先使用新结构的 TeamId
|
||
string teamName = "临时团队";
|
||
if (input.TeamId.HasValue)
|
||
{
|
||
var team = await _db.Queryable<YD_Event.Core.Entities.Team>().FirstAsync(t => t.Id == input.TeamId.Value && !t.DeleteMark);
|
||
if (team != null)
|
||
{
|
||
teamName = team.Name;
|
||
}
|
||
}
|
||
|
||
// 获取所有成员信息
|
||
var allMemberIds = new List<long>();
|
||
if (input.GroupMembers != null)
|
||
{
|
||
foreach (var gm in input.GroupMembers)
|
||
{
|
||
// 检查是否有多团队数据
|
||
if (gm.Teams != null && gm.Teams.Count > 0)
|
||
{
|
||
foreach (var team in gm.Teams)
|
||
{
|
||
allMemberIds.AddRange(team.MemberIds ?? new List<long>());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 向后兼容单团队模式
|
||
allMemberIds.AddRange(gm.MemberIds ?? new List<long>());
|
||
}
|
||
}
|
||
}
|
||
else if (input.MemberIds != null)
|
||
{
|
||
// 向后兼容旧结构
|
||
allMemberIds.AddRange(input.MemberIds);
|
||
}
|
||
|
||
allMemberIds = allMemberIds.Distinct().ToList();
|
||
|
||
// 获取成员信息
|
||
var members = await _userRepository.GetListAsync(x => allMemberIds.Contains(x.Id));
|
||
|
||
// 获取组别信息
|
||
var groupIds = input.GroupMembers?.Select(g => g.GroupId).ToList() ?? input.GroupIds ?? new List<long>();
|
||
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => groupIds.Contains(x.Id));
|
||
|
||
// 获取项目信息
|
||
var eventProjectIds = eventProjectGroups.Select(epg => epg.EventProjectId).Distinct().ToList();
|
||
var eventProjects = await _eventProjectRepository.GetListAsync(x => eventProjectIds.Contains(x.Id));
|
||
|
||
// 构建统一报名输入
|
||
var unifiedInput = new UnifiedRegistrationInput
|
||
{
|
||
EventId = input.EventId,
|
||
RegistrationMode = 2, // PC端团队报名
|
||
TeamName = teamName,
|
||
ContactName = userName,
|
||
ContactPhone = userMobile,
|
||
CreatedUserId = userId,
|
||
IsDraft = false,
|
||
Items = new List<UnifiedRegistrationItemInput>()
|
||
};
|
||
|
||
// 为每个组别添加项目项
|
||
foreach (var groupMember in input.GroupMembers ?? new List<GroupMemberAssignment>())
|
||
{
|
||
var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == groupMember.GroupId);
|
||
if (eventProjectGroup == null) continue;
|
||
|
||
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);
|
||
if (eventProject == null) continue;
|
||
|
||
var item = new UnifiedRegistrationItemInput
|
||
{
|
||
ProjectId = eventProject.Id,
|
||
GroupId = groupMember.GroupId,
|
||
GroupName = eventProjectGroup.GroupName,
|
||
Teams = new List<UnifiedTeamInput>(),
|
||
Persons = new List<UnifiedPersonInput>()
|
||
};
|
||
|
||
// 检查是多团队模式还是单团队模式
|
||
var hasMultipleTeams = groupMember.Teams != null && groupMember.Teams.Count > 0;
|
||
|
||
if (hasMultipleTeams)
|
||
{
|
||
// ========== 多团队模式 ==========
|
||
foreach (var team in groupMember.Teams)
|
||
{
|
||
var teamMembers = members.Where(m => team.MemberIds != null && team.MemberIds.Contains(m.Id)).ToList();
|
||
item.Teams.Add(new UnifiedTeamInput
|
||
{
|
||
TeamIndex = team.TeamIndex,
|
||
Persons = teamMembers.Select(m => new UnifiedPersonInput
|
||
{
|
||
UserId = m.Id,
|
||
Name = m.Name ?? "",
|
||
IdCardNumber = m.IdCardNumber ?? "",
|
||
Gender = m.Gender,
|
||
Mobile = m.Mobile,
|
||
BirthDate = m.BirthDate ?? DateTime.MinValue
|
||
}).ToList()
|
||
});
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// ========== 单团队模式(向后兼容) ==========
|
||
var groupMembersList = members.Where(m => groupMember.MemberIds != null && groupMember.MemberIds.Contains(m.Id)).ToList();
|
||
item.Persons = groupMembersList.Select(m => new UnifiedPersonInput
|
||
{
|
||
UserId = m.Id,
|
||
Name = m.Name ?? "",
|
||
IdCardNumber = m.IdCardNumber ?? "",
|
||
Gender = m.Gender,
|
||
Mobile = m.Mobile,
|
||
BirthDate = m.BirthDate ?? DateTime.MinValue
|
||
}).ToList();
|
||
}
|
||
|
||
unifiedInput.Items.Add(item);
|
||
}
|
||
|
||
// 调用统一的限制检查方法
|
||
return await _unifiedRegistrationService.CheckRegistrationLimitAsync(unifiedInput, input.ExcludeRegistrationId);
|
||
}
|
||
}
|