小程序赛事报名

This commit is contained in:
马梓彭 2026-04-13 18:14:32 +08:00
parent d3c3a847b0
commit 0927390ff1
6 changed files with 429 additions and 117 deletions

View File

@ -641,10 +641,20 @@ public class PaymentInfoDto
public long RegistrationId { get; set; }
/// <summary>
/// 金额
/// 实际金额
/// </summary>
public decimal Amount { get; set; }
/// <summary>
/// 原始金额
/// </summary>
public decimal OriginalAmount { get; set; }
/// <summary>
/// 优惠金额
/// </summary>
public decimal DiscountAmount { get; set; }
/// <summary>
/// 支付方式 (1-线下 2-线上)
/// </summary>
@ -959,6 +969,11 @@ public class GetPaymentInfoInput
[MaxLength(50, ErrorMessage = "OpenId长度不能超过50个字符")]
[RegularExpression(@"^[a-zA-Z0-9_-]+$", ErrorMessage = "OpenId格式不正确,只能包含字母、数字、下划线和连字符")]
public string OpenId { get; set; }
/// <summary>
/// 优惠券ID
/// </summary>
public long? CouponId { get; set; }
}
/// <summary>

View File

@ -499,105 +499,28 @@ public class EventClientController : IDynamicApiController
[DisplayName("计算报名金额")]
public async Task<CalculateAmountOutput> CalculateRegistrationAmount([FromBody] CalculateAmountInput input)
{
var evt = await _eventRepository.GetFirstAsync(e => e.Id == input.EventId && !e.DeleteMark);
if (evt == null) throw Oops.Bah("赛事不存在");
var inputItems = input.Items.Select(i => new UnifiedRegistrationService.CalculateAmountInputItem
{
ProjectId = i.ProjectId,
PersonCount = i.PersonCount
}).ToList();
var (totalAmount, chargeMode, itemDetails) = await _unifiedRegistrationService.CalculateRegistrationAmountAsync(input.EventId, inputItems);
var result = new CalculateAmountOutput
{
ChargeMode = evt.ChargeMode,
TotalAmount = 0,
ItemDetails = new List<CalculateAmountItemOutput>()
ChargeMode = chargeMode,
TotalAmount = totalAmount,
ItemDetails = itemDetails.Select(d => new CalculateAmountItemOutput
{
ProjectId = d.projectId,
ProjectName = d.projectName,
UnitPrice = d.unitPrice,
PersonCount = d.personCount,
Subtotal = d.subtotal
}).ToList()
};
if (evt.ChargeMode == ChargeMode.Free)
{
return result;
}
// 获取赛事费用配置
var feeConfig = await _feeConfigRepository.GetFirstAsync(f => f.EventId == input.EventId && !f.DeleteMark);
// 获取项目信息
var eventProjectIds = input.Items.Select(i => i.ProjectId).Distinct().ToList();
var eventProjects = await _eventProjectRepository.AsQueryable()
.Where(ep => eventProjectIds.Contains(ep.Id))
.ToListAsync();
var projectIds = eventProjects.Select(ep => ep.ProjectId).Distinct().ToList();
var baseProjects = await _projectRepository.AsQueryable()
.Where(p => projectIds.Contains(p.Id))
.ToListAsync();
// 计算总人数
int totalParticipantCount = input.Items.Sum(i => i.PersonCount);
// 构建定价服务输入
var pricingInput = new PricingInput
{
ChargeMode = evt.ChargeMode,
ParticipantCount = totalParticipantCount,
EventPrice = feeConfig?.Price ?? 0m
};
// 添加项目信息(用于单价项目模式)
foreach (var item in input.Items)
{
var ep = eventProjects.FirstOrDefault(p => p.Id == item.ProjectId);
if (ep == null) throw Oops.Bah($"项目不存在: {item.ProjectId}");
var bp = baseProjects.FirstOrDefault(p => p.Id == ep.ProjectId);
pricingInput.Projects.Add(new ProjectPricingInfo
{
ProjectId = ep.Id,
ProjectName = bp?.Name ?? "",
UnitPrice = ep.Price / 100m
});
}
// 使用统一的定价服务计算费用
var pricingResult = YD_Event.Core.Domain.Pricing.EventPricingService.Calculate(pricingInput);
result.TotalAmount = pricingResult.TotalAmount;
// 构建明细输出
foreach (var item in input.Items)
{
var ep = eventProjects.FirstOrDefault(p => p.Id == item.ProjectId);
var bp = baseProjects.FirstOrDefault(p => p.Id == ep?.ProjectId);
decimal unitPrice = 0;
decimal subtotal = 0;
switch (evt.ChargeMode)
{
case ChargeMode.FixedPrice:
// 定价(赛事):每个项目显示0,但实际只收一次固定费用
unitPrice = 0;
subtotal = 0;
break;
case ChargeMode.UnitPriceEvent:
// 单价(赛事):按赛事单价计算
unitPrice = feeConfig?.Price ?? 0;
subtotal = unitPrice * item.PersonCount;
break;
case ChargeMode.UnitPriceProject:
// 单价(项目):按项目单价计算
unitPrice = ep?.Price / 100m ?? 0;
subtotal = unitPrice * item.PersonCount;
break;
}
result.ItemDetails.Add(new CalculateAmountItemOutput
{
ProjectId = item.ProjectId,
ProjectName = bp?.Name ?? "",
UnitPrice = unitPrice,
PersonCount = item.PersonCount,
Subtotal = subtotal
});
}
return result;
}
@ -628,8 +551,8 @@ public class EventClientController : IDynamicApiController
}
// 获取当前用户信息(用于个人报名时自动填充)
var appUser = await _appUserRepository.GetFirstAsync(u => u.CreatedUserId == userId && !u.DeleteMark);
if (appUser == null && input.RegistrationMode == 1)
var appUser = await _appUserRepository.GetFirstAsync(u => u.Id == userId && !u.DeleteMark);
if (appUser == null && input.RegistrationMode == 1 && appUser.AuditStatus != AuditStatus.Pass)
throw Oops.Bah("请先完成身份认证");
// 个人报名模式下自动填充联系人信息
@ -684,6 +607,7 @@ public class EventClientController : IDynamicApiController
ContactPhone = input.ContactPhone ?? "",
CreatedUserId = userId,
IsDraft = false,
RegistrationChannel = 1, // 1-小程序渠道
Items = input.Items?.Select(item => new UnifiedRegistrationItemInput
{
ProjectId = item.ProjectId,
@ -725,6 +649,17 @@ public class EventClientController : IDynamicApiController
return result.RegistrationId;
}
/// <summary>
/// 验证优惠券
/// </summary>
[HttpPost("coupon/validate")]
[DisplayName("验证优惠券")]
public async Task<ValidateCouponOutput> ValidateCoupon([FromBody] ValidateCouponInput input)
{
var userId = GetCurrentUserId();
return await _unifiedRegistrationService.ValidateAndUseCouponAsync(input, userId);
}
/// <summary>
/// 获取支付信息
/// </summary>
@ -758,7 +693,7 @@ public class EventClientController : IDynamicApiController
RegistrationId = input.RegistrationId,
PaymentMethod = paymentMethod,
PaymentMethodType = reg.PaymentMethodType,
CouponId = null,
CouponId = input.CouponId,
OpenId = input.OpenId,
PaymentProofUrl = null,
OperatorUserId = userId
@ -772,6 +707,8 @@ public class EventClientController : IDynamicApiController
{
RegistrationId = unifiedResult.RegistrationId,
Amount = unifiedResult.ActualAmount,
OriginalAmount = unifiedResult.OriginalAmount,
DiscountAmount = unifiedResult.DiscountAmount,
PaymentMethod = unifiedResult.PaymentStatus == 1 ? 0 : paymentMethod, // 如果已支付,不需要显示支付方式
BankAccountName = merchant?.MerchantName,
BankAccountNo = merchant?.AccountNumber,

View File

@ -57,6 +57,11 @@ public class UnifiedRegistrationInput
/// 是否为草稿
/// </summary>
public bool IsDraft { get; set; }
/// <summary>
/// 报名渠道 (1-小程序 2-后台管理 3-其他)
/// </summary>
public int RegistrationChannel { get; set; } = 1;
}
/// <summary>

View File

@ -208,9 +208,41 @@ public class UnifiedRegistrationService : ITransient
throw Oops.Bah("赛事信息不存在");
}
// 3. 计算优惠后金额
var (originalAmount, discountAmount, finalAmount, usedCoupon, usedUserCoupon) =
await CalculateFinalAmountAsync(registration.TotalAmount, input.CouponId, input.OperatorUserId);
// 3. 检查是否已存在订单(幂等性处理)
var existingOrder = await _orderRepository.GetFirstAsync(o =>
o.RegistrationId == registration.Id && !o.DeleteMark);
decimal originalAmount, discountAmount, finalAmount;
YD_Event.Core.Entities.Coupon? usedCoupon = null;
YD_Event.Core.Entities.UserCoupon? usedUserCoupon = null;
// 如果订单已存在,使用订单中保存的金额,确保参数一致性
if (existingOrder != null)
{
_logger.LogInformation("找到已存在订单,报名ID: {RegistrationId}, 订单号: {OrderNo}",
registration.Id, existingOrder.OrderNo);
originalAmount = existingOrder.OriginalAmount;
discountAmount = existingOrder.DiscountAmount;
finalAmount = existingOrder.ActualAmount;
// 确保报名记录的订单号与订单一致
if (string.IsNullOrEmpty(registration.OrderNo))
{
registration.OrderNo = existingOrder.OrderNo;
await _registrationRepository.UpdateAsync(registration);
}
}
else
{
// 订单不存在,计算优惠后金额
var calculateResult = await CalculateFinalAmountAsync(
registration.TotalAmount, input.CouponId, input.OperatorUserId);
originalAmount = calculateResult.originalAmount;
discountAmount = calculateResult.discountAmount;
finalAmount = calculateResult.finalAmount;
usedCoupon = calculateResult.coupon;
usedUserCoupon = calculateResult.userCoupon;
}
// 4. 构建支付输出对象
var output = new UnifiedPaymentOutput
@ -220,7 +252,7 @@ public class UnifiedRegistrationService : ITransient
OriginalAmount = originalAmount,
DiscountAmount = discountAmount,
ActualAmount = finalAmount,
PaymentStatus = 0,
PaymentStatus = registration.PaymentStatus,
IsFree = finalAmount <= 0,
CouponRuleNote = usedCoupon != null ? "优惠券使用后不予退还" : null
};
@ -228,17 +260,24 @@ public class UnifiedRegistrationService : ITransient
// 5. 处理免费情况
if (finalAmount <= 0)
{
await HandleFreePaymentAsync(registration, usedUserCoupon, input.OperatorUserId);
if (registration.PaymentStatus != 1)
{
await HandleFreePaymentAsync(registration, usedUserCoupon, input.OperatorUserId);
}
output.PaymentStatus = 1;
return output;
}
// 6. 生成订单号
// 6. 生成订单号(仅在不存在时生成)
if (string.IsNullOrEmpty(registration.OrderNo))
{
registration.OrderNo = await _orderNoGeneratorService.GenerateOrderNoAsync();
output.OrderNo = registration.OrderNo;
}
else
{
output.OrderNo = registration.OrderNo;
}
// 处理支付方式转换逻辑
// 微信支付或支付宝支付 -> 线上支付
@ -268,18 +307,37 @@ public class UnifiedRegistrationService : ITransient
}
else
{
registration.PaymentStatus = 0; // 线上支付初始状态也是待支付
// 线上支付:只有在未支付时才设置为待支付
if (registration.PaymentStatus != 1)
{
registration.PaymentStatus = 0; // 待支付
}
}
await _registrationRepository.UpdateAsync(registration);
// 7. 创建订单记录
await CreateOrderAsync(registration, eventInfo, originalAmount, discountAmount, finalAmount,
input.CouponId, input.OperatorUserId);
// 7. 创建订单记录(仅在不存在时创建)
if (existingOrder == null)
{
await CreateOrderAsync(registration, eventInfo, originalAmount, discountAmount, finalAmount,
input.CouponId, input.OperatorUserId);
}
// 8. 根据支付方式处理
if (input.PaymentMethod == 2) // 线上支付
{
// 检查是否已经有支付记录或已支付状态,避免重复调用微信支付
var existingPayment = await _paymentRepository.GetFirstAsync(p =>
p.RegistrationId == registration.Id && !p.DeleteMark);
if (registration.PaymentStatus == 1 || existingPayment != null)
{
_logger.LogInformation("订单已支付或已有支付记录,跳过微信统一下单,报名ID: {RegistrationId}",
registration.Id);
output.PaymentStatus = registration.PaymentStatus;
return output;
}
var onlinePaymentOutput = await HandleOnlinePaymentAsync(registration, eventInfo, finalAmount, input);
output.WechatPayParams = onlinePaymentOutput.WechatPayParams;
output.QrcodeUrl = onlinePaymentOutput.QrcodeUrl;
@ -315,7 +373,39 @@ public class UnifiedRegistrationService : ITransient
return result;
}
// 2. 验证报名时间
// 1.5 验证报名相关配置参数
var configValidationResult = await ValidateRegistrationConfigAsync(eventInfo);
if (!configValidationResult.IsValid)
{
result.IsValid = false;
result.ErrorMessage = configValidationResult.ErrorMessage;
result.Details.AddRange(configValidationResult.Details);
return result;
}
result.Details.AddRange(configValidationResult.Details);
// 2. 验证用户单位认证状态:仅在满足以下特定条件时执行单位认证状态验证
// 条件1:报名类型为个人报名(非团队/机构报名)
// 条件2:用户通过小程序渠道进行报名
// 当同时满足上述两个条件时,执行以下单位认证验证逻辑
var isIndividualRegistration = input.RegistrationMode == 1; // 1-个人报名
var isMiniAppChannel = input.RegistrationChannel == 1; // 1-小程序渠道
if (isIndividualRegistration && isMiniAppChannel)
{
var certValidationResult = await ValidateUserCertificationAsync(input.CreatedUserId);
if (!certValidationResult.IsValid)
{
result.IsValid = false;
result.ErrorMessage = certValidationResult.ErrorMessage;
result.Details.AddRange(certValidationResult.Details);
return result;
}
result.Details.AddRange(certValidationResult.Details);
}
// 当不满足上述两个条件(即非个人报名或非小程序报名用户)时,跳过单位认证状态验证,不执行上述判断逻辑
// 4. 验证报名时间
var now = DateTime.Now;
if (now < eventInfo.RegistrationStartTime || now > eventInfo.RegistrationEndTime)
{
@ -325,7 +415,7 @@ public class UnifiedRegistrationService : ITransient
return result;
}
// 3. 验证项目选择(一阶段:单选机制,每个项目只能选择一个组别)
// 5. 验证项目选择(一阶段:单选机制,每个项目只能选择一个组别)
var projectSelectionResult = await ValidateProjectSelectionAsync(input);
if (!projectSelectionResult.IsValid)
{
@ -336,7 +426,7 @@ public class UnifiedRegistrationService : ITransient
}
result.Details.AddRange(projectSelectionResult.Details);
// 4. 验证队员分配(二阶段:人数校验、资质验证、年龄跨组等)
// 6. 验证队员分配(二阶段:人数校验、资质验证、年龄跨组等)
var teamAssignmentResult = await ValidateTeamAssignmentAsync(input);
if (!teamAssignmentResult.IsValid)
{
@ -347,7 +437,7 @@ public class UnifiedRegistrationService : ITransient
}
result.Details.AddRange(teamAssignmentResult.Details);
// 5. 验证报名限制
// 7. 验证报名限制
var limitCheckResult = await CheckRegistrationLimitAsync(input);
if (limitCheckResult.HasLimit)
{
@ -1280,7 +1370,7 @@ public class UnifiedRegistrationService : ITransient
totalPrice = unitPrice * personCount;
break;
case ChargeMode.UnitPriceProject:
unitPrice = eventProject.Price / 100m;
unitPrice = eventProject.Price;
totalPrice = unitPrice * personCount;
break;
}
@ -1323,6 +1413,65 @@ public class UnifiedRegistrationService : ITransient
return pricingResult.TotalAmount;
}
/// <summary>
/// 计算报名金额(对外公开方法)
/// </summary>
/// <param name="eventId">赛事ID</param>
/// <param name="items">报名项目列表(包含人员数量信息)</param>
/// <returns>计算结果</returns>
public async Task<(decimal totalAmount, ChargeMode chargeMode, List<(long projectId, string projectName, decimal unitPrice, int personCount, decimal subtotal)> itemDetails)> CalculateRegistrationAmountAsync(
long eventId, List<CalculateAmountInputItem> items)
{
var evt = await _eventRepository.GetFirstAsync(e => e.Id == eventId && !e.DeleteMark);
if (evt == null) throw Oops.Bah("赛事不存在");
var result = new List<(long projectId, string projectName, decimal unitPrice, int personCount, decimal subtotal)>();
if (evt.ChargeMode == ChargeMode.Free)
{
return (0, ChargeMode.Free, result);
}
var feeConfig = await _feeConfigRepository.GetFirstAsync(f => f.EventId == eventId && !f.DeleteMark);
var eventProjectIds = items.Select(i => i.ProjectId).Distinct().ToList();
var eventProjects = await _eventProjectRepository.GetListAsync(x => eventProjectIds.Contains(x.Id));
var projectIds = eventProjects.Select(ep => ep.ProjectId).Distinct().ToList();
var baseProjects = await _projectRepository.GetListAsync(x => projectIds.Contains(x.Id));
int totalParticipantCount = items.Sum(i => i.PersonCount);
var unifiedItems = items.Select(i => new UnifiedRegistrationItemInput
{
ProjectId = i.ProjectId,
Persons = new List<UnifiedPersonInput>(new UnifiedPersonInput[i.PersonCount])
}).ToList();
var totalAmount = CalculateTotalAmount(feeConfig, eventProjects, unifiedItems, totalParticipantCount);
foreach (var item in items)
{
var ep = eventProjects.FirstOrDefault(p => p.Id == item.ProjectId);
var bp = baseProjects.FirstOrDefault(p => p.Id == ep?.ProjectId);
if (ep != null)
{
var (unitPrice, subtotal) = CalculateItemPrice(feeConfig, ep, item.PersonCount);
result.Add((item.ProjectId, bp?.Name ?? "", unitPrice, item.PersonCount, subtotal));
}
}
return (totalAmount, evt.ChargeMode, result);
}
/// <summary>
/// 计算金额输入项(简化版)
/// </summary>
public class CalculateAmountInputItem
{
public long ProjectId { get; set; }
public int PersonCount { get; set; }
}
/// <summary>
/// 根据出生日期计算年龄
/// </summary>
@ -1648,4 +1797,117 @@ public class UnifiedRegistrationService : ITransient
return result;
}
/// <summary>
/// 验证报名相关配置参数
/// </summary>
private async Task<RegistrationValidationResult> ValidateRegistrationConfigAsync(EventInfo eventInfo)
{
var result = new RegistrationValidationResult
{
IsValid = true,
Details = new List<ValidationDetail>()
};
// 1. 验证费用配置是否存在(除非是免费赛事)
if (eventInfo.ChargeMode != ChargeMode.Free)
{
var feeConfig = await _feeConfigRepository.GetFirstAsync(x => x.EventId == eventInfo.Id && !x.DeleteMark);
if (feeConfig == null)
{
result.IsValid = false;
result.ErrorMessage = "赛事费用配置不存在,请联系管理员";
result.Details.Add(new ValidationDetail { ItemName = "费用配置验证", IsPassed = false, Message = "赛事费用配置不存在" });
return result;
}
// 验证费用配置值是否有效
if (eventInfo.ChargeMode == ChargeMode.FixedPrice && feeConfig.Price <= 0)
{
result.IsValid = false;
result.ErrorMessage = "赛事费用配置不正确,请联系管理员";
result.Details.Add(new ValidationDetail { ItemName = "费用配置验证", IsPassed = false, Message = "定价模式费用配置不正确" });
return result;
}
result.Details.Add(new ValidationDetail { ItemName = "费用配置验证", IsPassed = true, Message = "费用配置验证通过" });
}
else
{
result.Details.Add(new ValidationDetail { ItemName = "费用配置验证", IsPassed = true, Message = "免费赛事,无需费用配置" });
}
// 2. 验证赛事项目是否存在
var eventProjects = await _eventProjectRepository.GetListAsync(x => x.EventId == eventInfo.Id);
if (!eventProjects.Any())
{
result.IsValid = false;
result.ErrorMessage = "赛事未配置任何项目,请联系管理员";
result.Details.Add(new ValidationDetail { ItemName = "项目配置验证", IsPassed = false, Message = "赛事项目配置不存在" });
return result;
}
result.Details.Add(new ValidationDetail { ItemName = "项目配置验证", IsPassed = true, Message = $"赛事配置了{eventProjects.Count}个项目" });
// 3. 验证商户配置是否存在(如果需要支付)
if (eventInfo.ChargeMode != ChargeMode.Free)
{
if (eventInfo.MerchantId.HasValue)
{
var merchant = await _merchantRepository.GetFirstAsync(x => x.Id == eventInfo.MerchantId.Value && !x.DeleteMark);
if (merchant == null)
{
result.IsValid = false;
result.ErrorMessage = "赛事关联的商户不存在,请联系管理员";
result.Details.Add(new ValidationDetail { ItemName = "商户配置验证", IsPassed = false, Message = "赛事关联的商户不存在" });
return result;
}
result.Details.Add(new ValidationDetail { ItemName = "商户配置验证", IsPassed = true, Message = "商户配置验证通过" });
}
else
{
// 如果赛事没有关联商户,检查是否有平台级别的支付配置
var platformPayConfig = await _weChatPayConfigRepository.GetFirstAsync(x => x.Status == AvailabilityStatus.Enable && !x.DeleteMark);
if (platformPayConfig == null)
{
result.IsValid = false;
result.ErrorMessage = "支付配置不存在,请联系管理员";
result.Details.Add(new ValidationDetail { ItemName = "支付配置验证", IsPassed = false, Message = "平台支付配置不存在" });
return result;
}
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<RegistrationValidationResult> ValidateUserCertificationAsync(long userId)
{
var result = new RegistrationValidationResult
{
IsValid = true,
Details = new List<ValidationDetail>()
};
// 验证用户是否通过了单位认证
var certification = await _userRepository.GetFirstAsync(c =>
c.Id == userId &&
c.AuditStatus == AuditStatus.Pass &&
!c.DeleteMark);
if (certification == null)
{
result.IsValid = false;
result.ErrorMessage = "您尚未通过单位认证,请先完成单位认证";
result.Details.Add(new ValidationDetail { ItemName = "单位认证验证", IsPassed = false, Message = "用户未通过单位认证" });
return result;
}
result.Details.Add(new ValidationDetail { ItemName = "单位认证验证", IsPassed = true, Message = "用户单位认证验证通过" });
return result;
}
}

View File

@ -665,15 +665,22 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
{
try
{
_logger.LogInformation("========== 收到微信支付回调请求 ==========");
using var reader = new StreamReader(_httpContextAccessor.HttpContext.Request.Body);
var body = await reader.ReadToEndAsync();
_logger.LogInformation("微信支付回调 - 请求Body: {Body}", body);
var headers = _httpContextAccessor.HttpContext.Request.Headers;
var timestamp = headers["Wechatpay-Timestamp"].FirstOrDefault();
var nonce = headers["Wechatpay-Nonce"].FirstOrDefault();
var signature = headers["Wechatpay-Signature"].FirstOrDefault();
var serialNumber = headers["Wechatpay-Serial"].FirstOrDefault();
_logger.LogInformation("微信支付回调 - 头部参数: Timestamp={Timestamp}, Nonce={Nonce}, SerialNumber={SerialNumber}",
timestamp, nonce, serialNumber);
if (string.IsNullOrEmpty(timestamp) || string.IsNullOrEmpty(nonce) ||
string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(serialNumber))
{
@ -697,20 +704,35 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
Body = body
};
_logger.LogInformation("微信支付回调 - 开始验证签名");
var isValid = await _weChatPayService.VerifyPayNotifyAsync(platformPayConfig, notifyRequest);
if (!isValid)
{
_logger.LogWarning("微信支付回调签名验证失败");
return new JsonResult(new { code = "FAIL", message = "签名验证失败" });
}
_logger.LogInformation("微信支付回调 - 签名验证成功");
_logger.LogInformation("微信支付回调 - 开始解密数据");
var notifyData = await _weChatPayService.DecryptPayNotifyDataAsync(platformPayConfig, notifyRequest);
if (notifyData == null || notifyData.TradeState != "SUCCESS")
if (notifyData == null)
{
_logger.LogWarning("微信支付回调数据无效或交易未成功");
_logger.LogWarning("微信支付回调数据解密失败或为空");
return new JsonResult(new { code = "SUCCESS", message = "成功" });
}
_logger.LogInformation("微信支付回调 - 解密成功: OutTradeNo={OutTradeNo}, TradeState={TradeState}, TransactionId={TransactionId}",
notifyData.OutTradeNo, notifyData.TradeState, notifyData.TransactionId);
if (notifyData.TradeState != "SUCCESS")
{
_logger.LogWarning("微信支付回调交易未成功,TradeState={TradeState}", notifyData.TradeState);
return new JsonResult(new { code = "SUCCESS", message = "成功" });
}
_logger.LogInformation("微信支付回调 - 开始处理支付成功逻辑,订单号:{OutTradeNo}", notifyData.OutTradeNo);
// 调用统一支付成功处理方法
await _unifiedRegistrationService.HandlePaymentSuccessAsync(
notifyData.OutTradeNo,
@ -718,6 +740,9 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
notifyData.SuccessTime?.DateTime,
0);
_logger.LogInformation("微信支付回调 - 支付成功处理完成,订单号:{OutTradeNo}", notifyData.OutTradeNo);
_logger.LogInformation("========== 微信支付回调处理完成 ==========");
return new JsonResult(new { code = "SUCCESS", message = "成功" });
}
catch (ArgumentException ex)
@ -748,9 +773,20 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
if (orderInfo == null || orderInfo.Order == null) throw Oops.Bah("订单不存在");
if (orderInfo.Order.OrderStatus == OrderStatus.Paid)
{
_logger.LogInformation("查询订单支付状态 - 订单已支付,订单号:{OrderNo}", orderNo);
return 1; // 已支付
}
if (orderInfo.Registration?.PaymentMethod == 1)
// 检查是否是微信/支付宝支付(线上支付)
bool isOnlinePayment = orderInfo.Registration?.PaymentMethod == 2 ||
orderInfo.Registration?.PaymentMethodType == PaymentMethodType.WeChatPay ||
orderInfo.Registration?.PaymentMethodType == PaymentMethodType.AliPay;
_logger.LogInformation("查询订单支付状态 - 订单号:{OrderNo},PaymentMethod={PaymentMethod},PaymentMethodType={PaymentMethodType},IsOnlinePayment={IsOnlinePayment}",
orderNo, orderInfo.Registration?.PaymentMethod, orderInfo.Registration?.PaymentMethodType, isOnlinePayment);
if (isOnlinePayment)
{
WeChatPayConfig? payConfig = null;
if (orderInfo.Order.MerchantId.HasValue)
@ -769,7 +805,11 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
if (payConfig != null)
{
_logger.LogInformation("查询订单支付状态 - 调用微信支付查询接口,订单号:{OrderNo}", orderNo);
var queryResult = await _weChatPayService.QueryOrderAsync(payConfig, orderNo);
_logger.LogInformation("查询订单支付状态 - 微信支付查询结果,订单号:{OrderNo},Success={Success},TradeState={TradeState}",
orderNo, queryResult.Success, queryResult.TradeState);
if (queryResult.Success && queryResult.TradeState == "SUCCESS")
{
orderInfo.Order.OrderStatus = OrderStatus.Paid;
@ -786,9 +826,14 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
registration.PaymentStatus = 1;
await _registrationRepository.UpdateAsync(registration);
}
_logger.LogInformation("查询订单支付状态 - 订单已更新为已支付,订单号:{OrderNo}", orderNo);
return 1;
}
}
else
{
_logger.LogWarning("查询订单支付状态 - 未找到微信支付配置,订单号:{OrderNo}", orderNo);
}
}
return 0; // 未支付

View File

@ -2209,7 +2209,17 @@
</member>
<member name="P:YD_Event.Application.Client.Dtos.PaymentInfoDto.Amount">
<summary>
金额
实际金额
</summary>
</member>
<member name="P:YD_Event.Application.Client.Dtos.PaymentInfoDto.OriginalAmount">
<summary>
原始金额
</summary>
</member>
<member name="P:YD_Event.Application.Client.Dtos.PaymentInfoDto.DiscountAmount">
<summary>
优惠金额
</summary>
</member>
<member name="P:YD_Event.Application.Client.Dtos.PaymentInfoDto.PaymentMethod">
@ -2506,6 +2516,11 @@
- 允许的字符:字母、数字、下划线、连字符
</remarks>
</member>
<member name="P:YD_Event.Application.Client.Dtos.GetPaymentInfoInput.CouponId">
<summary>
优惠券ID
</summary>
</member>
<member name="T:YD_Event.Application.Client.Dtos.UploadMaterialInput">
<summary>
上传素材输入DTO
@ -3329,6 +3344,11 @@
<param name="input"></param>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Client.EventClientController.ValidateCoupon(YD_Event.Application.Event.Dtos.ValidateCouponInput)">
<summary>
验证优惠券
</summary>
</member>
<member name="M:YD_Event.Application.Client.EventClientController.GetPaymentInfo(YD_Event.Application.Client.Dtos.GetPaymentInfoInput)">
<summary>
获取支付信息
@ -5677,6 +5697,11 @@
是否为草稿
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UnifiedRegistrationInput.RegistrationChannel">
<summary>
报名渠道 (1-小程序 2-后台管理 3-其他)
</summary>
</member>
<member name="T:YD_Event.Application.Event.Dtos.UnifiedRegistrationItemInput">
<summary>
统一报名项目输入DTO
@ -7454,6 +7479,19 @@
计算总费用
</summary>
</member>
<member name="M:YD_Event.Application.Event.UnifiedRegistrationService.CalculateRegistrationAmountAsync(System.Int64,System.Collections.Generic.List{YD_Event.Application.Event.UnifiedRegistrationService.CalculateAmountInputItem})">
<summary>
计算报名金额(对外公开方法)
</summary>
<param name="eventId">赛事ID</param>
<param name="items">报名项目列表(包含人员数量信息)</param>
<returns>计算结果</returns>
</member>
<member name="T:YD_Event.Application.Event.UnifiedRegistrationService.CalculateAmountInputItem">
<summary>
计算金额输入项(简化版)
</summary>
</member>
<member name="M:YD_Event.Application.Event.UnifiedRegistrationService.CalculateAge(System.DateTime)">
<summary>
根据出生日期计算年龄
@ -7489,6 +7527,16 @@
统一优惠券验证和使用方法
</summary>
</member>
<member name="M:YD_Event.Application.Event.UnifiedRegistrationService.ValidateRegistrationConfigAsync(YD_Event.Core.Entities.EventInfo)">
<summary>
验证报名相关配置参数
</summary>
</member>
<member name="M:YD_Event.Application.Event.UnifiedRegistrationService.ValidateUserCertificationAsync(System.Int64)">
<summary>
验证用户单位认证状态
</summary>
</member>
<member name="T:YD_Event.Application.Event.UserEventRecordService">
<summary>
用户端参赛记录管理