2026-03-02 09:54:52 +08:00
|
|
|
using YD_Event.Application.Refund.Dtos;
|
|
|
|
|
using YD_Event.Core.Entities;
|
|
|
|
|
|
2026-05-15 13:46:58 +08:00
|
|
|
using YD_Event.Application.Event;
|
|
|
|
|
|
2026-03-02 09:54:52 +08:00
|
|
|
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;
|
2026-05-15 13:46:58 +08:00
|
|
|
private readonly EventRuleSnapshotService _ruleSnapshotService;
|
2026-03-02 09:54:52 +08:00
|
|
|
private const string CacheKey = "RefundConfig:List";
|
|
|
|
|
|
|
|
|
|
public RefundConfigService(ISqlSugarRepository<RefundConfig> repository,
|
|
|
|
|
IIdGenerator idGenerator,
|
2026-05-15 13:46:58 +08:00
|
|
|
IEasyCachingProvider easyCachingProvider,
|
|
|
|
|
EventRuleSnapshotService ruleSnapshotService) : base(repository)
|
2026-03-02 09:54:52 +08:00
|
|
|
{
|
|
|
|
|
_repository = repository;
|
|
|
|
|
_idGenerator = idGenerator;
|
|
|
|
|
_easyCachingProvider = easyCachingProvider;
|
2026-05-15 13:46:58 +08:00
|
|
|
_ruleSnapshotService = ruleSnapshotService;
|
2026-03-02 09:54:52 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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)
|
|
|
|
|
{
|
2026-06-06 23:34:12 +08:00
|
|
|
await _repository.SoftDeleteByIdsAsync(ids);
|
2026-03-02 09:54:52 +08:00
|
|
|
|
|
|
|
|
// 清除缓存
|
|
|
|
|
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;
|
|
|
|
|
}
|
2026-05-15 13:46:58 +08:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 根据赛事快照计算退款金额
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="totalAmount">总金额</param>
|
|
|
|
|
/// <param name="eventStartTime">赛事开始时间</param>
|
|
|
|
|
/// <param name="eventId">赛事ID</param>
|
|
|
|
|
/// <returns>应退金额</returns>
|
|
|
|
|
[NonAction]
|
|
|
|
|
public async Task<decimal> CalculateRefundAmountByEvent(decimal totalAmount, DateTime eventStartTime, long eventId)
|
|
|
|
|
{
|
|
|
|
|
// 从快照获取退款规则
|
|
|
|
|
var snapshots = await _ruleSnapshotService.GetRefundRuleSnapshot(eventId);
|
|
|
|
|
|
|
|
|
|
if (!snapshots.Any()) return totalAmount;
|
|
|
|
|
|
|
|
|
|
var daysRemaining = (eventStartTime - DateTime.Now).TotalDays;
|
|
|
|
|
if (daysRemaining < 0) daysRemaining = 0;
|
|
|
|
|
|
|
|
|
|
// 查找匹配的规则
|
|
|
|
|
var matchConfig = snapshots.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;
|
|
|
|
|
}
|
2026-03-02 09:54:52 +08:00
|
|
|
}
|