using YD_Event.Application.Coupon.Dtos; using YD_Event.Core.Entities; namespace YD_Event.Application.Coupon; /// /// 优惠券服务 /// public class CouponService : BaseService, ITransient { private readonly ISqlSugarRepository _repository; private readonly ISqlSugarRepository _eventRepository; private readonly ISqlSugarRepository _redemptionCodeRepository; private readonly ISqlSugarRepository _userCouponRepository; private readonly IIdGenerator _idGenerator; public CouponService( ISqlSugarRepository repository, ISqlSugarRepository eventRepository, ISqlSugarRepository redemptionCodeRepository, ISqlSugarRepository userCouponRepository, IIdGenerator idGenerator) : base(repository) { _repository = repository; _eventRepository = eventRepository; _redemptionCodeRepository = redemptionCodeRepository; _userCouponRepository = userCouponRepository; _idGenerator = idGenerator; } /// /// 优惠券分页查询 /// [DisplayName("优惠券分页查询")] public async Task Page(CouponQueryInput input) { var query = _repository.AsQueryable() .WhereIF(!string.IsNullOrWhiteSpace(input.Name), x => x.Name.Contains(input.Name)) .WhereIF(input.Type.HasValue, x => x.Type == input.Type.Value) .OrderByDescending(x => x.CreatedTime); // 先获取实体列表 var entityList = await query.ToPagedListAsync(input); // 获取所有相关的赛事ID var allEventIds = entityList.Rows .Where(x => !string.IsNullOrEmpty(x.EventIds)) .SelectMany(x => x.EventIds.Split(',', StringSplitOptions.RemoveEmptyEntries)) .Select(long.Parse) .Distinct() .ToList(); var eventMap = new Dictionary(); if (allEventIds.Any()) { var events = await _eventRepository.AsQueryable() .Where(x => allEventIds.Contains(x.Id)) .ToListAsync(); eventMap = events.ToDictionary(x => x.Id, x => x.EventName); } // 转换并填充赛事名称 var resultList = entityList.Rows.Select(item => { var eventIdList = string.IsNullOrEmpty(item.EventIds) ? new List() : item.EventIds.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(long.Parse).ToList(); var eventNames = "通用"; if (eventIdList.Any()) { var names = eventIdList .Where(id => eventMap.ContainsKey(id)) .Select(id => eventMap[id]); eventNames = string.Join(",", names); } return new CouponPageOutput { Id = item.Id, Name = item.Name, Type = item.Type, Value = item.Value, StartTime = item.StartTime, EndTime = item.EndTime, TotalQuantity = item.TotalQuantity, ClaimedQuantity = item.ClaimedQuantity, UsedQuantity = item.UsedQuantity, CreatedTime = item.CreatedTime, EventIds = eventIdList, EventNames = eventNames }; }).ToList(); return new CouponPageResult { PageNo = entityList.PageNo, PageSize = entityList.PageSize, Total = entityList.Total, Rows = resultList, TotalPage = (int)Math.Ceiling((double)entityList.Total / entityList.PageSize) }; } /// /// 添加优惠券 /// [DisplayName("添加优惠券")] public async Task Add(AddCouponInput input) { var entity = input.Adapt(); entity.Id = _idGenerator.NextId(); // 处理赛事ID集合 if (input.EventIds != null && input.EventIds.Any()) { entity.EventIds = string.Join(",", input.EventIds); } await _repository.InsertAsync(entity); } /// /// 更新优惠券 /// [DisplayName("更新优惠券")] public async Task Update(UpdateCouponInput input) { var entity = await _repository.GetByIdAsync(input.Id); if (entity == null) throw Oops.Bah("优惠券不存在"); // 使用Mapster进行属性映射 input.Adapt(entity); // 处理赛事ID集合 if (input.EventIds != null && input.EventIds.Any()) { entity.EventIds = string.Join(",", input.EventIds); } else { entity.EventIds = null; } await _repository.UpdateAsync(entity); } /// /// 删除优惠券 /// [DisplayName("删除优惠券")] public async Task Delete(List ids) { if (ids == null || !ids.Any()) return; await _repository.DeleteAsync(x => ids.Contains(x.Id)); } /// /// 生成单个兑换码(兼容旧接口) /// [DisplayName("生成兑换码")] [UnitOfWork] [Obsolete("请使用 GenerateRedemptionCodes 方法")] public async Task GenerateCode(long id) { var result = await GenerateRedemptionCodes(new GenerateRedemptionCodesInput { CouponId = id, Count = 1 }); return result.FirstOrDefault()?.Code ?? string.Empty; } /// /// 批量生成兑换码 /// [DisplayName("批量生成兑换码")] [UnitOfWork] public async Task> GenerateRedemptionCodes(GenerateRedemptionCodesInput input) { // 验证优惠券是否存在 var coupon = await _repository.GetByIdAsync(input.CouponId); if (coupon == null) throw Oops.Bah("优惠券不存在"); var generatedCodes = new List(); const int maxRetryPerCode = 5; for (int i = 0; i < input.Count; i++) { string newCode; int retryCount = 0; bool codeIsUnique = false; do { // 生成12位大写字母数字组合的兑换码 newCode = GenerateRandomCode(12); retryCount++; // 检查是否已存在 var exists = await _redemptionCodeRepository.AsQueryable() .Where(x => x.Code == newCode) .AnyAsync(); if (!exists) { codeIsUnique = true; } } while (!codeIsUnique && retryCount < maxRetryPerCode); if (!codeIsUnique) { throw Oops.Bah($"生成第 {i + 1} 个兑换码时失败,请重试"); } var redemptionCode = new CouponRedemptionCode { Id = _idGenerator.NextId(), CouponId = input.CouponId, Code = newCode, Status = 0, DeleteMark = false }; generatedCodes.Add(redemptionCode); } // 批量插入 await _redemptionCodeRepository.InsertRangeAsync(generatedCodes); // 返回结果 return generatedCodes.Select(x => new RedemptionCodeOutput { Id = x.Id, CouponId = x.CouponId, Code = x.Code, Status = x.Status, CreatedTime = x.CreatedTime }).ToList(); } /// /// 兑换码分页查询 /// [DisplayName("兑换码分页查询")] public async Task> GetRedemptionCodePage(RedemptionCodeQueryInput input) { var query = _redemptionCodeRepository.AsQueryable() .WhereIF(input.CouponId.HasValue, x => x.CouponId == input.CouponId.Value) .WhereIF(input.Status.HasValue, x => x.Status == input.Status.Value) .WhereIF(!string.IsNullOrWhiteSpace(input.Code), x => x.Code.Contains(input.Code)) .OrderByDescending(x => x.CreatedTime); var pagedList = await query.ToPagedListAsync(input); return new PageResult { PageNo = pagedList.PageNo, PageSize = pagedList.PageSize, Total = pagedList.Total, Rows = pagedList.Rows.Select(x => new RedemptionCodeOutput { Id = x.Id, CouponId = x.CouponId, Code = x.Code, Status = x.Status, UsedTime = x.UsedTime, UserCouponId = x.UserCouponId, CreatedTime = x.CreatedTime }).ToList() }; } /// /// 使用兑换码 /// [DisplayName("使用兑换码")] [UnitOfWork] public async Task UseRedemptionCode(UseRedemptionCodeInput input) { // 查找兑换码 var redemptionCode = await _redemptionCodeRepository.AsQueryable() .Where(x => x.Code == input.Code) .FirstAsync(); if (redemptionCode == null) { throw Oops.Bah("兑换码不存在"); } // 检查是否已使用 if (redemptionCode.Status == 1) { throw Oops.Bah("该兑换码已被使用"); } // 检查优惠券是否有效 var coupon = await _repository.GetByIdAsync(redemptionCode.CouponId); if (coupon == null) { throw Oops.Bah("关联优惠券不存在"); } if (coupon.Status != AvailabilityStatus.Enable) { throw Oops.Bah("优惠券已停用"); } // 检查优惠券有效期 var now = DateTime.Now; if (coupon.StartTime.HasValue && now < coupon.StartTime.Value) { throw Oops.Bah("优惠券尚未到使用时间"); } if (coupon.EndTime.HasValue && now > coupon.EndTime.Value) { throw Oops.Bah("优惠券已过期"); } // 检查库存 if (coupon.TotalQuantity > 0 && coupon.ClaimedQuantity >= coupon.TotalQuantity) { throw Oops.Bah("优惠券已领完"); } // 创建用户优惠券记录 var userCoupon = new UserCoupon { Id = _idGenerator.NextId(), UserId = input.UserId ?? 0, CouponId = coupon.Id, ClaimTime = now, Status = 0, RedemptionCodeId = redemptionCode.Id, RedemptionCode = redemptionCode.Code, DeleteMark = false }; await _userCouponRepository.InsertAsync(userCoupon); // 更新兑换码状态 redemptionCode.Status = 1; redemptionCode.UsedTime = now; redemptionCode.UserCouponId = userCoupon.Id; await _redemptionCodeRepository.UpdateAsync(redemptionCode); // 更新优惠券领取数量 coupon.ClaimedQuantity++; await _repository.UpdateAsync(coupon); return userCoupon.Id; } /// /// 生成随机兑换码 /// private string GenerateRandomCode(int length) { const string chars = "ABCDEFGHJKMNPQRSTUVWXYZ23456789"; var random = new Random(); return new string(Enumerable.Repeat(chars, length) .Select(s => s[random.Next(s.Length)]).ToArray()); } /// /// 获取赛事列表(下拉选择用) /// [DisplayName("获取赛事列表")] public async Task> GetEventList() { // 这里可以根据需要过滤赛事状态 return await _eventRepository.AsQueryable() .Where(x => x.DeleteMark == false) .OrderByDescending(x => x.CreatedTime) .Select(x => new EventSelectOutput { Id = x.Id, Name = x.EventName }) .ToListAsync(); } }