退款配置完善

This commit is contained in:
马梓彭 2026-03-02 09:54:52 +08:00
parent 2d433aed38
commit 83e4fef459
10 changed files with 367 additions and 159 deletions

View File

@ -2,6 +2,7 @@ using System.Text;
using YD_Event.Application.Order.Dtos;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using YD_Event.Application.Refund;
namespace YD_Event.Application.Order;
@ -14,18 +15,24 @@ public class OrderService : BaseService<EventRegistration>, ITransient
private readonly ISqlSugarRepository<EventRegistrationItem> _itemRepository;
private readonly ISqlSugarRepository<EventRegistrationPerson> _personRepository;
private readonly ISqlSugarRepository<EventRegistrationPayment> _paymentRepository;
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
private readonly RefundConfigService _refundConfigService;
private readonly IIdGenerator _idGenerator;
public OrderService(ISqlSugarRepository<EventRegistration> repository,
ISqlSugarRepository<EventRegistrationItem> itemRepository,
ISqlSugarRepository<EventRegistrationPerson> personRepository,
ISqlSugarRepository<EventRegistrationPayment> paymentRepository,
ISqlSugarRepository<EventInfo> eventRepository,
RefundConfigService refundConfigService,
IIdGenerator idGenerator) : base(repository)
{
_repository = repository;
_itemRepository = itemRepository;
_personRepository = personRepository;
_paymentRepository = paymentRepository;
_eventRepository = eventRepository;
_refundConfigService = refundConfigService;
_idGenerator = idGenerator;
}
@ -142,6 +149,22 @@ public class OrderService : BaseService<EventRegistration>, ITransient
await _repository.UpdateAsync(entity);
}
/// <summary>
/// 获取建议退款金额
/// </summary>
[DisplayName("获取建议退款金额")]
[HttpGet("refund-amount")]
public async Task<decimal> GetRefundAmount([FromQuery] long orderId)
{
var order = await _repository.GetByIdAsync(orderId);
if (order == null) throw Oops.Bah("订单不存在");
var eventInfo = await _eventRepository.GetByIdAsync(order.EventId);
if (eventInfo == null) return order.TotalAmount; // 无法获取赛事时间,默认全额
return await _refundConfigService.CalculateRefundAmount(order.TotalAmount, eventInfo.MatchStartTime);
}
/// <summary>
/// 订单退款
/// </summary>

View File

@ -0,0 +1,32 @@
using System.ComponentModel.DataAnnotations;
using YD_Event.Core.Enum;
namespace YD_Event.Application.Refund.Dtos;
public class RefundConfigDto
{
public long Id { get; set; }
public int DayStart { get; set; }
public int DayEnd { get; set; }
public decimal DeductPercentage { get; set; }
public string Remark { get; set; }
public AvailabilityStatus Status { get; set; }
}
public class RefundConfigInput
{
public long? Id { get; set; }
[Required]
public int DayStart { get; set; }
[Required]
public int DayEnd { get; set; }
[Required]
public decimal DeductPercentage { get; set; }
public string Remark { get; set; }
public AvailabilityStatus Status { get; set; }
}
public class RefundConfigQueryInput : Pagination
{
public AvailabilityStatus? Status { get; set; }
}

View File

