退款配置

This commit is contained in:
马梓彭 2026-04-17 12:47:13 +08:00
parent c9eb639123
commit 349bd553df
5 changed files with 166 additions and 35 deletions

View File

@ -174,6 +174,16 @@ public class CancelPreviewOutput
/// </summary>
public decimal RefundableAmount { get; set; }
/// <summary>
/// 退款规则列表
/// </summary>
public List<RefundRuleDto> RefundRules { get; set; } = new();
/// <summary>
/// 当前适用的退款规则描述
/// </summary>
public string? ApplicableRefundRule { get; set; }
/// <summary>
/// 项目列表
/// </summary>
@ -185,6 +195,32 @@ public class CancelPreviewOutput
public List<CancelPreviewMemberDto> Members { get; set; } = new();
}
/// <summary>
/// 退款规则DTO
/// </summary>
public class RefundRuleDto
{
/// <summary>
/// 距离比赛开始天数(起)
/// </summary>
public int DayStart { get; set; }
/// <summary>
/// 距离比赛开始天数(止)
/// </summary>
public int DayEnd { get; set; }
/// <summary>
/// 扣除比例(%)
/// </summary>
public decimal DeductPercentage { get; set; }
/// <summary>
/// 退款备注
/// </summary>
public string? Remark { get; set; }
}
/// <summary>
/// 取消报名项目DTO
/// </summary>

View File

@ -150,7 +150,7 @@ public class CoachLeaderListOutput
/// <summary>
/// 性别
/// </summary>
public string Gender { get; set; }
public int Gender { get; set; }
/// <summary>
/// 出生日期
@ -160,7 +160,17 @@ public class CoachLeaderListOutput
/// <summary>
/// 身份证号
/// </summary>
public string IdCardNumber { get; set; }
public string? IdCardNumber { get; set; }
/// <summary>
/// 身份证正面图片URL
/// </summary>
public string? IdCardImageUrl { get; set; }
/// <summary>
/// 身份证背面图片URL
/// </summary>
public string? IdCardBackImageUrl { get; set; }
/// <summary>
/// 电话

View File

@ -221,7 +221,11 @@ public class EventScoreService : BaseService<EventResult>, ITransient
Id = p.Id,
Name = p.Name,
Age = p.Age,
IdCardNumber = p.IdCardImageUrl,
Gender = (int)p.Gender,
BirthDate = p.BirthDate,
IdCardNumber = p.IdCardNumber,
IdCardImageUrl = p.IdCardImageUrl,
IdCardBackImageUrl = p.IdCardBackImageUrl,
Mobile = p.Mobile,
CertificateId = 0 // 需要单独查询,此处先设为0
})

View File

