重复报名

This commit is contained in:
马梓彭 2026-05-11 15:30:05 +08:00
parent 12562f962c
commit e03ad5eb46
2 changed files with 67 additions and 22 deletions

View File

@ -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();
// 计算退款金额和获取退款规则

View File

@ -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);