@ -0,0 +1,175 @@
using YD_Event.Application.Refund.Dtos;
using YD_Event.Core.Entities;
namespace YD_Event.Application.Refund;
/// <summary>
/// 退款配置服务
/// </summary>
public class RefundConfigService : BaseService<RefundConfig>, ITransient
{
private readonly ISqlSugarRepository<RefundConfig> _repository;
private readonly IIdGenerator _idGenerator;
private readonly IEasyCachingProvider _easyCachingProvider;
private const string CacheKey = "RefundConfig:List";
public RefundConfigService(ISqlSugarRepository<RefundConfig> repository,
IIdGenerator idGenerator,
IEasyCachingProvider easyCachingProvider) : base(repository)
{
_repository = repository;
_idGenerator = idGenerator;
_easyCachingProvider = easyCachingProvider;
}
/// <summary>
/// 获取所有退款配置
/// </summary>
/// <returns></returns>
[DisplayName("获取所有退款配置")]
[HttpGet("list")]
public async Task<List<RefundConfigDto>> GetList()
{
// 优先从缓存获取
var cacheValue = await _easyCachingProvider.GetAsync<List<RefundConfigDto>>(CacheKey);
if (cacheValue.HasValue)
{
return cacheValue.Value;
}
var list = await _repository.GetListAsync();
var dtos = list.OrderBy(x => x.DayStart).Adapt<List<RefundConfigDto>>();
// 写入缓存,过期时间 1 小时
await _easyCachingProvider.SetAsync(CacheKey, dtos, TimeSpan.FromHours(1));
return dtos;
}
/// <summary>
/// 分页查询退款配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("分页查询退款配置")]
[HttpGet("page")]
public async Task<PageResult<RefundConfigDto>> Page([FromQuery] RefundConfigQueryInput input)
{
return await _repository.AsQueryable()
.WhereIF(input.Status.HasValue, x => x.Status == input.Status)
.OrderBy(x => x.DayStart)
.Select(x => new RefundConfigDto
{
Id = x.Id,
DayStart = x.DayStart,
DayEnd = x.DayEnd,
DeductPercentage = x.DeductPercentage,
Remark = x.Remark,
Status = x.Status
})
.ToPagedListAsync(input);
}
/// <summary>
/// 添加退款配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("添加退款配置")]
[HttpPost("add")]
public async Task Add(RefundConfigInput input)
{
// 简单重叠校验
var isExist = await _repository.IsAnyAsync(x =>
(input.DayStart >= x.DayStart && input.DayStart < x.DayEnd) ||
(input.DayEnd > x.DayStart && input.DayEnd <= x.DayEnd) ||
(input.DayStart <= x.DayStart && input.DayEnd >= x.DayEnd));
if (isExist) throw Oops.Bah("退款规则时间段存在重叠");
var entity = input.Adapt<RefundConfig>();
entity.Id = _idGenerator.NextId();
await _repository.InsertAsync(entity);
// 清除缓存
await _easyCachingProvider.RemoveAsync(CacheKey);
}
/// <summary>
/// 更新退款配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[DisplayName("更新退款配置")]
[HttpPut("update")]
public async Task Update(RefundConfigInput input)
{
if (!input.Id.HasValue) throw Oops.Bah("Id不能为空");
// 校验重叠 (排除自身)
var isExist = await _repository.IsAnyAsync(x =>
x.Id != input.Id &&
((input.DayStart >= x.DayStart && input.DayStart < x.DayEnd) ||
(input.DayEnd > x.DayStart && input.DayEnd <= x.DayEnd) ||
(input.DayStart <= x.DayStart && input.DayEnd >= x.DayEnd)));
if (isExist) throw Oops.Bah("退款规则时间段存在重叠");
var entity = await _repository.GetByIdAsync(input.Id.Value);
if (entity == null) throw Oops.Bah("配置不存在");
input.Adapt(entity);
await _repository.UpdateAsync(entity);
// 清除缓存
await _easyCachingProvider.RemoveAsync(CacheKey);
}
/// <summary>
/// 删除退款配置
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
[DisplayName("删除退款配置")]
[HttpPost("delete")]
public async Task Delete([FromBody] List<long> ids)
{
await _repository.DeleteAsync(x => ids.Contains(x.Id));
// 清除缓存
await _easyCachingProvider.RemoveAsync(CacheKey);
}
/// <summary>
/// 计算退款金额
/// </summary>
/// <param name="totalAmount">总金额</param>
/// <param name="eventStartTime">赛事开始时间</param>
/// <returns>应退金额</returns>
[NonAction]
public async Task<decimal> CalculateRefundAmount(decimal totalAmount, DateTime eventStartTime)
{
// 优先使用 GetList (带缓存)
var configs = await GetList();
configs = configs.Where(x => x.Status == Core.Enum.AvailabilityStatus.Enable).ToList();
if (!configs.Any()) return totalAmount;
var daysRemaining = (eventStartTime - DateTime.Now).TotalDays;
if (daysRemaining < 0) daysRemaining = 0;
// 查找匹配的规则
var matchConfig = configs.FirstOrDefault(c =>
daysRemaining >= c.DayStart &&
(c.DayEnd == int.MaxValue ? true : daysRemaining < c.DayEnd)
);
if (matchConfig != null)
{
var deductAmount = totalAmount * (matchConfig.DeductPercentage / 100m);
return totalAmount - deductAmount;
}
return totalAmount;
}
}

