YD_Event.Admin/YD_Event.Application/Event/UserEventRecordService.cs

1148 lines
48 KiB
C#
Raw Normal View History

2026-03-20 03:15:31 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
2026-03-14 05:19:40 +08:00
using YD_Event.Application.Event.Dtos;
2026-04-12 13:14:20 +08:00
using YD_Event.Application.Common;
2026-03-14 05:19:40 +08:00
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
2026-04-20 14:01:30 +08:00
using YD_Event.Core.Extensions;
2026-04-10 10:54:47 +08:00
using Easy.Core;
2026-03-14 05:19:40 +08:00
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;
2026-03-20 03:15:31 +08:00
using Furion.FriendlyException;
using Furion.DatabaseAccessor;
using Furion.DependencyInjection;
using YD_Event.Core.Infrastructure.WeChat;
2026-04-10 22:39:32 +08:00
using YD_Event.Core.Domain;
using Microsoft.Extensions.Logging;
2026-03-14 05:19:40 +08:00
namespace YD_Event.Application.Event;
/// <summary>
2026-03-20 03:15:31 +08:00
/// 用户端参赛记录管理
2026-03-14 05:19:40 +08:00
/// </summary>
2026-03-20 03:15:31 +08:00
public class UserEventRecordService : IDynamicApiController, ITransient
2026-03-14 05:19:40 +08:00
{
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
private readonly ISqlSugarRepository<EventRegistrationPerson> _registrationPersonRepository;
private readonly ISqlSugarRepository<EventRegistrationItem> _registrationItemRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
2026-03-14 05:19:40 +08:00
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _userRepository;
private readonly ISqlSugarRepository<EventCertificate> _certificateRepository;
2026-05-08 18:02:40 +08:00
private readonly ISqlSugarRepository<EventResult> _eventResultRepository;
2026-04-10 22:39:32 +08:00
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
2026-04-15 09:58:05 +08:00
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
2026-04-20 13:01:39 +08:00
private readonly ISqlSugarRepository<SysUser> _sysUserRepository;
private readonly ISqlSugarRepository<EventProject> _eventProjectRepository;
private readonly ISqlSugarRepository<EventProjectGroup> _eventProjectGroupRepository;
2026-05-07 14:43:03 +08:00
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
private readonly ISqlSugarClient _db;
2026-03-14 05:19:40 +08:00
private readonly AuthManager _authManager;
private readonly IIdGenerator _idGenerator;
private readonly FileService _fileService;
2026-04-10 22:39:32 +08:00
private readonly ILogger<UserEventRecordService> _logger;
2026-04-15 09:58:05 +08:00
private readonly UnifiedRegistrationService _unifiedRegistrationService;
2026-03-14 05:19:40 +08:00
2026-03-20 03:15:31 +08:00
public UserEventRecordService(
2026-03-14 05:19:40 +08:00
ISqlSugarRepository<EventRegistration> registrationRepository,
ISqlSugarRepository<EventRegistrationPerson> registrationPersonRepository,
ISqlSugarRepository<EventRegistrationItem> registrationItemRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
2026-03-14 05:19:40 +08:00
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> userRepository,
ISqlSugarRepository<EventCertificate> certificateRepository,
2026-05-08 18:02:40 +08:00
ISqlSugarRepository<EventResult> eventResultRepository,
2026-04-10 22:39:32 +08:00
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
2026-04-15 09:58:05 +08:00
ISqlSugarRepository<EventInfo> eventRepository,
2026-04-20 13:01:39 +08:00
ISqlSugarRepository<SysUser> sysUserRepository,
ISqlSugarRepository<EventProject> eventProjectRepository,
ISqlSugarRepository<EventProjectGroup> eventProjectGroupRepository,
2026-05-07 14:43:03 +08:00
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
ISqlSugarClient db,
2026-03-14 05:19:40 +08:00
AuthManager authManager,
IIdGenerator idGenerator,
FileService fileService,
2026-04-15 09:58:05 +08:00
ILogger<UserEventRecordService> logger,
UnifiedRegistrationService unifiedRegistrationService)
2026-03-14 05:19:40 +08:00
{
_registrationRepository = registrationRepository;
_registrationPersonRepository = registrationPersonRepository;
_registrationItemRepository = registrationItemRepository;
_projectRepository = projectRepository;
2026-03-14 05:19:40 +08:00
_userRepository = userRepository;
_certificateRepository = certificateRepository;
2026-05-08 18:02:40 +08:00
_eventResultRepository = eventResultRepository;
2026-04-15 09:58:05 +08:00
_orderRepository = orderRepository;
_eventRepository = eventRepository;
2026-04-20 13:01:39 +08:00
_sysUserRepository = sysUserRepository;
_eventProjectRepository = eventProjectRepository;
_eventProjectGroupRepository = eventProjectGroupRepository;
2026-05-07 14:43:03 +08:00
_merchantCertificationRepository = merchantCertificationRepository;
_db = db;
2026-03-14 05:19:40 +08:00
_authManager = authManager;
_idGenerator = idGenerator;
_fileService = fileService;
2026-04-10 22:39:32 +08:00
_logger = logger;
2026-04-15 09:58:05 +08:00
_unifiedRegistrationService = unifiedRegistrationService;
2026-03-14 05:19:40 +08:00
}
/// <summary>
/// 1. 列表查询
/// </summary>
2026-03-17 15:32:01 +08:00
[DisplayName("参赛记录列表查询")]
2026-03-20 03:15:31 +08:00
[HttpGet("list")]
2026-03-14 05:19:40 +08:00
public async Task<PageResult<RegistrationItemDTO>> GetRegistrationList([FromQuery] RegistrationListQueryInput input)
{
var userId = _authManager.UserId;
2026-04-09 23:12:51 +08:00
2026-03-14 05:19:40 +08:00
var list = await _registrationRepository.AsQueryable()
.InnerJoin<EventInfo>((r, e) => r.EventId == e.Id)
2026-04-06 09:21:47 +08:00
.LeftJoin<EventFeeConfig>((r, e, fc) => e.Id == fc.EventId)
.Where((r, e, fc) => r.CreatedUserId == userId && !r.DeleteMark)
2026-04-15 09:58:05 +08:00
// 支付状态筛选:
2026-05-08 14:50:01 +08:00
// - 免费赛事:直接显示
2026-04-15 09:58:05 +08:00
// - 线下支付时:显示未支付(0)和已支付(1)状态的数据
// - 线上支付时:仅显示已支付(1)状态的数据
2026-05-11 14:56:42 +08:00
// - 排除预报名记录(5),仅在支付成功后才可见
2026-04-15 09:58:05 +08:00
.Where((r, e, fc) =>
2026-05-09 13:27:56 +08:00
r.PaymentStatus != 3 && // 排除已退款的报名
r.PaymentStatus != 4 && // 排除已取消的报名
2026-05-11 14:56:42 +08:00
r.PaymentStatus != 5 && // 排除预报名记录
2026-05-09 13:27:56 +08:00
((e.ChargeMode == ChargeMode.Free) ||
2026-04-15 09:58:05 +08:00
(r.PaymentMethod == 1 && (r.PaymentStatus == 0 || r.PaymentStatus == 1)) ||
2026-05-09 13:27:56 +08:00
(r.PaymentMethod == 2 && r.PaymentStatus == 1)))
2026-04-06 09:21:47 +08:00
.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
2026-03-14 05:19:40 +08:00
{
2026-04-09 23:12:51 +08:00
r.Id, r.EventId, e.EventName, r.TeamName, r.PaymentStatus, r.MaterialUrl, r.PaymentProofUrl,
2026-04-06 09:21:47 +08:00
r.CreatedTime, r.UpdatedTime, r.TotalPersonCount, e.MaterialUploadEndTime,
e.CoverImage, e.EventType, e.EventStatus, e.RegistrationStartTime, e.RegistrationEndTime,
e.MatchStartTime, e.MatchEndTime,
2026-04-15 09:58:05 +08:00
fc.ChargeMode,
r.PaymentMethod
2026-03-14 05:19:40 +08:00
})
.ToPagedListAsync(input);
var resultItems = list.Rows.Select(x => {
return new RegistrationItemDTO
{
RegistrationId = x.Id,
EventName = x.EventName,
2026-04-06 09:21:47 +08:00
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,
2026-03-14 05:19:40 +08:00
TeamName = x.TeamName,
CreateTime = x.CreatedTime,
UpdateTime = x.UpdatedTime,
2026-04-05 20:58:17 +08:00
PayStatus = x.PaymentStatus,
2026-04-20 13:01:39 +08:00
PaymentMethod = x.PaymentMethod,
2026-04-05 20:58:17 +08:00
CertStatus = 0, // 未颁发
2026-03-14 05:19:40 +08:00
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>
2026-03-17 15:32:01 +08:00
[DisplayName("取消报名")]
2026-03-20 03:15:31 +08:00
[HttpPost("cancel/{registrationId}")]
2026-03-14 05:19:40 +08:00
[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("报名记录不存在");
2026-04-09 23:12:51 +08:00
if (registration.PaymentStatus == 4) throw Oops.Bah("订单已取消");
2026-03-14 05:19:40 +08:00
2026-04-20 13:01:39 +08:00
// 校验状态:线下支付(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("已支付订单请联系客服退款");
}
2026-03-14 05:19:40 +08:00
2026-04-20 13:01:39 +08:00
_logger.LogInformation("开始取消报名:报名ID={RegistrationId}, 操作人={UserId}, 当前状态={PaymentStatus}, 支付方式={PaymentMethod}",
registrationId, userId, registration.PaymentStatus, registration.PaymentMethod);
2026-04-10 22:39:32 +08:00
// 更新报名状态
2026-04-09 23:12:51 +08:00
registration.PaymentStatus = 4; // 已取消
2026-03-14 05:19:40 +08:00
registration.UpdatedUserId = userId;
registration.UpdatedTime = DateTime.Now;
await _registrationRepository.UpdateAsync(registration);
2026-04-10 22:39:32 +08:00
// 同步更新订单状态
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);
2026-03-14 05:19:40 +08:00
return new ResponseDTO<bool> { Code = 200, Success = true, Data = true, Message = "取消成功" };
}
/// <summary>
/// 3. 上传素材
/// </summary>
2026-03-17 15:32:01 +08:00
[DisplayName("上传素材")]
2026-03-20 03:15:31 +08:00
[HttpPost("material/{registrationId}")]
2026-03-14 05:19:40 +08:00
[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)
{
2026-04-09 23:12:51 +08:00
var uploadResult = await _fileService.Upload(file);
var item = uploadResult.FirstOrDefault();
2026-03-14 05:19:40 +08:00
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>
2026-03-17 15:32:01 +08:00
[DisplayName("上传付款凭证")]
2026-03-20 03:15:31 +08:00
[HttpPost("voucher/{registrationId}")]
2026-03-14 05:19:40 +08:00
[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("报名记录不存在");
2026-04-27 11:23:47 +08:00
// 允许的文件类型
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;
2026-04-21 08:38:45 +08:00
2026-04-27 11:23:47 +08:00
// 验证文件类型
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");
}
2026-03-14 05:19:40 +08:00
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>
2026-03-17 15:32:01 +08:00
[DisplayName("查看队员列表")]
2026-03-20 03:15:31 +08:00
[HttpGet("members/{registrationId}")]
2026-03-14 05:19:40 +08:00
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 = "", // 暂无
2026-03-20 02:00:34 +08:00
Role = p.PersonType == PersonType.Leader ? "队长" : "队员", // 假设
2026-03-14 05:19:40 +08:00
InviteStatus = "已加入",
JoinTime = p.CreatedTime
}).ToList();
}
/// <summary>
/// 5.2 更换队员
/// </summary>
2026-03-17 15:32:01 +08:00
[DisplayName("更换队员")]
2026-03-20 03:15:31 +08:00
[HttpPost("members/replace/{registrationId}/{userId}")]
2026-03-14 05:19:40 +08:00
[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("队员不存在");
2026-04-09 23:12:51 +08:00
// 被替换人 role ≠ 队长
2026-03-20 02:00:34 +08:00
if (person.PersonType == PersonType.Leader) throw Oops.Bah("不能替换队长");
2026-03-14 05:19:40 +08:00
// 检查新队员
var newUser = await _userRepository.GetByIdAsync(input.NewUserId);
if (newUser == null) throw Oops.Bah("新队员不存在");
2026-04-09 23:12:51 +08:00
long oldUserId = person.Id;
2026-06-28 18:04:46 +08:00
var newCertNumber = newUser.AuthType == 2 ? newUser.PassportNumber : newUser.IdCardNumber;
2026-03-14 05:19:40 +08:00
// 更新信息
person.Name = newUser.Name;
2026-06-28 18:04:46 +08:00
person.IdCard = newCertNumber ?? "";
person.Phone = newUser.Mobile ?? "";
2026-03-14 05:19:40 +08:00
person.UpdatedTime = DateTime.Now;
await _registrationPersonRepository.UpdateAsync(person);
return new ResponseDTO<bool> { Code = 200, Success = true, Data = true, Message = "更换成功" };
}
/// <summary>
/// 6. 查看证书
/// </summary>
2026-03-17 15:32:01 +08:00
[DisplayName("查看证书")]
2026-03-20 03:15:31 +08:00
[HttpGet("certificate/{registrationId}")]
2026-03-14 05:19:40 +08:00
public async Task<ResponseDTO<CertificateDTO>> GetCertificate([FromRoute] long registrationId)
{
2026-04-16 15:42:29 +08:00
var userId = _authManager.UserId;
2026-03-14 05:19:40 +08:00
// 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 };
2026-04-16 15:42:29 +08:00
// 权限验证
bool hasPermission = await CheckCertificatePermissionAsync(userId, cert);
if (!hasPermission)
{
return new ResponseDTO<CertificateDTO> { Code = 403, Message = "无权查看该证书", Success = false };
}
2026-03-14 05:19:40 +08:00
return new ResponseDTO<CertificateDTO>
{
Code = 200, Success = true,
Data = new CertificateDTO
{
2026-04-13 00:40:44 +08:00
CertUrl = cert.CertificateUrl,
2026-03-14 05:19:40 +08:00
CertNumber = cert.CertificateNo,
IssueTime = cert.IssuedTime ?? DateTime.MinValue,
ExpireTime = null
}
};
}
2026-04-16 15:42:29 +08:00
/// <summary>
/// 检查证书查看权限
/// </summary>
private async Task<bool> CheckCertificatePermissionAsync(long currentUserId, EventCertificate cert)
{
// 如果是自己的证书,直接允许
if (cert.UserId == currentUserId)
{
return true;
}
2026-05-07 14:43:03 +08:00
// 先尝试获取 appuser
var appUser = await _userRepository.GetByIdAsync(currentUserId);
// 如果 appuser 为空,尝试获取 sysuser
var sysUser = appUser == null ? await _sysUserRepository.GetByIdAsync(currentUserId) : null;
// 两个用户都不存在,返回 false
if (appUser == null && sysUser == null)
2026-04-16 15:42:29 +08:00
{
return false;
}
2026-05-07 14:43:03 +08:00
long? merchantCertificationId = null;
// 优先使用 appuser 的认证信息
if (appUser != null && appUser.MerchantCertificationId.HasValue)
{
merchantCertificationId = appUser.MerchantCertificationId.Value;
}
// 如果 appuser 没有认证,尝试通过 sysuser 获取认证信息
else if (sysUser != null)
2026-04-16 15:42:29 +08:00
{
2026-05-07 14:43:03 +08:00
// 查找该 sysuser 关联的单位认证
var merchantCert = await _merchantCertificationRepository.GetFirstAsync(m =>
m.UserId == currentUserId &&
!m.DeleteMark &&
m.AuditStatus == AuditStatus.Pass &&
m.Status == AvailabilityStatus.Enable);
if (merchantCert != null)
2026-04-16 15:42:29 +08:00
{
2026-05-07 14:43:03 +08:00
merchantCertificationId = merchantCert.Id;
2026-04-16 15:42:29 +08:00
}
}
2026-05-07 14:43:03 +08:00
// 如果没有单位认证,返回 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;
}
2026-04-16 15:42:29 +08:00
return false;
}
2026-05-07 14:43:03 +08:00
/// <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);
2026-05-08 18:02:40 +08:00
// 获取成绩信息
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);
2026-05-07 14:43:03 +08:00
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 = "领队";
}
}
2026-05-08 18:02:40 +08:00
// 获取成绩和名次
var resultEntity = resultDict.TryGetValue(cert.ResultId, out var r) ? r : null;
2026-05-07 14:43:03 +08:00
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,
2026-05-08 18:02:40 +08:00
IssuedTime = cert.IssuedTime ?? DateTime.MinValue,
Score = resultEntity?.Score,
Rank = resultEntity?.Rank
2026-05-07 14:43:03 +08:00
};
}).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;
2026-04-15 09:58:05 +08:00
_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;
2026-04-15 09:58:05 +08:00
_logger.LogInformation("调用统一执行取消报名:报名ID={RegistrationId}, 操作人={UserId}", input.RegistrationId, userId);
return await _unifiedRegistrationService.ExecuteCancelRegistrationAsync(input, userId);
}
2026-04-30 09:28:50 +08:00
/// <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("报名记录不存在");
2026-05-11 14:56:42 +08:00
// 预报名记录不可见
if (registration.PaymentStatus == 5) throw Oops.Bah("报名记录不存在");
// 获取赛事信息
2026-04-15 09:58:05 +08:00
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,
2026-04-20 13:01:39 +08:00
EventId = registration.EventId,
EventName = eventInfo?.EventName ?? "",
2026-05-06 22:20:16 +08:00
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,
2026-04-20 13:01:39 +08:00
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,
2026-04-20 14:01:30 +08:00
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);
2026-04-20 13:01:39 +08:00
// 获取赛事信息
var eventInfo = await _eventRepository.GetFirstAsync(x => x.Id == registration.EventId);
if (eventInfo == null) throw Oops.Bah("赛事信息不存在");
2026-05-06 22:20:16 +08:00
// 检查报名时间是否已结束
var now = DateTime.Now;
if (now > eventInfo.RegistrationEndTime)
{
throw Oops.Bah("赛事报名时间已结束,无法更换参赛队员");
}
// 检查赛事状态,只有报名中状态才允许更换队员
if (eventInfo.EventStatus != EventStatus.Enrolling)
{
throw Oops.Bah("当前赛事状态不允许更换参赛队员");
}
2026-04-20 13:01:39 +08:00
// 获取报名项目信息
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}");
2026-06-06 23:34:12 +08:00
// 软删旧的队员
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;
2026-06-28 18:04:46 +08:00
var passportNumber = user?.PassportNumber;
var authType = user?.AuthType ?? 1;
var birthDate = member.BirthDate ?? user?.BirthDate ?? DateTime.Now.AddYears(-20);
var mobile = member.Mobile ?? user?.Mobile;
2026-06-28 18:04:46 +08:00
var certNumber = authType == 2 ? passportNumber : idCardNumber;
var certName = authType switch
{
2 => "护照号码",
3 => "身份证号(户口本认证)",
_ => "身份证号"
};
if (string.IsNullOrWhiteSpace(name)) throw Oops.Bah("队员姓名不能为空");
2026-06-28 18:04:46 +08:00
if (string.IsNullOrWhiteSpace(certNumber)) throw Oops.Bah($"队员{certName}不能为空");
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,
2026-06-28 18:04:46 +08:00
IdCard = certNumber ?? "",
BirthDate = birthDate,
2026-06-28 18:04:46 +08:00
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;
}
2026-04-20 14:01:30 +08:00
/// <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 = "验证通过"
};
2026-06-22 22:29:15 +08:00
var validationMemberIds = input.MemberIds?
.Where(id => id > 0)
.Distinct()
.ToList() ?? new List<long>();
if (!validationMemberIds.Contains(input.MemberId))
{
validationMemberIds.Add(input.MemberId);
}
2026-06-26 15:13:17 +08:00
var members = await _userRepository.GetListAsync(x => validationMemberIds.Contains(x.Id));
2026-06-22 22:29:15 +08:00
var member = members.FirstOrDefault(x => x.Id == input.MemberId);
2026-04-20 14:01:30 +08:00
// 添加调试:如果找不到,尝试不删除标记查询
if (member == null)
{
var memberWithoutDelete = await _db.Queryable<YD_Event.Core.Entities.AppUser>()
2026-06-22 22:29:15 +08:00
.FirstAsync(m => m.Id == input.MemberId);
2026-04-20 14:01:30 +08:00
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;
}
2026-06-26 15:13:17 +08:00
var eventProjectGroup = await _eventProjectGroupRepository.GetByIdAsync(input.GroupId);
2026-04-20 14:01:30 +08:00
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;
2026-06-22 22:29:15 +08:00
var persons = BuildValidationPersons(members, input.MemberId);
2026-04-20 14:01:30 +08:00
// 使用统一的验证方法进行验证
var validationResult = _unifiedRegistrationService.ValidatePersonQualifications(persons, eventProjectGroup, isTeamProject);
if (!validationResult.IsValid)
{
result.IsValid = false;
result.Message = validationResult.ErrorMessage ?? "验证失败";
// 根据验证详情设置具体的验证状态
2026-06-22 22:29:15 +08:00
var ageDetail = validationResult.Details?.FirstOrDefault(d =>
d.ItemName == "年龄验证" ||
d.ItemName == "年龄跨组" ||
d.ItemName.StartsWith("年龄跨组验证"));
2026-04-20 14:01:30 +08:00
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;
}
2026-06-22 22:29:15 +08:00
private static List<UnifiedPersonInput> BuildValidationPersons(List<YD_Event.Core.Entities.AppUser> members, long currentMemberId)
{
var orderedMembers = members
.OrderByDescending(x => x.Id == currentMemberId)
.ToList();
return orderedMembers.Select(m => new UnifiedPersonInput
{
UserId = m.Id,
Name = m.Name ?? "",
IdCardNumber = m.IdCardNumber ?? "",
2026-06-28 18:04:46 +08:00
PassportNumber = m.PassportNumber,
AuthType = m.AuthType,
2026-06-22 22:29:15 +08:00
Gender = m.Gender,
Mobile = m.Mobile,
BirthDate = m.BirthDate ?? DateTime.MinValue
}).ToList();
}
2026-04-20 14:01:30 +08:00
/// <summary>
/// 检查报名限制
/// </summary>
[DisplayName("检查报名限制")]
[HttpPost("registration-limit/check")]
public async Task<RegistrationLimitCheckOutput> CheckRegistrationLimit([FromBody] RegistrationLimitCheckInput input)
{
var userId = _authManager.UserId;
2026-06-26 15:13:17 +08:00
// 获取当前用户信息
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;
}
}
// 获取所有成员信息
2026-04-20 14:01:30 +08:00
var allMemberIds = new List<long>();
if (input.GroupMembers != null)
{
foreach (var gm in input.GroupMembers)
{
2026-06-26 15:13:17 +08:00
// 检查是否有多团队数据
2026-04-20 14:01:30 +08:00
if (gm.Teams != null && gm.Teams.Count > 0)
{
foreach (var team in gm.Teams)
{
allMemberIds.AddRange(team.MemberIds ?? new List<long>());
}
}
else
{
2026-06-26 15:13:17 +08:00
// 向后兼容单团队模式
2026-04-20 14:01:30 +08:00
allMemberIds.AddRange(gm.MemberIds ?? new List<long>());
}
}
}
else if (input.MemberIds != null)
{
2026-06-26 15:13:17 +08:00
// 向后兼容旧结构
2026-04-20 14:01:30 +08:00
allMemberIds.AddRange(input.MemberIds);
}
2026-06-26 15:13:17 +08:00
2026-04-20 14:01:30 +08:00
allMemberIds = allMemberIds.Distinct().ToList();
2026-06-26 15:13:17 +08:00
// 获取成员信息
var members = await _userRepository.GetListAsync(x => allMemberIds.Contains(x.Id));
// 获取组别信息
2026-04-20 14:01:30 +08:00
var groupIds = input.GroupMembers?.Select(g => g.GroupId).ToList() ?? input.GroupIds ?? new List<long>();
2026-06-26 15:13:17 +08:00
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => groupIds.Contains(x.Id));
// 获取项目信息
2026-04-20 14:01:30 +08:00
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 ?? "",
2026-06-28 18:04:46 +08:00
PassportNumber = m.PassportNumber,
AuthType = m.AuthType,
2026-04-20 14:01:30 +08:00
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 ?? "",
2026-06-28 18:04:46 +08:00
PassportNumber = m.PassportNumber,
AuthType = m.AuthType,
2026-04-20 14:01:30 +08:00
Gender = m.Gender,
Mobile = m.Mobile,
BirthDate = m.BirthDate ?? DateTime.MinValue
}).ToList();
}
unifiedInput.Items.Add(item);
}
2026-06-26 15:13:17 +08:00
// 调用统一的限制检查方法
return await _unifiedRegistrationService.CheckRegistrationLimitAsync(unifiedInput, input.ExcludeRegistrationId);
2026-04-20 14:01:30 +08:00
}
2026-03-14 05:19:40 +08:00
}