diff --git a/YD_Event.Application/Event/Dtos/EventRecordDtos.cs b/YD_Event.Application/Event/Dtos/EventRecordDtos.cs index 2ceebcb..a824947 100644 --- a/YD_Event.Application/Event/Dtos/EventRecordDtos.cs +++ b/YD_Event.Application/Event/Dtos/EventRecordDtos.cs @@ -225,6 +225,11 @@ public class CancelPreviewOutput /// public decimal RefundableAmount { get; set; } + /// + /// 历史已退款金额 + /// + public decimal RefundedAmount { get; set; } + /// /// 当前选择的退款金额(动态计算) /// diff --git a/YD_Event.Application/Event/EventArrangementService.cs b/YD_Event.Application/Event/EventArrangementService.cs index bde6ea7..b46eb3b 100644 --- a/YD_Event.Application/Event/EventArrangementService.cs +++ b/YD_Event.Application/Event/EventArrangementService.cs @@ -319,29 +319,59 @@ namespace YD_Event.Application.Event; // 计算项目总时长(在查询前计算,避免在SQL中使用C#方法) int projTotalDuration = proj.GetTotalDurationInSeconds(); - // 获取参赛人员,并关联用户和单位认证信息 - var persons = await _personRepository.AsQueryable() + // 第一步:先查询主要数据和相关的 MerchantCertificationId + var basicPersons = await _personRepository.AsQueryable() .InnerJoin((p, r) => p.RegistrationId == r.Id) - .LeftJoin((p, r, m) => r.MerchantCertificationId == m.Id) - .Where((p, r, m) => p.ProjectId == eventProj.Id + .Where((p, r) => p.ProjectId == eventProj.Id && r.PaymentStatus != 4 && !p.DeleteMark && p.PersonType == PersonType.Athlete) .OrderBy(p => p.GroupName) .OrderBy(p => p.Id) - .Select((p, r, m) => new { - Id = p.Id, // 添加 Id 属性 + .Select((p, r) => new { + Id = p.Id, RegistrationItemId = p.RegistrationItemId, p.UserId, p.Name, p.GroupName, r.TeamName, - MerchantName = m.MerchantName ?? "", - m.AuditStatus, + r.MerchantCertificationId, Duration = projTotalDuration }) .ToListAsync(); + // 第二步:收集所有需要查询的 MerchantCertificationId + var merchantCertIds = basicPersons + .Where(x => x.MerchantCertificationId.HasValue) + .Select(x => x.MerchantCertificationId.Value) + .Distinct() + .ToList(); + + // 第三步:批量查询 MerchantCertification + Dictionary merchantDict = new Dictionary(); + if (merchantCertIds.Any()) + { + var merchants = await _merchantCertificationRepository.GetListAsync(x => merchantCertIds.Contains(x.Id)); + merchantDict = merchants.ToDictionary(x => x.Id, x => x); + } + + // 第四步:在内存中合并数据 + var persons = basicPersons.Select(bp => new { + bp.Id, + bp.RegistrationItemId, + bp.UserId, + bp.Name, + bp.GroupName, + bp.TeamName, + MerchantName = bp.MerchantCertificationId.HasValue && merchantDict.ContainsKey(bp.MerchantCertificationId.Value) + ? merchantDict[bp.MerchantCertificationId.Value].MerchantName + : null, + AuditStatus = bp.MerchantCertificationId.HasValue && merchantDict.ContainsKey(bp.MerchantCertificationId.Value) + ? merchantDict[bp.MerchantCertificationId.Value].AuditStatus + : (AuditStatus?)null, + bp.Duration + }).ToList(); + if (!persons.Any()) continue; // 计算项目总时长 @@ -471,7 +501,14 @@ namespace YD_Event.Application.Event; var teamNameProp = personData.GetType().GetProperty("TeamName"); string? merchantName = merchantNameProp?.GetValue(personData) as string; - AuditStatus? auditStatus = auditStatusProp?.GetValue(personData) as AuditStatus?; + var auditStatusValue = auditStatusProp?.GetValue(personData); + AuditStatus? auditStatus = null; + + if (auditStatusValue != null && auditStatusValue is AuditStatus status) + { + auditStatus = status; + } + string? teamName = teamNameProp?.GetValue(personData) as string; // 优先使用认证通过的单位名称 diff --git a/YD_Event.Application/Event/UnifiedRegistrationService.cs b/YD_Event.Application/Event/UnifiedRegistrationService.cs index 3187add..c34dca5 100644 --- a/YD_Event.Application/Event/UnifiedRegistrationService.cs +++ b/YD_Event.Application/Event/UnifiedRegistrationService.cs @@ -2468,10 +2468,21 @@ public class UnifiedRegistrationService : ITransient var items = await _db.Queryable().Where(x => x.RegistrationId == registrationId).ToListAsync(); var persons = await _registrationPersonRepository.GetListAsync(x => x.RegistrationId == registrationId); - var refundableAmount = await CalculateRefundAmountAsync(registration.TotalAmount, eventInfo.MatchStartTime); + // 查询历史已退款金额 + var historyRefunds = await _refundRecordRepository.GetListAsync(x => x.RegistrationId == registrationId && x.RefundStatus == 1); + var refundedAmount = historyRefunds.Sum(x => x.RefundAmount); + + // 计算剩余未退款的原始金额 + var remainingOriginalAmount = registration.TotalAmount - refundedAmount; + + // 基于剩余金额计算可退款金额 + var refundableAmount = await CalculateRefundAmountAsync(remainingOriginalAmount, eventInfo.MatchStartTime); + + // 确保可退款金额不小于0 + refundableAmount = Math.Max(0, refundableAmount); - _logger.LogInformation("统一取消报名预览:报名ID={RegistrationId}, 赛事名称={EventName}, 实际支付={ActualAmount}, 可退款={RefundAmount}, 支付方式={PaymentMethod}", - registrationId, eventInfo?.EventName, registration.TotalAmount, refundableAmount, registration.PaymentMethod); + _logger.LogInformation("统一取消报名预览:报名ID={RegistrationId}, 赛事名称={EventName}, 实际支付={ActualAmount}, 已退款={RefundedAmount}, 剩余可退款={RefundAmount}, 支付方式={PaymentMethod}", + registrationId, eventInfo?.EventName, registration.TotalAmount, refundedAmount, refundableAmount, registration.PaymentMethod); // 获取组别信息(年龄和性别限制) var groupIds = items.Where(x => x.EventProjectGroupId.HasValue).Select(x => x.EventProjectGroupId.Value).Distinct().ToList(); @@ -2501,6 +2512,7 @@ public class UnifiedRegistrationService : ITransient ChargeMode = (int)eventInfo.ChargeMode, ActualPaidAmount = registration.TotalAmount, RefundableAmount = refundableAmount, + RefundedAmount = refundedAmount, CurrentRefundAmount = refundableAmount, PaymentMethod = registration.PaymentMethod, PaymentStatus = registration.PaymentStatus, @@ -2669,15 +2681,28 @@ public class UnifiedRegistrationService : ITransient return 0; } + // 查询历史已退款金额 + var historyRefunds = await _refundRecordRepository.GetListAsync(x => x.RegistrationId == registration.Id && x.RefundStatus == 1); + var refundedAmount = historyRefunds.Sum(x => x.RefundAmount); + + // 计算剩余的原始未退款金额 + var remainingOriginalAmount = registration.TotalAmount - refundedAmount; + + // 如果没有剩余可退,直接返回0 + if (remainingOriginalAmount <= 0) + { + return 0; + } + // 全部取消 if (input.CancelType == 0) { - return await CalculateRefundAmountAsync(registration.TotalAmount, eventInfo.MatchStartTime); + return await CalculateRefundAmountAsync(remainingOriginalAmount, eventInfo.MatchStartTime); } // 部分取消 - var baseRefundAmount = await CalculateRefundAmountAsync(registration.TotalAmount, eventInfo.MatchStartTime); - var refundRatio = registration.TotalAmount > 0 ? baseRefundAmount / registration.TotalAmount : 1; + var baseRefundAmount = await CalculateRefundAmountAsync(remainingOriginalAmount, eventInfo.MatchStartTime); + var refundRatio = remainingOriginalAmount > 0 ? baseRefundAmount / remainingOriginalAmount : 1; var cancelMemberIds = input.PersonIds ?? new List(); var cancelTeams = input.CancelTeams ?? new List(); @@ -2712,7 +2737,9 @@ public class UnifiedRegistrationService : ITransient return baseRefundAmount; } // 否则,按人数比例退款 - return baseRefundAmount * (decimal)cancelTeamMemberCount / persons.Count; + var fixedPriceCalculated = baseRefundAmount * (decimal)cancelTeamMemberCount / persons.Count; + // 确保不超过剩余可退款金额 + return Math.Min(fixedPriceCalculated, baseRefundAmount); case ChargeMode.UnitPriceEvent: // 单价(赛事):按取消人数计算 @@ -2732,8 +2759,10 @@ public class UnifiedRegistrationService : ITransient } var cancelCount = allCancelMemberIds.Count; - var unitPrice = persons.Count > 0 ? registration.TotalAmount / persons.Count : 0; - return unitPrice * cancelCount * refundRatio; + var unitPrice = persons.Count > 0 ? remainingOriginalAmount / persons.Count : 0; + var calculatedAmount = unitPrice * cancelCount * refundRatio; + // 确保不超过剩余可退款金额 + return Math.Min(calculatedAmount, baseRefundAmount); case ChargeMode.UnitPriceProject: // 单价(项目):按每个项目的人数计算 @@ -2766,7 +2795,9 @@ public class UnifiedRegistrationService : ITransient } } - return totalCancelAmount * refundRatio; + var projectCalculatedAmount = totalCancelAmount * refundRatio; + // 确保不超过剩余可退款金额 + return Math.Min(projectCalculatedAmount, baseRefundAmount); default: return 0; diff --git a/YD_Event.Application/YD_Event.Application.xml b/YD_Event.Application/YD_Event.Application.xml index fc3bd45..7a1ba40 100644 --- a/YD_Event.Application/YD_Event.Application.xml +++ b/YD_Event.Application/YD_Event.Application.xml @@ -5034,6 +5034,11 @@ 可退款金额(全部取消时) + + + 历史已退款金额 + + 当前选择的退款金额(动态计算) diff --git a/YD_Event.Application/applicationsettings.json b/YD_Event.Application/applicationsettings.json index d2c81fb..8c3cdc6 100644 --- a/YD_Event.Application/applicationsettings.json +++ b/YD_Event.Application/applicationsettings.json @@ -53,7 +53,7 @@ "SleepMs": 300, "dbconfig": { "ConnectionStrings": [ - "8.153.108.90,Password=yd708090,SyncTimeout=15000,ConnectTimeout=15000,ResponseTimeout=15000,defaultDatabase=7,poolsize=100,ssl=false,writeBuffer=10240,idleTimeout=30000,connectRetry=3,asyncPipeline=false", + "8.153.108.90,Password=yd708090,SyncTimeout=15000,ConnectTimeout=15000,ResponseTimeout=15000,defaultDatabase=7,poolsize=100,ssl=false,writeBuffer=10240,idleTimeout=30000,connectRetry=3,asyncPipeline=false" //"8.153.108.90,Password=yd708090,SyncTimeout=15000,ConnectTimeout=15000,ResponseTimeout=15000,defaultDatabase=8,poolsize=100,ssl=false,writeBuffer=10240,idleTimeout=30000,connectRetry=3,asyncPipeline=false" ], //"Sentinels": [