View File

@ -1,38 +0,0 @@
using System.ComponentModel.DataAnnotations;
namespace YD_Event.Application.Setting.Dtos;
/// <summary>
/// 退款配置输入参数
/// </summary>
public class RefundConfigInput
{
/// <summary>
/// 开赛前7天扣除比例(%)
/// </summary>
[Required(ErrorMessage = "请输入开赛前7天扣除比例")]
[Range(0, 100, ErrorMessage = "比例必须在0-100之间")]
public decimal DeductPercentageBefore7Days { get; set; }
/// <summary>
/// 3-7天扣除比例(%)
/// </summary>
[Required(ErrorMessage = "请输入3-7天扣除比例")]
[Range(0, 100, ErrorMessage = "比例必须在0-100之间")]
public decimal DeductPercentage3To7Days { get; set; }
/// <summary>
/// 3天内扣除比例(%)
/// </summary>
[Required(ErrorMessage = "请输入3天内扣除比例")]
[Range(0, 100, ErrorMessage = "比例必须在0-100之间")]
public decimal DeductPercentageWithin3Days { get; set; }
}
/// <summary>
/// 退款配置输出参数
/// </summary>
public class RefundConfigOutput : RefundConfigInput
{
public DateTime? UpdatedTime { get; set; }
}

View File

@ -1,59 +0,0 @@
using YD_Event.Application.Setting.Dtos;
namespace YD_Event.Application.Setting;
/// <summary>
/// 退款配置服务
/// </summary>
public class RefundConfigService : ITransient
{
private readonly ISqlSugarRepository<RefundConfig> _repository;
private readonly IIdGenerator _idGenerator;
public RefundConfigService(ISqlSugarRepository<RefundConfig> repository, IIdGenerator idGenerator)
{
_repository = repository;
_idGenerator = idGenerator;
}
/// <summary>
/// 获取退款配置
/// </summary>
/// <returns></returns>
public async Task<RefundConfigOutput> GetConfig()
{
var config = await _repository.GetFirstAsync(x => true);
if (config == null)
{
// 如果没有配置,返回默认值
return new RefundConfigOutput
{
DeductPercentageBefore7Days = 0,
DeductPercentage3To7Days = 0,
DeductPercentageWithin3Days = 0
};
}
return config.Adapt<RefundConfigOutput>();
}
/// <summary>
/// 更新退款配置
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task UpdateConfig(RefundConfigInput input)
{
var config = await _repository.GetFirstAsync(x => true);
if (config == null)
{
config = input.Adapt<RefundConfig>();
config.Id = _idGenerator.NextId();
await _repository.InsertAsync(config);
}
else
{
input.Adapt(config);
await _repository.UpdateAsync(config);
}
}
}

View File

