diff --git a/YD_Event.Application/Order/OrderService.cs b/YD_Event.Application/Order/OrderService.cs index 400fe48..59769b2 100644 --- a/YD_Event.Application/Order/OrderService.cs +++ b/YD_Event.Application/Order/OrderService.cs @@ -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, ITransient private readonly ISqlSugarRepository _itemRepository; private readonly ISqlSugarRepository _personRepository; private readonly ISqlSugarRepository _paymentRepository; + private readonly ISqlSugarRepository _eventRepository; + private readonly RefundConfigService _refundConfigService; private readonly IIdGenerator _idGenerator; public OrderService(ISqlSugarRepository repository, ISqlSugarRepository itemRepository, ISqlSugarRepository personRepository, ISqlSugarRepository paymentRepository, + ISqlSugarRepository 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, ITransient await _repository.UpdateAsync(entity); } + /// + /// 获取建议退款金额 + /// + [DisplayName("获取建议退款金额")] + [HttpGet("refund-amount")] + public async Task 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); + } + /// /// 订单退款 /// diff --git a/YD_Event.Application/Refund/Dtos/RefundConfigDtos.cs b/YD_Event.Application/Refund/Dtos/RefundConfigDtos.cs new file mode 100644 index 0000000..86eabd4 --- /dev/null +++ b/YD_Event.Application/Refund/Dtos/RefundConfigDtos.cs @@ -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; } +} diff --git a/YD_Event.Application/Refund/RefundConfigService.cs b/YD_Event.Application/Refund/RefundConfigService.cs new file mode 100644 index 0000000..bcad5a5 --- /dev/null +++ b/YD_Event.Application/Refund/RefundConfigService.cs @@ -0,0 +1,175 @@ +using YD_Event.Application.Refund.Dtos; +using YD_Event.Core.Entities; + +namespace YD_Event.Application.Refund; + +/// +/// 退款配置服务 +/// +public class RefundConfigService : BaseService, ITransient +{ + private readonly ISqlSugarRepository _repository; + private readonly IIdGenerator _idGenerator; + private readonly IEasyCachingProvider _easyCachingProvider; + private const string CacheKey = "RefundConfig:List"; + + public RefundConfigService(ISqlSugarRepository repository, + IIdGenerator idGenerator, + IEasyCachingProvider easyCachingProvider) : base(repository) + { + _repository = repository; + _idGenerator = idGenerator; + _easyCachingProvider = easyCachingProvider; + } + + /// + /// 获取所有退款配置 + /// + /// + [DisplayName("获取所有退款配置")] + [HttpGet("list")] + public async Task> GetList() + { + // 优先从缓存获取 + var cacheValue = await _easyCachingProvider.GetAsync>(CacheKey); + if (cacheValue.HasValue) + { + return cacheValue.Value; + } + + var list = await _repository.GetListAsync(); + var dtos = list.OrderBy(x => x.DayStart).Adapt>(); + + // 写入缓存,过期时间 1 小时 + await _easyCachingProvider.SetAsync(CacheKey, dtos, TimeSpan.FromHours(1)); + + return dtos; + } + + /// + /// 分页查询退款配置 + /// + /// + /// + [DisplayName("分页查询退款配置")] + [HttpGet("page")] + public async Task> 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); + } + + /// + /// 添加退款配置 + /// + /// + /// + [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(); + entity.Id = _idGenerator.NextId(); + await _repository.InsertAsync(entity); + + // 清除缓存 + await _easyCachingProvider.RemoveAsync(CacheKey); + } + + /// + /// 更新退款配置 + /// + /// + /// + [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); + } + + /// + /// 删除退款配置 + /// + /// + /// + [DisplayName("删除退款配置")] + [HttpPost("delete")] + public async Task Delete([FromBody] List ids) + { + await _repository.DeleteAsync(x => ids.Contains(x.Id)); + + // 清除缓存 + await _easyCachingProvider.RemoveAsync(CacheKey); + } + + /// + /// 计算退款金额 + /// + /// 总金额 + /// 赛事开始时间 + /// 应退金额 + [NonAction] + public async Task 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; + } +} diff --git a/YD_Event.Application/Setting/Dtos/RefundConfigDtos.cs b/YD_Event.Application/Setting/Dtos/RefundConfigDtos.cs deleted file mode 100644 index 32501ad..0000000 --- a/YD_Event.Application/Setting/Dtos/RefundConfigDtos.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace YD_Event.Application.Setting.Dtos; - -/// -/// 退款配置输入参数 -/// -public class RefundConfigInput -{ - /// - /// 开赛前7天扣除比例(%) - /// - [Required(ErrorMessage = "请输入开赛前7天扣除比例")] - [Range(0, 100, ErrorMessage = "比例必须在0-100之间")] - public decimal DeductPercentageBefore7Days { get; set; } - - /// - /// 3-7天扣除比例(%) - /// - [Required(ErrorMessage = "请输入3-7天扣除比例")] - [Range(0, 100, ErrorMessage = "比例必须在0-100之间")] - public decimal DeductPercentage3To7Days { get; set; } - - /// - /// 3天内扣除比例(%) - /// - [Required(ErrorMessage = "请输入3天内扣除比例")] - [Range(0, 100, ErrorMessage = "比例必须在0-100之间")] - public decimal DeductPercentageWithin3Days { get; set; } -} - -/// -/// 退款配置输出参数 -/// -public class RefundConfigOutput : RefundConfigInput -{ - public DateTime? UpdatedTime { get; set; } -} diff --git a/YD_Event.Application/Setting/RefundConfigService.cs b/YD_Event.Application/Setting/RefundConfigService.cs deleted file mode 100644 index cc3a4af..0000000 --- a/YD_Event.Application/Setting/RefundConfigService.cs +++ /dev/null @@ -1,59 +0,0 @@ -using YD_Event.Application.Setting.Dtos; - -namespace YD_Event.Application.Setting; - -/// -/// 退款配置服务 -/// -public class RefundConfigService : ITransient -{ - private readonly ISqlSugarRepository _repository; - private readonly IIdGenerator _idGenerator; - - public RefundConfigService(ISqlSugarRepository repository, IIdGenerator idGenerator) - { - _repository = repository; - _idGenerator = idGenerator; - } - - /// - /// 获取退款配置 - /// - /// - public async Task GetConfig() - { - var config = await _repository.GetFirstAsync(x => true); - if (config == null) - { - // 如果没有配置,返回默认值 - return new RefundConfigOutput - { - DeductPercentageBefore7Days = 0, - DeductPercentage3To7Days = 0, - DeductPercentageWithin3Days = 0 - }; - } - return config.Adapt(); - } - - /// - /// 更新退款配置 - /// - /// - /// - public async Task UpdateConfig(RefundConfigInput input) - { - var config = await _repository.GetFirstAsync(x => true); - if (config == null) - { - config = input.Adapt(); - config.Id = _idGenerator.NextId(); - await _repository.InsertAsync(config); - } - else - { - input.Adapt(config); - await _repository.UpdateAsync(config); - } - } -} diff --git a/YD_Event.Application/YD_Event.Application.xml b/YD_Event.Application/YD_Event.Application.xml index 49bc015..a1dfecd 100644 --- a/YD_Event.Application/YD_Event.Application.xml +++ b/YD_Event.Application/YD_Event.Application.xml @@ -1782,6 +1782,11 @@ 更新订单状态 + + + 获取建议退款金额 + + 订单退款 @@ -2348,6 +2353,53 @@ 删除项目类型 + + + 退款配置服务 + + + + + 获取所有退款配置 + + + + + + 分页查询退款配置 + + + + + + + 添加退款配置 + + + + + + + 更新退款配置 + + + + + + + 删除退款配置 + + + + + + + 计算退款金额 + + 总金额 + 赛事开始时间 + 应退金额 + 报名查询输入 @@ -2548,49 +2600,6 @@ 下载模板 - - - 退款配置输入参数 - - - - - 开赛前7天扣除比例(%) - - - - - 3-7天扣除比例(%) - - - - - 3天内扣除比例(%) - - - - - 退款配置输出参数 - - - - - 退款配置服务 - - - - - 获取退款配置 - - - - - - 更新退款配置 - - - - 姓名 diff --git a/YD_Event.Core/Entities/RefundConfig.cs b/YD_Event.Core/Entities/RefundConfig.cs index 6710edb..a0caee9 100644 --- a/YD_Event.Core/Entities/RefundConfig.cs +++ b/YD_Event.Core/Entities/RefundConfig.cs @@ -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; /// 退款配置 /// [SugarTable("RefundConfig")] -public class RefundConfig : Entity, ISoftDelete, ICreatedUserId, ICreatedTime, IUpdatedUserId, IUpdatedTime +public class RefundConfig : Entity, ISoftDelete, ICreatedUserId, ICreatedTime, IUpdatedUserId, IUpdatedTime, IAvailability { /// - /// 开赛前7天扣除比例(%) + /// 天数起始值 /// - [SugarColumn(ColumnDescription = "开赛前7天扣除比例", DecimalDigits = 2)] - public decimal DeductPercentageBefore7Days { get; set; } + [SugarColumn(ColumnDescription = "天数起始值")] + public int DayStart { get; set; } /// - /// 3-7天扣除比例(%) + /// 天数结束值 /// - [SugarColumn(ColumnDescription = "3-7天扣除比例", DecimalDigits = 2)] - public decimal DeductPercentage3To7Days { get; set; } + [SugarColumn(ColumnDescription = "天数结束值")] + public int DayEnd { get; set; } /// - /// 3天内扣除比例(%) + /// 扣除比例(%) /// - [SugarColumn(ColumnDescription = "3天内扣除比例", DecimalDigits = 2)] - public decimal DeductPercentageWithin3Days { get; set; } + [SugarColumn(ColumnDescription = "扣除比例", DecimalDigits = 2)] + public decimal DeductPercentage { get; set; } /// - /// 备注 + /// 退款备注 /// - [SugarColumn(ColumnDescription = "备注", Length = 256, IsNullable = true)] + [SugarColumn(ColumnDescription = "退款备注", Length = 256, IsNullable = true)] public string Remark { get; set; } + /// + /// 可用状态 + /// + public AvailabilityStatus Status { get; set; } + /// /// 标记删除 /// @@ -43,6 +49,9 @@ public class RefundConfig : Entity, ISoftDelete, ICreatedUserId, ICreatedT /// public long CreatedUserId { get; set; } + /// + /// 创建时间 + /// public DateTime CreatedTime { get; set; } /// @@ -50,5 +59,8 @@ public class RefundConfig : Entity, ISoftDelete, ICreatedUserId, ICreatedT /// public long? UpdatedUserId { get; set; } + /// + /// 更新时间 + /// public DateTime? UpdatedTime { get; set; } } diff --git a/YD_Event.Core/InitData/refundconfig.txt b/YD_Event.Core/InitData/refundconfig.txt new file mode 100644 index 0000000..5a8132d --- /dev/null +++ b/YD_Event.Core/InitData/refundconfig.txt @@ -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" + } +] diff --git a/YD_Event.Core/YD_Event.Core.csproj b/YD_Event.Core/YD_Event.Core.csproj index 6f93994..8c02bef 100644 --- a/YD_Event.Core/YD_Event.Core.csproj +++ b/YD_Event.Core/YD_Event.Core.csproj @@ -39,6 +39,10 @@ PreserveNewest + + PreserveNewest + + PreserveNewest diff --git a/YD_Event.Core/YD_Event.Core.xml b/YD_Event.Core/YD_Event.Core.xml index f7f46bc..16641e8 100644 --- a/YD_Event.Core/YD_Event.Core.xml +++ b/YD_Event.Core/YD_Event.Core.xml @@ -2525,24 +2525,29 @@ 退款配置 - + - 开赛前7天扣除比例(%) + 天数起始值 - + - 3-7天扣除比例(%) + 天数结束值 - + - 3天内扣除比例(%) + 扣除比例(%) - 备注 + 退款备注 + + + + + 可用状态 @@ -2555,11 +2560,21 @@ 创建人 + + + 创建时间 + + 更新人 + + + 更新时间 + + 系统菜单表