2026-04-12 13:14:20 +08:00

379 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using YD_Event.Application.Coupon.Dtos;
using YD_Event.Core.Entities;
namespace YD_Event.Application.Coupon;
/// <summary>
/// 优惠券服务
/// </summary>
public class CouponService : BaseService<YD_Event.Core.Entities.Coupon>, ITransient
{
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Coupon> _repository;
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
private readonly ISqlSugarRepository<CouponRedemptionCode> _redemptionCodeRepository;
private readonly ISqlSugarRepository<UserCoupon> _userCouponRepository;
private readonly IIdGenerator _idGenerator;
public CouponService(
ISqlSugarRepository<YD_Event.Core.Entities.Coupon> repository,
ISqlSugarRepository<EventInfo> eventRepository,
ISqlSugarRepository<CouponRedemptionCode> redemptionCodeRepository,
ISqlSugarRepository<UserCoupon> userCouponRepository,
IIdGenerator idGenerator) : base(repository)
{
_repository = repository;
_eventRepository = eventRepository;
_redemptionCodeRepository = redemptionCodeRepository;
_userCouponRepository = userCouponRepository;
_idGenerator = idGenerator;
}
/// <summary>
/// 优惠券分页查询
/// </summary>
[DisplayName("优惠券分页查询")]
public async Task<CouponPageResult> 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<long, string>();
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<long>()
: 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)
};
}
/// <summary>
/// 添加优惠券
/// </summary>
[DisplayName("添加优惠券")]
public async Task Add(AddCouponInput input)
{
var entity = input.Adapt<YD_Event.Core.Entities.Coupon>();
entity.Id = _idGenerator.NextId();
// 处理赛事ID集合
if (input.EventIds != null && input.EventIds.Any())
{
entity.EventIds = string.Join(",", input.EventIds);
}
await _repository.InsertAsync(entity);
}
/// <summary>
/// 更新优惠券
/// </summary>
[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);
}
/// <summary>
/// 删除优惠券
/// </summary>
[DisplayName("删除优惠券")]
public async Task Delete(List<long> ids)
{
if (ids == null || !ids.Any()) return;
await _repository.DeleteAsync(x => ids.Contains(x.Id));
}
/// <summary>
/// 生成单个兑换码(兼容旧接口)
/// </summary>
[DisplayName("生成兑换码")]
[UnitOfWork]
[Obsolete("请使用 GenerateRedemptionCodes 方法")]
public async Task<string> GenerateCode(long id)
{
var result = await GenerateRedemptionCodes(new GenerateRedemptionCodesInput { CouponId = id, Count = 1 });
return result.FirstOrDefault()?.Code ?? string.Empty;
}
/// <summary>
/// 批量生成兑换码
/// </summary>
[DisplayName("批量生成兑换码")]
[UnitOfWork]
public async Task<List<RedemptionCodeOutput>> GenerateRedemptionCodes(GenerateRedemptionCodesInput input)
{
// 验证优惠券是否存在
var coupon = await _repository.GetByIdAsync(input.CouponId);
if (coupon == null) throw Oops.Bah("优惠券不存在");
var generatedCodes = new List<CouponRedemptionCode>();
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();
}
/// <summary>
/// 兑换码分页查询
/// </summary>
[DisplayName("兑换码分页查询")]
public async Task<PageResult<RedemptionCodeOutput>> 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<RedemptionCodeOutput>
{
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()
};
}
/// <summary>
/// 使用兑换码
/// </summary>
[DisplayName("使用兑换码")]
[UnitOfWork]
public async Task<long> 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;
}
/// <summary>
/// 生成随机兑换码
/// </summary>
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());
}
/// <summary>
/// 获取赛事列表(下拉选择用)
/// </summary>
[DisplayName("获取赛事列表")]
public async Task<List<EventSelectOutput>> 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();
}
}