@ -1782,6 +1782,11 @@
更新订单状态
</summary>
</member>
<member name="M:YD_Event.Application.Order.OrderService.GetRefundAmount(System.Int64)">
<summary>
获取建议退款金额
</summary>
</member>
<member name="M:YD_Event.Application.Order.OrderService.Refund(YD_Event.Application.Order.Dtos.RefundOrderInput)">
<summary>
订单退款
@ -2348,6 +2353,53 @@
删除项目类型
</summary>
</member>
<member name="T:YD_Event.Application.Refund.RefundConfigService">
<summary>
退款配置服务
</summary>
</member>
<member name="M:YD_Event.Application.Refund.RefundConfigService.GetList">
<summary>
获取所有退款配置
</summary>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Refund.RefundConfigService.Page(YD_Event.Application.Refund.Dtos.RefundConfigQueryInput)">
<summary>
分页查询退款配置
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Refund.RefundConfigService.Add(YD_Event.Application.Refund.Dtos.RefundConfigInput)">
<summary>
添加退款配置
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Refund.RefundConfigService.Update(YD_Event.Application.Refund.Dtos.RefundConfigInput)">
<summary>
更新退款配置
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Refund.RefundConfigService.Delete(System.Collections.Generic.List{System.Int64})">
<summary>
删除退款配置
</summary>
<param name="ids"></param>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Refund.RefundConfigService.CalculateRefundAmount(System.Decimal,System.DateTime)">
<summary>
计算退款金额
</summary>
<param name="totalAmount">总金额</param>
<param name="eventStartTime">赛事开始时间</param>
<returns>应退金额</returns>
</member>
<member name="T:YD_Event.Application.Registration.Dtos.RegistrationQueryInput">
<summary>
报名查询输入
@ -2548,49 +2600,6 @@
下载模板
</summary>
</member>
<member name="T:YD_Event.Application.Setting.Dtos.RefundConfigInput">
<summary>
退款配置输入参数
</summary>
</member>
<member name="P:YD_Event.Application.Setting.Dtos.RefundConfigInput.DeductPercentageBefore7Days">
<summary>
开赛前7天扣除比例(%)
</summary>
</member>
<member name="P:YD_Event.Application.Setting.Dtos.RefundConfigInput.DeductPercentage3To7Days">
<summary>
3-7天扣除比例(%)
</summary>
</member>
<member name="P:YD_Event.Application.Setting.Dtos.RefundConfigInput.DeductPercentageWithin3Days">
<summary>
3天内扣除比例(%)
</summary>
</member>
<member name="T:YD_Event.Application.Setting.Dtos.RefundConfigOutput">
<summary>
退款配置输出参数
</summary>
</member>
<member name="T:YD_Event.Application.Setting.RefundConfigService">
<summary>
退款配置服务
</summary>
</member>
<member name="M:YD_Event.Application.Setting.RefundConfigService.GetConfig">
<summary>
获取退款配置
</summary>
<returns></returns>
</member>
<member name="M:YD_Event.Application.Setting.RefundConfigService.UpdateConfig(YD_Event.Application.Setting.Dtos.RefundConfigInput)">
<summary>
更新退款配置
</summary>
<param name="input"></param>
<returns></returns>
</member>
<member name="P:YD_Event.Application.User.Dtos.AddSysUserInput.Name">
<summary>
姓名

View File

