From e03ad5eb465ad9bf657072bfe0b4ad7ce967623f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A9=AC=E6=A2=93=E5=BD=AD?= Date: Mon, 11 May 2026 15:30:05 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=A4=8D=E6=8A=A5=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Client/EventClientController.cs | 6 +- .../Event/UnifiedRegistrationService.cs | 83 ++++++++++++++----- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/YD_Event.Application/Client/EventClientController.cs b/YD_Event.Application/Client/EventClientController.cs index 57b5ccb..59ec067 100644 --- a/YD_Event.Application/Client/EventClientController.cs +++ b/YD_Event.Application/Client/EventClientController.cs @@ -923,13 +923,13 @@ public class EventClientController : IDynamicApiController else if (now >= reg.MatchStart && now <= reg.MatchEnd) status = EventStatus.Ongoing; else if (now > reg.MatchEnd) status = EventStatus.Ended; - // 获取 Items 和 Persons + // 获取 Items 和 Persons - 只获取未被软删除的 var items = await _registrationItemRepository.AsQueryable() - .Where(i => i.RegistrationId == registrationId) + .Where(i => i.RegistrationId == registrationId && !i.DeleteMark) .ToListAsync(); var persons = await _personRepository.AsQueryable() - .Where(p => p.RegistrationId == registrationId) + .Where(p => p.RegistrationId == registrationId && !p.DeleteMark) .ToListAsync(); // 计算退款金额和获取退款规则 diff --git a/YD_Event.Application/Event/UnifiedRegistrationService.cs b/YD_Event.Application/Event/UnifiedRegistrationService.cs index 485bea6..2460e4e 100644 --- a/YD_Event.Application/Event/UnifiedRegistrationService.cs +++ b/YD_Event.Application/Event/UnifiedRegistrationService.cs @@ -184,9 +184,35 @@ public class UnifiedRegistrationService : ITransient registration.UpdatedTime = DateTime.Now; registration.UpdatedUserId = input.CreatedUserId; - // 删除旧的明细和人员 - await _registrationItemRepository.DeleteAsync(x => x.RegistrationId == registration.Id); - await _registrationPersonRepository.DeleteAsync(x => x.RegistrationId == registration.Id); + // 软删除旧的明细和人员(保留历史记录,与取消报名逻辑一致) + var oldItems = await _registrationItemRepository.GetListAsync(x => x.RegistrationId == registration.Id && !x.DeleteMark); + var oldPersons = await _registrationPersonRepository.GetListAsync(x => x.RegistrationId == registration.Id && !x.DeleteMark); + + var now = DateTime.Now; + + if (oldItems.Any()) + { + foreach (var item in oldItems) + { + item.DeleteMark = true; + item.UpdatedUserId = input.CreatedUserId; + item.UpdatedTime = now; + } + await _registrationItemRepository.UpdateRangeAsync(oldItems); + _logger.LogInformation("重新报名,软删除旧项目:报名ID={RegistrationId}, 项目数={Count}", registration.Id, oldItems.Count); + } + + if (oldPersons.Any()) + { + foreach (var person in oldPersons) + { + person.DeleteMark = true; + person.UpdatedUserId = input.CreatedUserId; + person.UpdatedTime = now; + } + await _registrationPersonRepository.UpdateRangeAsync(oldPersons); + _logger.LogInformation("重新报名,软删除旧人员:报名ID={RegistrationId}, 人数={Count}", registration.Id, oldPersons.Count); + } return registration; } @@ -2792,13 +2818,17 @@ public class UnifiedRegistrationService : ITransient projects = projectList.ToDictionary(x => x.Id); } + // 查询订单获取实际支付金额(考虑优惠券抵扣) + var order = await _orderRepository.GetFirstAsync(x => x.RegistrationId == registrationId && x.OrderType == 1 && !x.DeleteMark); + var actualPaidAmount = order?.ActualAmount ?? registration.TotalAmount; + var output = new CancelPreviewOutput { RegistrationId = registration.Id, EventName = eventInfo?.EventName ?? "", TeamName = registration.TeamName ?? "", ChargeMode = (int)eventInfo.ChargeMode, - ActualPaidAmount = registration.TotalAmount, + ActualPaidAmount = actualPaidAmount, RefundableAmount = refundableAmount, RefundedAmount = refundedAmount, CurrentRefundAmount = refundableAmount, @@ -2807,7 +2837,7 @@ public class UnifiedRegistrationService : ITransient NeedPayeeInfo = registration.PaymentMethod == 1 && registration.PaymentStatus == 1 }; - foreach (var item in items) + foreach (var item in items.Where(x => !x.DeleteMark)) { output.Projects.Add(new CancelPreviewProjectDto { @@ -2819,7 +2849,7 @@ public class UnifiedRegistrationService : ITransient }); } - foreach (var person in persons) + foreach (var person in persons.Where(x => !x.DeleteMark)) { output.Members.Add(new CancelPreviewMemberDto { @@ -2830,7 +2860,7 @@ public class UnifiedRegistrationService : ITransient } // 构建新版详细数据结构(按项目 -> 组别 -> 团队 -> 队员分类) - var itemGroups = items.GroupBy(x => x.ProjectId); + var itemGroups = items.Where(x => !x.DeleteMark).GroupBy(x => x.ProjectId); foreach (var itemGroup in itemGroups) { var firstItem = itemGroup.First(); @@ -2879,8 +2909,8 @@ public class UnifiedRegistrationService : ITransient int teamIndex = 0; foreach (var item in groupGroup) { - // 获取该报名项目下的所有队员 - var itemPersons = persons.Where(p => p.RegistrationItemId == item.Id).ToList(); + // 获取该报名项目下的所有队员 - 只获取未被软删除的 + var itemPersons = persons.Where(p => p.RegistrationItemId == item.Id && !p.DeleteMark).ToList(); // 每个报名项作为一个独立的团队条目 var teamDto = new CancelPreviewTeamDto @@ -3025,12 +3055,16 @@ public class UnifiedRegistrationService : ITransient remainingRefundableAmount = await CalculateRefundAmountAsync(remainingOriginalAmount, eventInfo.MatchStartTime); } + // 查询订单获取实际支付金额(考虑优惠券抵扣) + var order = await _orderRepository.GetFirstAsync(x => x.RegistrationId == input.RegistrationId && x.OrderType == 1 && !x.DeleteMark); + var actualPaidAmount = order?.ActualAmount ?? registration.TotalAmount; + return new CalculateRefundAmountOutput { refundAmount = refundAmount, refundableAmount = remainingRefundableAmount, refundedAmount = refundedAmount, - actualPaidAmount = registration.TotalAmount + actualPaidAmount = actualPaidAmount }; } @@ -3328,9 +3362,20 @@ public class UnifiedRegistrationService : ITransient // 计算实际退款金额 // 【重要】始终信任后端计算结果,前端计算只用于展示 // 不接受前端传递的退款金额,避免资金风险 - decimal actualRefundAmount = await CalculateActualRefundAmountAsync(input, registration, eventInfo, items, persons); - _logger.LogInformation("统一退款金额计算:报名ID={RegistrationId}, 总金额={TotalAmount}, 退款金额={ActualRefundAmount}", - registration.Id, registration.TotalAmount, actualRefundAmount); + var order = await _orderRepository.GetFirstAsync(o => o.RegistrationId == registration.Id && o.OrderType == 1 && !o.DeleteMark); + var actualPaidAmount = order?.ActualAmount ?? registration.TotalAmount; + var originalAmount = registration.TotalAmount; + + // 先计算按原价应该退多少 + decimal originalRefundAmount = await CalculateActualRefundAmountAsync(input, registration, eventInfo, items, persons); + + // 然后按比例计算实际退款金额(考虑优惠券折扣) + decimal actualRefundAmount = originalAmount > 0 ? originalRefundAmount * (actualPaidAmount / originalAmount) : 0; + // 确保退款金额不超过实际支付金额 + actualRefundAmount = Math.Min(actualRefundAmount, actualPaidAmount); + + _logger.LogInformation("统一退款金额计算:报名ID={RegistrationId}, 原价={OriginalAmount}, 实付={ActualPaidAmount}, 按原价应退={OriginalRefundAmount}, 实际退款={ActualRefundAmount}", + registration.Id, originalAmount, actualPaidAmount, originalRefundAmount, actualRefundAmount); bool isPartialCancel = input.CancelType == 1; @@ -3489,12 +3534,12 @@ public class UnifiedRegistrationService : ITransient } else { - registration.PaymentStatus = 3; + registration.PaymentStatus = 4; // 已取消(与线下支付保持一致) registration.UpdatedUserId = operatorUserId; registration.UpdatedTime = now; registration.RegistrationRemark = input.CancelReason; await _registrationRepository.UpdateAsync(registration); - _logger.LogInformation("统一所有人员已退款,更新报名状态为已退款:报名ID={RegistrationId}", registration.Id); + _logger.LogInformation("统一所有人员已退款,更新报名状态为已取消:报名ID={RegistrationId}", registration.Id); } } else @@ -3510,7 +3555,7 @@ public class UnifiedRegistrationService : ITransient await SoftDeleteItemsAsync(items.Select(i => i.Id).ToList(), operatorUserId, now); _logger.LogInformation("已软删除所有项目:报名ID={RegistrationId}", registration.Id); - registration.PaymentStatus = 3; + registration.PaymentStatus = 4; // 已取消(与线下支付保持一致) registration.UpdatedUserId = operatorUserId; registration.UpdatedTime = now; registration.RegistrationRemark = input.CancelReason; @@ -3568,7 +3613,7 @@ public class UnifiedRegistrationService : ITransient // 创建线下支付退款记录 await CreateRefundRecordAsync(registration, eventInfo, null, refundOrder.OrderNo, - null, actualRefundAmount, registration.TotalAmount, input.CancelReason, operatorUserId, + null, actualRefundAmount, actualPaidAmount, input.CancelReason, operatorUserId, input.PayeeName, input.PayeeBankName, input.PayeeBankAccount); } } @@ -3624,7 +3669,7 @@ public class UnifiedRegistrationService : ITransient OutTradeNo = registration.OrderNo, OutRefundNo = outRefundNo, RefundAmount = AmountHelper.YuanToFen(actualRefundAmount), - TotalAmount = AmountHelper.YuanToFen(registration.TotalAmount), + TotalAmount = AmountHelper.YuanToFen(actualPaidAmount), // 使用实际支付金额作为总金额 Reason = input.CancelReason }; @@ -3642,7 +3687,7 @@ public class UnifiedRegistrationService : ITransient } await CreateRefundRecordAsync(registration, eventInfo, wechatConfig, outRefundNo, - refundResult.RefundId, actualRefundAmount, registration.TotalAmount, input.CancelReason, operatorUserId); + refundResult.RefundId, actualRefundAmount, actualPaidAmount, input.CancelReason, operatorUserId); _logger.LogInformation("统一退款成功:报名ID={RegistrationId}, 退款单号={RefundNo}, 退款金额={Amount}", registration.Id, outRefundNo, actualRefundAmount);