YD_Event.Admin/YD_Event.Application/Event/UnifiedRegistrationService.cs
2026-04-13 13:23:00 +08:00

1516 lines
64 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

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

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using YD_Event.Application.Event.Dtos;
using YD_Event.Core.Domain;
using YD_Event.Core.Domain.Pricing;
using YD_Event.Core.Domain.Pricing.Models;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using YD_Event.Core.Extensions;
using YD_Event.Core.Infrastructure.WeChat;
using YD_Event.Core.Infrastructure.WeChat.Dto;
using YD_Event.Core.Options;
namespace YD_Event.Application.Event;
/// <summary>
/// 统一报名处理服务
/// </summary>
public class UnifiedRegistrationService : ITransient
{
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
private readonly ISqlSugarRepository<EventUnit> _unitRepository;
private readonly ISqlSugarRepository<EventProject> _eventProjectRepository;
private readonly ISqlSugarRepository<EventProjectGroup> _eventProjectGroupRepository;
private readonly ISqlSugarRepository<ProjectGroup> _projectGroupRepository;
private readonly ISqlSugarRepository<EventProjectRegistrationLimit> _projectLimitRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _projectRepository;
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
private readonly ISqlSugarRepository<EventRegistrationItem> _registrationItemRepository;
private readonly ISqlSugarRepository<EventRegistrationPerson> _registrationPersonRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _userRepository;
private readonly ISqlSugarRepository<SysUser> _sysUserRepository;
private readonly ISqlSugarRepository<EventFeeConfig> _feeConfigRepository;
private readonly ISqlSugarRepository<EventRegistrationLimit> _limitRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
private readonly ISqlSugarRepository<WeChatPayConfig> _weChatPayConfigRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Merchant> _merchantRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Coupon> _couponRepository;
private readonly ISqlSugarRepository<YD_Event.Core.Entities.UserCoupon> _userCouponRepository;
private readonly ISqlSugarRepository<EventRegistrationPayment> _paymentRepository;
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
private readonly ISqlSugarClient _db;
private readonly IIdGenerator _idGenerator;
private readonly WeChatPayService _weChatPayService;
private readonly IOptionsMonitor<WechatTenpayClientOptions> _wechatTenpayClientOptionsMonitor;
private readonly ILogger<UnifiedRegistrationService> _logger;
private readonly OrderNoGeneratorService _orderNoGeneratorService;
public UnifiedRegistrationService(
ISqlSugarRepository<EventInfo> eventRepository,
ISqlSugarRepository<EventUnit> unitRepository,
ISqlSugarRepository<EventProject> eventProjectRepository,
ISqlSugarRepository<EventProjectGroup> eventProjectGroupRepository,
ISqlSugarRepository<ProjectGroup> projectGroupRepository,
ISqlSugarRepository<EventProjectRegistrationLimit> projectLimitRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Project> projectRepository,
ISqlSugarRepository<EventRegistration> registrationRepository,
ISqlSugarRepository<EventRegistrationItem> registrationItemRepository,
ISqlSugarRepository<EventRegistrationPerson> registrationPersonRepository,
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> userRepository,
ISqlSugarRepository<SysUser> sysUserRepository,
ISqlSugarRepository<EventFeeConfig> feeConfigRepository,
ISqlSugarRepository<EventRegistrationLimit> limitRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
ISqlSugarRepository<WeChatPayConfig> weChatPayConfigRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Merchant> merchantRepository,
ISqlSugarRepository<YD_Event.Core.Entities.Coupon> couponRepository,
ISqlSugarRepository<YD_Event.Core.Entities.UserCoupon> userCouponRepository,
ISqlSugarRepository<EventRegistrationPayment> paymentRepository,
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
ISqlSugarClient db,
IIdGenerator idGenerator,
WeChatPayService weChatPayService,
IOptionsMonitor<WechatTenpayClientOptions> wechatTenpayClientOptionsMonitor,
ILogger<UnifiedRegistrationService> logger,
OrderNoGeneratorService orderNoGeneratorService)
{
_eventRepository = eventRepository;
_unitRepository = unitRepository;
_eventProjectRepository = eventProjectRepository;
_eventProjectGroupRepository = eventProjectGroupRepository;
_projectGroupRepository = projectGroupRepository;
_projectLimitRepository = projectLimitRepository;
_projectRepository = projectRepository;
_registrationRepository = registrationRepository;
_registrationItemRepository = registrationItemRepository;
_registrationPersonRepository = registrationPersonRepository;
_userRepository = userRepository;
_sysUserRepository = sysUserRepository;
_feeConfigRepository = feeConfigRepository;
_limitRepository = limitRepository;
_orderRepository = orderRepository;
_weChatPayConfigRepository = weChatPayConfigRepository;
_merchantRepository = merchantRepository;
_couponRepository = couponRepository;
_userCouponRepository = userCouponRepository;
_paymentRepository = paymentRepository;
_merchantCertificationRepository = merchantCertificationRepository;
_db = db;
_idGenerator = idGenerator;
_weChatPayService = weChatPayService;
_wechatTenpayClientOptionsMonitor = wechatTenpayClientOptionsMonitor;
_logger = logger;
_orderNoGeneratorService = orderNoGeneratorService;
}
/// <summary>
/// 统一报名方法A - 处理核心报名业务逻辑
/// </summary>
[UnitOfWork]
public async Task<UnifiedRegistrationOutput> ProcessRegistrationAsync(UnifiedRegistrationInput input)
{
_logger.LogInformation("开始处理统一报名,赛事ID: {EventId}, 模式: {Mode}", input.EventId, input.RegistrationMode);
// 1. 验证报名
var validationResult = await ValidateRegistrationAsync(input);
if (!validationResult.IsValid)
{
throw Oops.Bah(validationResult.ErrorMessage ?? "报名验证失败");
}
// 2. 检查是否已有报名记录
var existingRegistration = await _registrationRepository.GetFirstAsync(
r => r.EventId == input.EventId && r.CreatedUserId == input.CreatedUserId && r.PaymentStatus != 4);
EventRegistration registration;
if (existingRegistration != null)
{
// 更新现有报名
registration = existingRegistration;
registration.RegistrationMode = input.RegistrationMode;
registration.TeamName = input.RegistrationMode == 2 ? input.TeamName : null;
registration.ContactName = input.ContactName;
registration.ContactPhone = input.ContactPhone;
registration.PaymentStatus = 0;
registration.UpdatedTime = DateTime.Now;
registration.UpdatedUserId = input.CreatedUserId;
// 删除旧的明细和人员
await _registrationItemRepository.DeleteAsync(x => x.RegistrationId == registration.Id);
await _registrationPersonRepository.DeleteAsync(x => x.RegistrationId == registration.Id);
}
else
{
// 创建新报名
registration = new EventRegistration
{
Id = _idGenerator.NewLong(),
EventId = input.EventId,
RegistrationMode = input.RegistrationMode,
TeamName = input.RegistrationMode == 2 ? input.TeamName : null,
ContactName = input.ContactName,
ContactPhone = input.ContactPhone,
PaymentStatus = 0,
RegistrationTime = DateTime.Now,
CreatedUserId = input.CreatedUserId,
CreatedTime = DateTime.Now,
Status = AvailabilityStatus.Enable,
OrderNo = string.Empty
};
await _registrationRepository.InsertAsync(registration);
}
// 3. 创建报名明细和参赛人员
var (totalAmount, totalPersonCount) = await CreateRegistrationDetailsAsync(registration, input);
// 4. 更新报名信息
registration.TotalAmount = totalAmount;
registration.TotalPersonCount = totalPersonCount;
await _registrationRepository.UpdateAsync(registration);
_logger.LogInformation("统一报名处理完成,报名ID: {RegistrationId}", registration.Id);
return new UnifiedRegistrationOutput
{
RegistrationId = registration.Id,
OrderNo = registration.OrderNo,
OriginalAmount = totalAmount,
DiscountAmount = 0,
ActualAmount = totalAmount,
PaymentStatus = registration.PaymentStatus
};
}
/// <summary>
/// 统一支付处理方法 - 处理支付相关逻辑
/// </summary>
[UnitOfWork]
public async Task<UnifiedPaymentOutput> ProcessPaymentAsync(UnifiedPaymentInput input)
{
_logger.LogInformation("开始处理统一支付,报名ID: {RegistrationId}, 支付方式: {PaymentMethod}",
input.RegistrationId, input.PaymentMethod);
// 1. 获取报名信息
var registration = await _registrationRepository.GetByIdAsync(input.RegistrationId);
if (registration == null)
{
throw Oops.Bah("报名记录不存在");
}
// 2. 获取赛事信息
var eventInfo = await _eventRepository.GetByIdAsync(registration.EventId);
if (eventInfo == null)
{
throw Oops.Bah("赛事信息不存在");
}
// 3. 计算优惠后金额
var (originalAmount, discountAmount, finalAmount, usedCoupon, usedUserCoupon) =
await CalculateFinalAmountAsync(registration.TotalAmount, input.CouponId, input.OperatorUserId);
// 4. 构建支付输出对象
var output = new UnifiedPaymentOutput
{
RegistrationId = registration.Id,
OrderNo = registration.OrderNo,
OriginalAmount = originalAmount,
DiscountAmount = discountAmount,
ActualAmount = finalAmount,
PaymentStatus = 0,
IsFree = finalAmount <= 0,
CouponRuleNote = usedCoupon != null ? "优惠券使用后不予退还" : null
};
// 5. 处理免费情况
if (finalAmount <= 0)
{
await HandleFreePaymentAsync(registration, usedUserCoupon, input.OperatorUserId);
output.PaymentStatus = 1;
return output;
}
// 6. 生成订单号
if (string.IsNullOrEmpty(registration.OrderNo))
{
registration.OrderNo = await _orderNoGeneratorService.GenerateOrderNoAsync();
output.OrderNo = registration.OrderNo;
}
// 处理支付方式转换逻辑
// 微信支付或支付宝支付 -> 线上支付
// 银行卡转账 -> 线下支付
if (input.PaymentMethodType == PaymentMethodType.WeChatPay ||
input.PaymentMethodType == PaymentMethodType.AliPay)
{
registration.PaymentMethod = 2; // 线上支付
}
else if (input.PaymentMethodType == PaymentMethodType.BankTransfer)
{
registration.PaymentMethod = 1; // 线下支付
}
else
{
// 如果没有指定具体支付方式,使用输入的支付方式
registration.PaymentMethod = input.PaymentMethod;
}
// 设置具体支付方式
registration.PaymentMethodType = input.PaymentMethodType;
// 线下支付订单状态设置为待审核
if (registration.PaymentMethod == 1)
{
registration.PaymentStatus = 0; // 待审核
}
else
{
registration.PaymentStatus = 0; // 线上支付初始状态也是待支付
}
await _registrationRepository.UpdateAsync(registration);
// 7. 创建订单记录
await CreateOrderAsync(registration, eventInfo, originalAmount, discountAmount, finalAmount,
input.CouponId, input.OperatorUserId);
// 8. 根据支付方式处理
if (input.PaymentMethod == 2) // 线上支付
{
var onlinePaymentOutput = await HandleOnlinePaymentAsync(registration, eventInfo, finalAmount, input);
output.WechatPayParams = onlinePaymentOutput.WechatPayParams;
output.QrcodeUrl = onlinePaymentOutput.QrcodeUrl;
output.TradeType = onlinePaymentOutput.TradeType;
}
else // 线下支付
{
var offlinePaymentOutput = await HandleOfflinePaymentAsync(registration, input);
output.PaymentStatus = offlinePaymentOutput.PaymentStatus;
}
return output;
}
/// <summary>
/// 验证报名
/// </summary>
private async Task<RegistrationValidationResult> ValidateRegistrationAsync(UnifiedRegistrationInput input)
{
var result = new RegistrationValidationResult
{
IsValid = true,
Details = new List<ValidationDetail>()
};
// 1. 验证赛事是否存在且可报名
var eventInfo = await _eventRepository.GetByIdAsync(input.EventId);
if (eventInfo == null)
{
result.IsValid = false;
result.ErrorMessage = "赛事不存在";
result.Details.Add(new ValidationDetail { ItemName = "赛事验证", IsPassed = false, Message = "赛事不存在" });
return result;
}
// 2. 验证报名时间
var now = DateTime.Now;
if (now < eventInfo.RegistrationStartTime || now > eventInfo.RegistrationEndTime)
{
result.IsValid = false;
result.ErrorMessage = "不在报名时间内";
result.Details.Add(new ValidationDetail { ItemName = "时间验证", IsPassed = false, Message = "不在报名时间内" });
return result;
}
// 3. 验证项目选择(一阶段:单选机制,每个项目只能选择一个组别)
var projectSelectionResult = await ValidateProjectSelectionAsync(input);
if (!projectSelectionResult.IsValid)
{
result.IsValid = false;
result.ErrorMessage = projectSelectionResult.ErrorMessage;
result.Details.AddRange(projectSelectionResult.Details);
return result;
}
result.Details.AddRange(projectSelectionResult.Details);
// 4. 验证队员分配(二阶段:人数校验、资质验证、年龄跨组等)
var teamAssignmentResult = await ValidateTeamAssignmentAsync(input);
if (!teamAssignmentResult.IsValid)
{
result.IsValid = false;
result.ErrorMessage = teamAssignmentResult.ErrorMessage;
result.Details.AddRange(teamAssignmentResult.Details);
return result;
}
result.Details.AddRange(teamAssignmentResult.Details);
// 5. 验证报名限制
var limitCheckResult = await CheckRegistrationLimitAsync(input);
if (limitCheckResult.HasLimit)
{
result.IsValid = false;
var firstLimit = limitCheckResult.LimitDetails.FirstOrDefault();
result.ErrorMessage = firstLimit?.LimitMessage ?? "报名限制检查失败";
result.Details.AddRange(limitCheckResult.LimitDetails.Select(l => new ValidationDetail
{
ItemName = l.LimitType,
IsPassed = false,
Message = l.LimitMessage
}));
return result;
}
result.Details.Add(new ValidationDetail { ItemName = "整体验证", IsPassed = true, Message = "验证通过" });
return result;
}
/// <summary>
/// 验证项目选择(一阶段:单选机制)
/// </summary>
private async Task<RegistrationValidationResult> ValidateProjectSelectionAsync(UnifiedRegistrationInput input)
{
var result = new RegistrationValidationResult
{
IsValid = true,
Details = new List<ValidationDetail>()
};
// 1. 检查是否有重复选择的项目(每个项目只能选择一个组别)
var duplicateProjects = input.Items
.GroupBy(i => i.ProjectId)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
if (duplicateProjects.Any())
{
var eventProjects = await _eventProjectRepository.GetListAsync(x => duplicateProjects.Contains(x.Id));
var projectNames = eventProjects.Select(ep => $"{ep.ProjectId}").ToList();
result.IsValid = false;
result.ErrorMessage = $"以下项目被重复选择,请确保每个项目只选择一个组别:{string.Join(", ", projectNames)}";
result.Details.Add(new ValidationDetail
{
ItemName = "项目选择",
IsPassed = false,
Message = result.ErrorMessage
});
return result;
}
result.Details.Add(new ValidationDetail
{
ItemName = "项目单选机制",
IsPassed = true,
Message = "每个项目仅选择一个组别,符合要求"
});
return result;
}
/// <summary>
/// 验证队员分配(二阶段)
/// </summary>
private async Task<RegistrationValidationResult> ValidateTeamAssignmentAsync(UnifiedRegistrationInput input)
{
var result = new RegistrationValidationResult
{
IsValid = true,
Details = new List<ValidationDetail>()
};
var eventProjectIds = input.Items.Select(i => i.ProjectId).Distinct().ToList();
var eventProjects = await _eventProjectRepository.GetListAsync(x => eventProjectIds.Contains(x.Id));
var projectIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
var baseProjects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => eventProjectIds.Contains(x.EventProjectId));
foreach (var item in input.Items)
{
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == item.ProjectId);
if (eventProject == null) continue;
var baseProject = baseProjects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
var isTeamProject = baseProject?.IsTeamProject() ?? false;
var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == item.GroupId);
if (eventProjectGroup == null)
{
result.IsValid = false;
result.ErrorMessage = $"项目组别不存在";
result.Details.Add(new ValidationDetail
{
ItemName = "组别验证",
IsPassed = false,
Message = result.ErrorMessage
});
return result;
}
// 处理团队列表(支持多团队报名)
var teamsToValidate = new List<UnifiedTeamInput>();
if (item.Teams != null && item.Teams.Any())
{
// 使用新的团队列表格式
teamsToValidate.AddRange(item.Teams);
}
else if (item.Persons != null && item.Persons.Any())
{
// 向后兼容:使用旧的单个团队格式
teamsToValidate.Add(new UnifiedTeamInput
{
TeamIndex = 1,
Persons = item.Persons
});
}
else
{
result.IsValid = false;
result.ErrorMessage = $"请为项目添加至少一个团队和队员";
result.Details.Add(new ValidationDetail
{
ItemName = "团队验证",
IsPassed = false,
Message = result.ErrorMessage
});
return result;
}
// 验证每个团队
foreach (var team in teamsToValidate)
{
if (team.Persons == null || !team.Persons.Any())
{
result.IsValid = false;
result.ErrorMessage = $"团队{team.TeamIndex}请至少添加一个队员";
result.Details.Add(new ValidationDetail
{
ItemName = "团队验证",
IsPassed = false,
Message = result.ErrorMessage
});
return result;
}
// 验证队员资质(年龄和性别)
var qualificationResult = ValidatePersonQualifications(team.Persons, eventProjectGroup, isTeamProject);
if (!qualificationResult.IsValid)
{
result.IsValid = false;
result.ErrorMessage = qualificationResult.ErrorMessage;
result.Details.AddRange(qualificationResult.Details);
return result;
}
result.Details.AddRange(qualificationResult.Details);
}
}
return result;
}
/// <summary>
/// 验证队员资质(年龄和性别)
/// </summary>
public RegistrationValidationResult ValidatePersonQualifications(List<UnifiedPersonInput> persons, EventProjectGroup group, bool isTeamProject)
{
var result = new RegistrationValidationResult
{
IsValid = true,
Details = new List<ValidationDetail>()
};
bool hasAtLeastOneInTargetAge = false;
bool hasAnyBelowTargetAge = false;
bool hasAnyAboveTargetAge = false;
bool allBelowTargetAge = true;
bool allOutsideTargetAge = true;
foreach (var person in persons)
{
// 验证性别 - 如果组别是"全部",则允许任何性别
if (group.Gender != Gender.All && person.Gender != group.Gender)
{
result.IsValid = false;
result.ErrorMessage = $"队员{person.Name}的性别不符合组别要求。要求:{group.Gender.Description()},实际:{person.Gender.Description()}";
result.Details.Add(new ValidationDetail
{
ItemName = "性别验证",
IsPassed = false,
Message = result.ErrorMessage
});
return result;
}
// 检查年龄状态
if (person.BirthDate >= group.BirthDateStart && person.BirthDate <= group.BirthDateEnd)
{
hasAtLeastOneInTargetAge = true;
allBelowTargetAge = false;
allOutsideTargetAge = false;
}
else if (person.BirthDate < group.BirthDateStart)
{
hasAnyBelowTargetAge = true;
}
else if (person.BirthDate > group.BirthDateEnd)
{
hasAnyAboveTargetAge = true;
allBelowTargetAge = false;
}
}
// 验证年龄
// 规则:
// 1. 所有队员年龄都在目标年龄段内 → 允许
// 2. 有队员年龄在目标年龄段内,且有队员年龄小于目标年龄段(跨组)→ 允许
// 3. 有队员年龄大于目标年龄段 → 不允许
if (hasAnyAboveTargetAge)
{
// 找到年龄大于目标年龄段的队员
var aboveAgePerson = persons.FirstOrDefault(p => p.BirthDate > group.BirthDateEnd);
if (aboveAgePerson != null)
{
result.IsValid = false;
result.ErrorMessage = $"队员{aboveAgePerson.Name}的年龄超过组别最大限制。要求出生日期不晚于:{group.BirthDateEnd:yyyy-MM-dd},实际:{aboveAgePerson.BirthDate:yyyy-MM-dd}";
result.Details.Add(new ValidationDetail
{
ItemName = "年龄验证",
IsPassed = false,
Message = result.ErrorMessage
});
return result;
}
}
// 仅在团队项目中应用"至少需要有一名成员属于目标年龄段"的验证规则
if (isTeamProject && hasAnyBelowTargetAge)
{
if (!hasAtLeastOneInTargetAge)
{
string errorMessage;
string itemName;
if (allBelowTargetAge)
{
errorMessage = "跨组报名时,团队中所有成员年龄均小于目标年龄段,团队中至少需要有一名成员属于目标年龄段";
itemName = "年龄跨组验证(全部小于目标年龄)";
}
else if (allOutsideTargetAge)
{
errorMessage = "跨组报名时,团队中所有成员年龄均不在目标年龄段范围内,团队中至少需要有一名成员属于目标年龄段";
itemName = "年龄跨组验证(全部不在目标年龄范围)";
}
else
{
errorMessage = "跨组报名时,团队中至少需要有一名成员属于目标年龄段";
itemName = "年龄跨组验证";
}
result.IsValid = false;
result.ErrorMessage = errorMessage;
result.Details.Add(new ValidationDetail
{
ItemName = itemName,
IsPassed = false,
Message = result.ErrorMessage
});
return result;
}
result.Details.Add(new ValidationDetail
{
ItemName = "年龄跨组",
IsPassed = true,
Message = "符合年龄跨组报名要求"
});
}
else
{
result.Details.Add(new ValidationDetail
{
ItemName = "年龄验证",
IsPassed = true,
Message = "所有队员均在目标年龄段内"
});
}
result.Details.Add(new ValidationDetail
{
ItemName = "队员资质",
IsPassed = true,
Message = "所有队员的性别符合组别要求,年龄符合规则"
});
return result;
}
/// <summary>
/// 创建报名明细和参赛人员
/// </summary>
private async Task<(decimal totalAmount, int totalPersonCount)> CreateRegistrationDetailsAsync(
EventRegistration registration, UnifiedRegistrationInput input)
{
// 获取相关数据
var eventProjectIds = input.Items.Select(i => i.ProjectId).Distinct().ToList();
var eventProjects = await _eventProjectRepository.GetListAsync(x => eventProjectIds.Contains(x.Id));
var projectIds = eventProjects.Select(x => x.ProjectId).Distinct().ToList();
var baseProjects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => eventProjectIds.Contains(x.EventProjectId));
var feeConfig = await _feeConfigRepository.GetFirstAsync(x => x.EventId == input.EventId && !x.DeleteMark);
var regItems = new List<EventRegistrationItem>();
var persons = new List<EventRegistrationPerson>();
int totalPersonCount = 0;
foreach (var item in input.Items)
{
var eventProject = eventProjects.FirstOrDefault(p => p.Id == item.ProjectId);
if (eventProject == null) continue;
var baseProject = baseProjects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
var groupName = GetGroupName(item, eventProjectGroups);
// 处理团队列表(支持多团队报名)
var teamsToProcess = new List<UnifiedTeamInput>();
if (item.Teams != null && item.Teams.Any())
{
// 使用新的团队列表格式
teamsToProcess.AddRange(item.Teams);
}
else if (item.Persons != null && item.Persons.Any())
{
// 向后兼容:使用旧的单个团队格式
teamsToProcess.Add(new UnifiedTeamInput
{
TeamIndex = 1,
Persons = item.Persons
});
}
// 为每个团队创建报名明细
foreach (var team in teamsToProcess)
{
// 计算费用
var (unitPrice, totalPrice) = CalculateItemPrice(feeConfig, eventProject, team.Persons.Count);
var regItemId = _idGenerator.NewLong();
var regItem = new EventRegistrationItem
{
Id = regItemId,
RegistrationId = registration.Id,
ProjectId = item.ProjectId,
EventProjectGroupId = item.GroupId,
ProjectName = baseProject?.Name ?? "",
GroupName = groupName,
ProjectTypeName = baseProject?.ProjectType.Description() ?? "",
UnitPrice = unitPrice,
PersonCount = team.Persons.Count,
TotalPrice = totalPrice,
CreatedUserId = input.CreatedUserId,
CreatedTime = DateTime.Now
};
regItems.Add(regItem);
totalPersonCount += team.Persons.Count;
// 添加参赛人员
foreach (var pInput in team.Persons)
{
persons.Add(new EventRegistrationPerson
{
Id = _idGenerator.NewLong(),
RegistrationId = registration.Id,
RegistrationItemId = regItemId,
ProjectId = item.ProjectId,
EventProjectGroupId = item.GroupId,
GroupName = groupName,
ProjectName = baseProject?.Name ?? "",
Name = pInput.Name,
Gender = pInput.Gender,
IdCard = pInput.IdCardNumber,
BirthDate = pInput.BirthDate,
Phone = pInput.Mobile,
Age = CalculateAge(pInput.BirthDate),
PersonType = PersonType.Athlete,
UserId = pInput.UserId ?? 0,
CreatedUserId = input.CreatedUserId,
CreatedTime = DateTime.Now
});
}
}
}
await _registrationItemRepository.InsertRangeAsync(regItems);
await _registrationPersonRepository.InsertRangeAsync(persons);
// 计算总费用
var totalAmount = CalculateTotalAmount(feeConfig, eventProjects, input.Items, totalPersonCount);
return (totalAmount, totalPersonCount);
}
/// <summary>
/// 计算最终金额(含优惠券)
/// </summary>
private async Task<(decimal originalAmount, decimal discountAmount, decimal finalAmount, YD_Event.Core.Entities.Coupon? coupon, YD_Event.Core.Entities.UserCoupon? userCoupon)>
CalculateFinalAmountAsync(decimal originalAmount, long? couponId, long userId)
{
decimal discountAmount = 0;
decimal finalAmount = originalAmount;
YD_Event.Core.Entities.Coupon? coupon = null;
YD_Event.Core.Entities.UserCoupon? userCoupon = null;
if (couponId.HasValue)
{
coupon = await _couponRepository.GetFirstAsync(c =>
c.Id == couponId.Value && c.Status == AvailabilityStatus.Enable && !c.DeleteMark);
if (coupon != null)
{
userCoupon = await _userCouponRepository.GetFirstAsync(uc =>
uc.CouponId == coupon.Id && uc.UserId == userId && uc.Status == 0 && !uc.DeleteMark);
if (userCoupon != null)
{
if (coupon.Type == 0) // 折扣
{
discountAmount = originalAmount * (1 - coupon.Value / 100);
}
else if (coupon.Type == 1) // 定额
{
discountAmount = Math.Min(coupon.Value, originalAmount);
}
finalAmount = originalAmount - discountAmount;
finalAmount = Math.Max(0, finalAmount);
}
}
}
return (originalAmount, discountAmount, finalAmount, coupon, userCoupon);
}
/// <summary>
/// 处理免费支付
/// </summary>
private async Task HandleFreePaymentAsync(EventRegistration registration, YD_Event.Core.Entities.UserCoupon? userCoupon, long operatorUserId)
{
if (userCoupon != null)
{
userCoupon.Status = 1;
userCoupon.UseTime = DateTime.Now;
await _userCouponRepository.UpdateAsync(userCoupon);
}
registration.PaymentStatus = 1;
registration.PaymentTime = DateTime.Now;
registration.UpdatedTime = DateTime.Now;
registration.UpdatedUserId = operatorUserId;
await _registrationRepository.UpdateAsync(registration);
}
/// <summary>
/// 创建订单记录
/// </summary>
private async Task<YD_Event.Core.Entities.Order> CreateOrderAsync(EventRegistration registration, EventInfo eventInfo,
decimal originalAmount, decimal discountAmount, decimal finalAmount, long? couponId, long operatorUserId)
{
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 = originalAmount,
ActualAmount = finalAmount,
DiscountAmount = discountAmount,
CouponId = couponId,
OrderStatus = OrderStatus.Pending,
CreatedTime = DateTime.Now,
CreatedUserId = operatorUserId,
Status = AvailabilityStatus.Enable
};
await _orderRepository.InsertAsync(order);
return order;
}
/// <summary>
/// 处理线上支付
/// </summary>
private async Task<UnifiedPaymentOutput> HandleOnlinePaymentAsync(EventRegistration registration,
EventInfo eventInfo, decimal finalAmount, UnifiedPaymentInput input)
{
WeChatPayConfig? payConfig = null;
if (eventInfo.MerchantId.HasValue)
{
payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
x.MerchantId == eventInfo.MerchantId.Value && x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
}
if (payConfig == null)
{
throw Oops.Bah("商户微信支付配置不存在");
}
var configOptions = _wechatTenpayClientOptionsMonitor.CurrentValue;
var output = new UnifiedPaymentOutput
{
RegistrationId = registration.Id,
OrderNo = registration.OrderNo,
PaymentStatus = 0,
IsFree = false
};
if (!string.IsNullOrEmpty(input.OpenId))
{
// JSAPI支付
var jsapiRequest = new CreateJsApiPayRequest
{
OutTradeNo = registration.OrderNo,
AppId = configOptions.AppId,
Description = $"{eventInfo.EventName} - 报名费",
NotifyUrl = configOptions.NotifyUrl,
TotalAmount = AmountHelper.YuanToFen(finalAmount),
ExpireTime = DateTimeOffset.Now.AddMinutes(30),
OpenId = input.OpenId
};
var payResult = await _weChatPayService.CreateJsApiPayAsync(payConfig, jsapiRequest);
if (payResult.Success && payResult.JsApiParameters != null)
{
output.WechatPayParams = new
{
appId = payResult.JsApiParameters.AppId,
timeStamp = payResult.JsApiParameters.TimeStamp,
nonceStr = payResult.JsApiParameters.NonceStr,
package = payResult.JsApiParameters.Package,
signType = payResult.JsApiParameters.SignType,
paySign = payResult.JsApiParameters.PaySign
};
output.TradeType = payResult.TradeType;
_logger.LogInformation("微信JSAPI支付下单成功,订单号:{OrderNo}", registration.OrderNo);
}
else
{
_logger.LogError("微信JSAPI支付下单失败,订单号:{OrderNo},错误:{Error}", registration.OrderNo, payResult.Message);
throw Oops.Bah($"微信统一下单失败:{payResult.Message}");
}
}
else
{
// Native支付
var nativeRequest = new CreateNativePayRequest
{
OutTradeNo = registration.OrderNo,
AppId = configOptions.AppId,
Description = $"{eventInfo.EventName} - 报名费",
NotifyUrl = configOptions.NotifyUrl,
TotalAmount = AmountHelper.YuanToFen(finalAmount),
ExpireTime = DateTimeOffset.Now.AddMinutes(5)
};
var payResult = await _weChatPayService.CreateNativePayAsync(payConfig, nativeRequest);
if (payResult.Success)
{
output.QrcodeUrl = payResult.QrcodeUrl;
output.TradeType = payResult.TradeType;
_logger.LogInformation("微信Native支付下单成功,订单号:{OrderNo}", registration.OrderNo);
}
else
{
_logger.LogError("微信Native支付下单失败,订单号:{OrderNo},错误:{Error}", registration.OrderNo, payResult.Message);
throw Oops.Bah($"微信统一下单失败:{payResult.Message}");
}
}
return output;
}
/// <summary>
/// 处理线下支付
/// </summary>
private async Task<UnifiedPaymentOutput> HandleOfflinePaymentAsync(EventRegistration registration, UnifiedPaymentInput input)
{
registration.PaymentProofUrl = input.PaymentProofUrl;
registration.PaymentStatus = 0; // 待审核
registration.UpdatedTime = DateTime.Now;
registration.UpdatedUserId = input.OperatorUserId;
await _registrationRepository.UpdateAsync(registration);
_logger.LogInformation("线下支付提交审核,报名ID:{RegistrationId},凭证URL:{ProofUrl}",
registration.Id, input.PaymentProofUrl);
return new UnifiedPaymentOutput
{
RegistrationId = registration.Id,
OrderNo = registration.OrderNo,
PaymentStatus = 0, // 待审核
IsFree = false
};
}
/// <summary>
/// 检查报名限制
/// </summary>
public async Task<RegistrationLimitCheckOutput> CheckRegistrationLimitAsync(UnifiedRegistrationInput input)
{
var result = new RegistrationLimitCheckOutput
{
HasLimit = false,
LimitDetails = new List<RegistrationLimitDetail>()
};
_logger.LogInformation("开始检查报名限制,赛事ID: {EventId}", input.EventId);
var limit = await _limitRepository.GetFirstAsync(x => x.EventId == input.EventId && !x.DeleteMark);
var projectLimits = await _projectLimitRepository.GetListAsync(x => x.EventId == input.EventId && !x.DeleteMark);
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == input.EventId);
var projects = await _projectRepository.GetListAsync(x => eventProjects.Select(ep => ep.ProjectId).Contains(x.Id) && !x.DeleteMark);
var eventProjectGroups = await _eventProjectGroupRepository.GetListAsync(x => x.EventId == input.EventId && !x.DeleteMark);
// 先获取所有报名信息,供后续检查使用
var eventRegistrations = await _registrationRepository.GetListAsync(x =>
x.EventId == input.EventId && x.PaymentStatus != 4 && !x.DeleteMark);
var eventRegistrationIds = eventRegistrations.Select(r => r.Id).ToList();
var registrationPersons = await _registrationPersonRepository.GetListAsync(x =>
eventRegistrationIds.Contains(x.RegistrationId) && !x.DeleteMark);
var allRegistrationItems = await _registrationItemRepository.GetListAsync(x =>
eventRegistrationIds.Contains(x.RegistrationId) && !x.DeleteMark);
_logger.LogInformation("获取到赛事报名记录: {RegistrationCount},报名人员: {PersonCount},报名项目: {ItemCount}",
eventRegistrations.Count, registrationPersons.Count, allRegistrationItems.Count);
// 1. 检查本次提交的团队数量是否超过项目限报配置(仅检查本次提交,不统计已报名数量)
var groupIds = input.Items.Where(i => i.GroupId.HasValue).Select(i => i.GroupId.Value).ToList();
if (groupIds.Any())
{
// 按组别分组统计本次提交的团队数量
var groupTeamCount = groupIds.GroupBy(g => g).ToDictionary(g => g.Key, g => g.Count());
foreach (var groupId in groupTeamCount.Keys)
{
var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == groupId);
if (eventProjectGroup == null) continue;
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);
if (eventProject == null) continue;
var projectLimit = projectLimits.FirstOrDefault(pl => pl.ProjectId == eventProject.Id);
if (projectLimit == null) continue;
var submittedTeamCount = groupTeamCount[groupId];
if (submittedTeamCount > projectLimit.LimitCount)
{
var project = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
var projectName = project?.Name ?? "未知项目";
_logger.LogWarning("组别容量超限,项目: {ProjectName},组别: {GroupName},限报: {LimitCount},提交: {SubmittedCount}",
projectName, eventProjectGroup.GroupName, projectLimit.LimitCount, submittedTeamCount);
result.HasLimit = true;
result.LimitDetails.Add(new RegistrationLimitDetail
{
MemberId = 0,
MemberName = eventProjectGroup.GroupName ?? "未知组别",
LimitType = "ProjectGroupCapacity",
LimitMessage = $"项目\"{projectName}\"的组别\"{eventProjectGroup.GroupName}\"限报{projectLimit.LimitCount}队,本次提交{submittedTeamCount}队,超出限报配置"
});
}
}
}
// 2. 检查个人/团队项目限报
var memberIds = input.Items.SelectMany(i => i.Persons.Where(p => p.UserId.HasValue).Select(p => p.UserId.Value)).Distinct().ToList();
if (limit != null && memberIds.Any())
{
var members = await _userRepository.GetListAsync(x => memberIds.Contains(x.Id) && !x.DeleteMark);
_logger.LogInformation("检查报名限制的成员数量: {MemberCount}", members.Count);
foreach (var member in members)
{
_logger.LogDebug("检查成员报名限制,成员ID: {MemberId}, 姓名: {MemberName}", member.Id, member.Name);
// 修改为通过 UserId 查询,而不是身份证号
var matchedPersons = registrationPersons.Where(x => x.UserId == member.Id).ToList();
var personRegistrationIds = matchedPersons.Select(x => x.RegistrationId).Distinct().ToList();
// 获取该队员已报名的所有项目项,增加赛事维度判断,仅统计同一赛事内的报名
var personRegistrationItems = allRegistrationItems.Where(ri =>
personRegistrationIds.Contains(ri.RegistrationId)).ToList();
// 统计个人项目和团队项目数量
int individualProjectCount = 0;
int teamProjectCount = 0;
foreach (var item in personRegistrationItems)
{
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == item.ProjectId);
if (eventProject == null) continue;
var baseProject = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
if (baseProject == null) continue;
// 根据项目类型判断是个人项目还是团队项目
if (baseProject.IsIndividualProject())
{
individualProjectCount++;
}
else if (baseProject.IsTeamProject())
{
teamProjectCount++;
}
}
_logger.LogDebug("成员 {MemberName} 个人项目报名数: {IndividualCount}, 团队项目报名数: {TeamCount}",
member.Name, individualProjectCount, teamProjectCount);
// 检查个人项目限报
if (individualProjectCount >= limit.IndividualProjectLimit)
{
_logger.LogWarning("个人项目限报超限,成员: {MemberName}, 已报名: {Count}, 限制: {Limit}",
member.Name, individualProjectCount, limit.IndividualProjectLimit);
result.HasLimit = true;
result.LimitDetails.Add(new RegistrationLimitDetail
{
MemberId = member.Id,
MemberName = member.Name ?? member.NickName ?? "未知",
LimitType = "IndividualProjectLimit",
LimitMessage = $"队员{member.Name}已报名{individualProjectCount}个个人项目,达到个人项目限报{limit.IndividualProjectLimit}个"
});
}
// 检查团队项目限报
if (teamProjectCount >= limit.TeamProjectLimit)
{
_logger.LogWarning("团队项目限报超限,成员: {MemberName}, 已报名: {Count}, 限制: {Limit}",
member.Name, teamProjectCount, limit.TeamProjectLimit);
result.HasLimit = true;
result.LimitDetails.Add(new RegistrationLimitDetail
{
MemberId = member.Id,
MemberName = member.Name ?? member.NickName ?? "未知",
LimitType = "TeamProjectLimit",
LimitMessage = $"队员{member.Name}已参与{teamProjectCount}个团队项目,达到团队项目限报{limit.TeamProjectLimit}个"
});
}
}
}
// 3. 强化人员-项目唯一性校验:同一人员不得同时在多个团队中报名同一个项目组别
var personProjectGroupMap = new Dictionary<string, List<(long GroupId, string GroupName, string ProjectName)>>();
foreach (var item in input.Items)
{
if (!item.GroupId.HasValue) continue;
var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == item.GroupId.Value);
if (eventProjectGroup == null) continue;
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);
if (eventProject == null) continue;
var project = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
var projectName = project?.Name ?? "未知项目";
foreach (var person in item.Persons)
{
// 使用UserId作为主键,但保留身份证作为辅助标识
var key = person.UserId.HasValue ? person.UserId.Value.ToString() : person.IdCardNumber;
if (string.IsNullOrEmpty(key)) continue;
if (!personProjectGroupMap.ContainsKey(key))
{
personProjectGroupMap[key] = new List<(long GroupId, string GroupName, string ProjectName)>();
}
personProjectGroupMap[key].Add((item.GroupId.Value, eventProjectGroup.GroupName ?? "未知组别", projectName));
}
}
// 检查同一人员在同一项目组别中是否出现多次
foreach (var kvp in personProjectGroupMap)
{
var key = kvp.Key;
var groupInfos = kvp.Value;
// 按组别分组
var groupDuplicates = groupInfos
.GroupBy(g => g.GroupId)
.Where(g => g.Count() > 1)
.ToList();
if (groupDuplicates.Any())
{
foreach (var duplicate in groupDuplicates)
{
var groupInfo = duplicate.First();
var person = input.Items.SelectMany(i => i.Persons).FirstOrDefault(p =>
(p.UserId.HasValue && p.UserId.Value.ToString() == key) ||
(!p.UserId.HasValue && p.IdCardNumber == key));
var personName = person?.Name ?? "未知人员";
_logger.LogWarning("人员项目组别重复,人员: {PersonName}, 项目: {ProjectName}, 组别: {GroupName}",
personName, groupInfo.ProjectName, groupInfo.GroupName);
result.HasLimit = true;
result.LimitDetails.Add(new RegistrationLimitDetail
{
MemberId = 0,
MemberName = personName,
LimitType = "PersonProjectDuplicate",
LimitMessage = $"人员\"{personName}\"在项目\"{groupInfo.ProjectName}\"的组别\"{groupInfo.GroupName}\"中重复出现,同一人员只能在一个团队中报名同一项目组别"
});
}
}
}
// 4. 检查人员是否已在其他报名中报过同一项目组别(同一赛事内)
if (eventRegistrationIds.Any())
{
foreach (var item in input.Items)
{
if (!item.GroupId.HasValue) continue;
var eventProjectGroup = eventProjectGroups.FirstOrDefault(epg => epg.Id == item.GroupId.Value);
if (eventProjectGroup == null) continue;
var eventProject = eventProjects.FirstOrDefault(ep => ep.Id == eventProjectGroup.EventProjectId);
if (eventProject == null) continue;
var project = projects.FirstOrDefault(p => p.Id == eventProject.ProjectId);
var projectName = project?.Name ?? "未知项目";
// 查找该组别已有的报名项(同一赛事内)
var existingItems = allRegistrationItems.Where(ri => ri.EventProjectGroupId == item.GroupId.Value).ToList();
if (!existingItems.Any()) continue;
var existingItemIds = existingItems.Select(ri => ri.Id).ToList();
var existingPersons = registrationPersons.Where(rp => existingItemIds.Contains(rp.RegistrationItemId)).ToList();
foreach (var person in item.Persons)
{
if (!person.UserId.HasValue) continue;
// 使用UserId查询已存在的人员
var existingPerson = existingPersons.FirstOrDefault(ep => ep.UserId == person.UserId.Value);
if (existingPerson != null)
{
_logger.LogWarning("人员已报名该项目组别,人员: {PersonName}, 项目: {ProjectName}, 组别: {GroupName}",
person.Name, projectName, eventProjectGroup.GroupName);
result.HasLimit = true;
result.LimitDetails.Add(new RegistrationLimitDetail
{
MemberId = 0,
MemberName = person.Name,
LimitType = "PersonAlreadyRegistered",
LimitMessage = $"人员\"{person.Name}\"已在项目\"{projectName}\"的组别\"{eventProjectGroup.GroupName}\"中报名过,不能重复报名"
});
}
}
}
}
_logger.LogInformation("报名限制检查完成,是否存在限制: {HasLimit}, 限制详情数: {LimitCount}",
result.HasLimit, result.LimitDetails.Count);
return result;
}
/// <summary>
/// 获取组别名称
/// </summary>
private string GetGroupName(UnifiedRegistrationItemInput item, List<EventProjectGroup> eventProjectGroups)
{
if (!string.IsNullOrEmpty(item.GroupName))
{
return item.GroupName;
}
if (item.GroupId.HasValue)
{
var selectedGroup = eventProjectGroups.FirstOrDefault(eg => eg.Id == item.GroupId.Value);
if (selectedGroup != null)
{
return selectedGroup.GroupName ?? "";
}
}
var firstGroup = eventProjectGroups.FirstOrDefault(eg => eg.EventProjectId == item.ProjectId);
return firstGroup?.GroupName ?? "";
}
/// <summary>
/// 计算单个项目费用
/// </summary>
private (decimal unitPrice, decimal totalPrice) CalculateItemPrice(EventFeeConfig? feeConfig, EventProject eventProject, int personCount)
{
decimal unitPrice = 0;
decimal totalPrice = 0;
if (feeConfig == null)
{
return (0, 0);
}
switch (feeConfig.ChargeMode)
{
case ChargeMode.Free:
unitPrice = 0;
totalPrice = 0;
break;
case ChargeMode.FixedPrice:
unitPrice = feeConfig.Price;
totalPrice = unitPrice * personCount;
break;
case ChargeMode.UnitPriceEvent:
unitPrice = feeConfig.Price;
totalPrice = unitPrice * personCount;
break;
case ChargeMode.UnitPriceProject:
unitPrice = eventProject.Price / 100m;
totalPrice = unitPrice * personCount;
break;
}
return (unitPrice, totalPrice);
}
/// <summary>
/// 计算总费用
/// </summary>
private decimal CalculateTotalAmount(EventFeeConfig? feeConfig, List<EventProject> eventProjects,
List<UnifiedRegistrationItemInput> items, int totalPersonCount)
{
if (feeConfig == null)
{
return 0;
}
var pricingInput = new PricingInput
{
ChargeMode = feeConfig.ChargeMode,
ParticipantCount = totalPersonCount,
EventPrice = feeConfig.Price
};
foreach (var item in items)
{
var ep = eventProjects.FirstOrDefault(p => p.Id == item.ProjectId);
if (ep != null)
{
pricingInput.Projects.Add(new ProjectPricingInfo
{
ProjectId = ep.Id,
UnitPrice = ep.Price
});
}
}
var pricingResult = EventPricingService.Calculate(pricingInput);
return pricingResult.TotalAmount;
}
/// <summary>
/// 根据出生日期计算年龄
/// </summary>
private static 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>
public async Task HandlePaymentSuccessAsync(string orderNo, string transactionId, DateTime? successTime, long operatorUserId)
{
var reg = await _registrationRepository.GetFirstAsync(r => r.OrderNo == orderNo);
if (reg == null)
{
_logger.LogWarning($"未找到订单:{orderNo}");
return;
}
if (reg.PaymentStatus == 1)
{
_logger.LogInformation($"订单 {reg.OrderNo} 已处理过支付");
return;
}
var evt = await _eventRepository.GetFirstAsync(e => e.Id == reg.EventId);
if (evt == null)
{
_logger.LogError($"未找到赛事信息,订单号:{reg.OrderNo},赛事ID:{reg.EventId}");
return;
}
_logger.LogInformation("订单支付成功,订单号={OrderNo},赛事ID={EventId},商户ID={MerchantId}",
reg.OrderNo, evt.Id, evt.MerchantId);
reg.PaymentStatus = 1;
reg.PaymentTime = successTime;
reg.RegistrationRemark = transactionId;
reg.UpdatedTime = DateTime.Now;
reg.UpdatedUserId = operatorUserId;
await _registrationRepository.UpdateAsync(reg);
_logger.LogInformation($"订单 {reg.OrderNo} 支付成功,微信交易号:{transactionId}");
var payment = new EventRegistrationPayment
{
Id = _idGenerator.NewLong(),
RegistrationId = reg.Id,
TransactionNo = transactionId ?? "",
PaymentChannel = 1,
PaymentAmount = reg.TotalAmount,
PaymentTime = successTime ?? DateTime.Now,
PaymentStatus = 1,
RefundStatus = 0,
PayeeAccount = "",
CreatedUserId = reg.CreatedUserId,
CreatedTime = DateTime.Now
};
await _paymentRepository.InsertAsync(payment);
_logger.LogInformation($"支付记录创建成功,报名ID={reg.Id},交易号={transactionId}");
var order = await _orderRepository.GetFirstAsync(o => o.OrderNo == reg.OrderNo);
if (order != null)
{
order.OrderStatus = OrderStatus.Paid;
order.PaymentTime = successTime;
order.UpdatedTime = DateTime.Now;
await _orderRepository.UpdateAsync(order);
_logger.LogInformation($"Order 记录 {order.OrderNo} 状态已更新为已支付");
}
}
/// <summary>
/// 取消报名
/// </summary>
public async Task<bool> CancelRegistrationAsync(long registrationId, long operatorUserId)
{
var reg = await _registrationRepository.GetFirstAsync(r => r.Id == registrationId && r.CreatedUserId == operatorUserId);
if (reg == null) throw Oops.Bah("报名记录不存在");
if (reg.PaymentStatus == 1) throw Oops.Bah("已支付订单请联系客服退款");
if (reg.PaymentStatus == 4) throw Oops.Bah("订单已取消,无需重复操作");
_logger.LogInformation("开始取消报名:报名ID={RegistrationId}, 操作人={UserId}, 当前状态={PaymentStatus}",
registrationId, operatorUserId, reg.PaymentStatus);
reg.PaymentStatus = 4;
reg.UpdatedTime = DateTime.Now;
reg.UpdatedUserId = operatorUserId;
await _registrationRepository.UpdateAsync(reg);
if (!string.IsNullOrEmpty(reg.OrderNo))
{
var order = await _orderRepository.GetFirstAsync(o => o.OrderNo == reg.OrderNo);
if (order != null)
{
order.OrderStatus = OrderStatus.Cancelled;
order.UpdatedTime = DateTime.Now;
order.UpdatedUserId = operatorUserId;
await _orderRepository.UpdateAsync(order);
_logger.LogInformation("订单状态已同步更新:订单号={OrderNo}, 状态={OrderStatus}",
reg.OrderNo, OrderStatus.Cancelled);
}
else
{
_logger.LogWarning("未找到对应的订单记录:报名ID={RegistrationId}, 订单号={OrderNo}",
registrationId, reg.OrderNo);
}
}
_logger.LogInformation("取消报名成功:报名ID={RegistrationId}", registrationId);
return true;
}
/// <summary>
/// 查询并同步微信支付状态
/// </summary>
public async Task<bool> QueryAndSyncWeChatPaymentStatusAsync(long registrationId, long operatorUserId)
{
var reg = await _registrationRepository.GetFirstAsync(r => r.Id == registrationId && r.CreatedUserId == operatorUserId);
if (reg == null) throw Oops.Bah("报名记录不存在");
if (reg.PaymentStatus == 1) return true;
var evt = await _eventRepository.GetFirstAsync(e => e.Id == reg.EventId);
if (evt == null) throw Oops.Bah("赛事不存在");
WeChatPayConfig? payConfig = null;
if (evt.MerchantId.HasValue)
{
payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
x.MerchantId == evt.MerchantId.Value &&
x.Status == AvailabilityStatus.Enable &&
!x.DeleteMark);
}
if (payConfig == null)
{
payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
}
if (payConfig == null)
{
_logger.LogError("未找到可用的微信支付配置,赛事ID={EventId},商户ID={MerchantId}", evt.Id, evt.MerchantId);
throw Oops.Bah("未找到可用的微信支付配置,请联系赛事管理员");
}
var queryResult = await _weChatPayService.QueryOrderAsync(payConfig, reg.OrderNo);
if (queryResult.Success && queryResult.TradeState == "SUCCESS")
{
await HandlePaymentSuccessAsync(reg.OrderNo, queryResult.TransactionId, queryResult.SuccessTime?.DateTime, operatorUserId);
return true;
}
return false;
}
/// <summary>
/// 获取平台级别的微信支付配置
/// </summary>
public async Task<WeChatPayConfig?> GetPlatformWeChatPayConfigAsync()
{
return await _weChatPayConfigRepository.GetFirstAsync(x =>
x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
}
/// <summary>
/// 根据赛事获取对应的微信支付配置
/// </summary>
public async Task<WeChatPayConfig?> GetWeChatPayConfigByEventAsync(EventInfo evt)
{
WeChatPayConfig? payConfig = null;
if (evt.MerchantId.HasValue)
{
payConfig = await _weChatPayConfigRepository.GetFirstAsync(x =>
x.MerchantId == evt.MerchantId.Value &&
x.Status == AvailabilityStatus.Enable &&
!x.DeleteMark);
}
if (payConfig == null)
{
payConfig = await GetPlatformWeChatPayConfigAsync();
}
return payConfig;
}
}