@ -1,5 +1,6 @@
using SqlSugar;
using System.ComponentModel.DataAnnotations;
using YD_Event.Core.Enum;
namespace YD_Event.Core.Entities;
@ -7,32 +8,37 @@ namespace YD_Event.Core.Entities;
/// 退款配置
/// </summary>
[SugarTable("RefundConfig")]
public class RefundConfig : Entity<long>, ISoftDelete, ICreatedUserId, ICreatedTime, IUpdatedUserId, IUpdatedTime
public class RefundConfig : Entity<long>, ISoftDelete, ICreatedUserId, ICreatedTime, IUpdatedUserId, IUpdatedTime, IAvailability
{
/// <summary>
/// 开赛前7天扣除比例(%)
/// 天数起始值
/// </summary>
[SugarColumn(ColumnDescription = "开赛前7天扣除比例", DecimalDigits = 2)]
public decimal DeductPercentageBefore7Days { get; set; }
[SugarColumn(ColumnDescription = "天数起始值")]
public int DayStart { get; set; }
/// <summary>
/// 3-7天扣除比例(%)
/// 天数结束值
/// </summary>
[SugarColumn(ColumnDescription = "3-7天扣除比例", DecimalDigits = 2)]
public decimal DeductPercentage3To7Days { get; set; }
[SugarColumn(ColumnDescription = "天数结束值")]
public int DayEnd { get; set; }
/// <summary>
/// 3天内扣除比例(%)
/// 扣除比例(%)
/// </summary>
[SugarColumn(ColumnDescription = "3天内扣除比例", DecimalDigits = 2)]
public decimal DeductPercentageWithin3Days { get; set; }
[SugarColumn(ColumnDescription = "扣除比例", DecimalDigits = 2)]
public decimal DeductPercentage { get; set; }
/// <summary>
/// 备注
/// 退款备注
/// </summary>
[SugarColumn(ColumnDescription = "备注", Length = 256, IsNullable = true)]
[SugarColumn(ColumnDescription = "退款备注", Length = 256, IsNullable = true)]
public string Remark { get; set; }
/// <summary>
/// 可用状态
/// </summary>
public AvailabilityStatus Status { get; set; }
/// <summary>
/// 标记删除
/// </summary>
@ -43,6 +49,9 @@ public class RefundConfig : Entity<long>, ISoftDelete, ICreatedUserId, ICreatedT
/// </summary>
public long CreatedUserId { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; }
/// <summary>
@ -50,5 +59,8 @@ public class RefundConfig : Entity<long>, ISoftDelete, ICreatedUserId, ICreatedT
/// </summary>
public long? UpdatedUserId { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public DateTime? UpdatedTime { get; set; }
}

View File

@ -0,0 +1,35 @@
[
{
"Id": 1000,
"DayStart": 7,
"DayEnd": 999,
"DeductPercentage": 0,
"Remark": "开赛前7天",
"Status": 1,
"DeleteMark": false,
"CreatedUserId": 0,
"CreatedTime": "2023-01-01T00:00:00"
},
{
"Id": 1001,
"DayStart": 3,
"DayEnd": 7,
"DeductPercentage": 0,
"Remark": "3-7天",
"Status": 1,
"DeleteMark": false,
"CreatedUserId": 0,
"CreatedTime": "2023-01-01T00:00:00"
},
{
"Id": 1002,
"DayStart": 0,
"DayEnd": 3,
"DeductPercentage": 0,
"Remark": "3天内",
"Status": 1,
"DeleteMark": false,
"CreatedUserId": 0,
"CreatedTime": "2023-01-01T00:00:00"
}
]

View File

@ -39,6 +39,10 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="InitData\refundconfig.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="InitData\sysmenu.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@ -2525,24 +2525,29 @@
退款配置
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.DeductPercentageBefore7Days">
<member name="P:YD_Event.Core.Entities.RefundConfig.DayStart">
<summary>
开赛前7天扣除比例(%)
天数起始值
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.DeductPercentage3To7Days">
<member name="P:YD_Event.Core.Entities.RefundConfig.DayEnd">
<summary>
3-7天扣除比例(%)
天数结束值
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.DeductPercentageWithin3Days">
<member name="P:YD_Event.Core.Entities.RefundConfig.DeductPercentage">
<summary>
3天内扣除比例(%)
扣除比例(%)
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.Remark">
<summary>
备注
退款备注
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.Status">
<summary>
可用状态
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.DeleteMark">
@ -2555,11 +2560,21 @@
创建人
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.CreatedTime">
<summary>
创建时间
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.UpdatedUserId">
<summary>
更新人
</summary>
</member>
<member name="P:YD_Event.Core.Entities.RefundConfig.UpdatedTime">
<summary>
更新时间
</summary>
</member>
<member name="T:YD_Event.Core.Entities.SysMenu">
<summary>
系统菜单表