@ -2445,6 +2445,38 @@ public class UnifiedRegistrationService : ITransient
});
}
var refundConfigs = await _refundConfigService.GetList();
refundConfigs = refundConfigs.Where(x => x.Status == Core.Enum.AvailabilityStatus.Enable).OrderBy(x => x.DayStart).ToList();
foreach (var config in refundConfigs)
{
output.RefundRules.Add(new RefundRuleDto
{
DayStart = config.DayStart,
DayEnd = config.DayEnd,
DeductPercentage = config.DeductPercentage,
Remark = config.Remark
});
}
var daysRemaining = (eventInfo.MatchStartTime - DateTime.Now).TotalDays;
if (daysRemaining < 0) daysRemaining = 0;
var matchConfig = refundConfigs.FirstOrDefault(c =>
daysRemaining >= c.DayStart &&
(c.DayEnd == int.MaxValue ? true : daysRemaining < c.DayEnd)
);
if (matchConfig != null)
{
var refundPercentage = 100 - matchConfig.DeductPercentage;
output.ApplicableRefundRule = $"距离比赛开始还有 {Math.Round(daysRemaining, 1)} 天,退款比例为 {refundPercentage}%,扣除 {matchConfig.DeductPercentage}%。";
}
else
{
output.ApplicableRefundRule = "距离比赛开始还有 " + Math.Round(daysRemaining, 1) + " 天,全额退款。";
}
return output;
}
@ -2544,6 +2576,36 @@ public class UnifiedRegistrationService : ITransient
if (registration.TotalAmount > 0 && !string.IsNullOrEmpty(registration.OrderNo) && actualRefundAmount > 0)
{
var originalOrder = await _orderRepository.GetFirstAsync(o => o.OrderNo == registration.OrderNo);
if (originalOrder != null)
{
var refundOrder = new YD_Event.Core.Entities.Order
{
Id = _idGenerator.NewLong(),
OrderNo = await _orderNoGeneratorService.GenerateRefundOrderNoAsync(),
OrderType = 2,
MerchantId = originalOrder.MerchantId,
MerchantCertificationId = originalOrder.MerchantCertificationId,
EventId = originalOrder.EventId,
RegistrationId = originalOrder.RegistrationId,
RelatedOrderId = originalOrder.Id,
OriginalAmount = originalOrder.ActualAmount,
ActualAmount = actualRefundAmount,
DiscountAmount = 0,
OrderStatus = OrderStatus.Refunded,
PaymentTime = now,
Remark = input.CancelReason,
CreatedTime = now,
CreatedUserId = operatorUserId,
Status = AvailabilityStatus.Enable
};
await _orderRepository.InsertAsync(refundOrder);
output.RefundNo = refundOrder.OrderNo;
_logger.LogInformation("退款订单创建成功:报名ID={RegistrationId}, 原订单号={OriginalOrderNo}, 退款订单号={RefundOrderNo}, 退款金额={Amount}",
registration.Id, originalOrder.OrderNo, refundOrder.OrderNo, actualRefundAmount);
}
var merchant = await _merchantRepository.GetFirstAsync(x => x.Id == eventInfo.MerchantId);
if (merchant != null)
@ -2553,7 +2615,10 @@ public class UnifiedRegistrationService : ITransient
if (wechatConfig != null)
{
var outRefundNo = await _orderNoGeneratorService.GenerateRefundOrderNoAsync();
output.RefundNo = outRefundNo;
if (string.IsNullOrEmpty(output.RefundNo))
{
output.RefundNo = outRefundNo;
}
var refundRequest = new Core.Infrastructure.WeChat.Dto.CreateRefundRequest
{
@ -2574,8 +2639,6 @@ public class UnifiedRegistrationService : ITransient
registration.Id, refundResult.Message);
output.Success = false;
output.Message = refundResult.Message;
output.RefundAmount = 0;
output.RefundNo = null;
return output;
}

View File

@ -4920,6 +4920,16 @@
可退款金额
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.CancelPreviewOutput.RefundRules">
<summary>
退款规则列表
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.CancelPreviewOutput.ApplicableRefundRule">
<summary>
当前适用的退款规则描述
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.CancelPreviewOutput.Projects">
<summary>
项目列表
@ -4930,6 +4940,31 @@
队员列表
</summary>
</member>
<member name="T:YD_Event.Application.Event.Dtos.RefundRuleDto">
<summary>
退款规则DTO
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.RefundRuleDto.DayStart">
<summary>
距离比赛开始天数(起)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.RefundRuleDto.DayEnd">
<summary>
距离比赛开始天数(止)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.RefundRuleDto.DeductPercentage">
<summary>
扣除比例(%)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.RefundRuleDto.Remark">
<summary>
退款备注
</summary>
</member>
<member name="T:YD_Event.Application.Event.Dtos.CancelPreviewProjectDto">
<summary>
取消报名项目DTO
@ -5865,6 +5900,16 @@
身份证号
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.CoachLeaderListOutput.IdCardImageUrl">
<summary>
身份证正面图片URL
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.CoachLeaderListOutput.IdCardBackImageUrl">
<summary>
身份证背面图片URL
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.CoachLeaderListOutput.Mobile">
<summary>
电话
@ -5897,7 +5942,7 @@
</member>
<member name="P:YD_Event.Application.Event.Dtos.IssueCertificateInput.PageType">
<summary>
页面类型:0-未指定,1-运动员页面,2-教练页面,3-领队页面
页面类型:1-运动员页面,2-教练页面,3-领队页面
用于明确区分不同角色页面的证书生成逻辑
</summary>
</member>
@ -6758,27 +6803,7 @@
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserGroupMemberInput.MemberIds">
<summary>
成员ID列表(单个团队报名时使用,向后兼容)
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserGroupMemberInput.Teams">
<summary>
团队列表(支持多个团队报名同一项目组别)
</summary>
</member>
<member name="T:YD_Event.Application.Event.Dtos.UserTeamInput">
<summary>
用户团队输入DTO
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserTeamInput.TeamIndex">
<summary>
团队序号(用于显示,如"团队一"、"团队二")
</summary>
</member>
<member name="P:YD_Event.Application.Event.Dtos.UserTeamInput.MemberIds">
<summary>
该团队的成员ID列表
成员ID列表
</summary>
</member>
<member name="T:YD_Event.Application.Event.Dtos.UserOrderPreviewOutput">
@ -7775,13 +7800,6 @@
发放证书(支持模板选择和批量发放)
</summary>
</member>
<member name="M:YD_Event.Application.Event.EventScoreService.IsValidPageType(System.Int32)">
<summary>
验证 PageType 参数是否有效
</summary>
<param name="pageType">页面类型值</param>
<returns>是否有效</returns>
</member>
<member name="M:YD_Event.Application.Event.EventScoreService.GetAllowedPersonTypes(System.Int32)">
<summary>
根据页面类型获取允许的人员类型列表