using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Logging; using SqlSugar; using Volo.Abp.Application.Services; using Volo.Abp.Users; using Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip; using Yi.Framework.AiHub.Application.Contracts.IServices; using Yi.Framework.AiHub.Domain.Entities; using Yi.Framework.AiHub.Domain.Extensions; using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.AiHub.Application.Services; /// /// 翻牌服务 /// [Authorize] public class CardFlipService : ApplicationService, ICardFlipService { private readonly ISqlSugarRepository _cardFlipTaskRepository; private readonly ISqlSugarRepository _inviteCodeRepository; private readonly ISqlSugarRepository _invitationRecordRepository; private readonly ISqlSugarRepository _premiumPackageRepository; private readonly ILogger _logger; // 翻牌规则配置 private const int MAX_FREE_FLIPS = 5; // 免费翻牌次数 private const int MAX_BONUS_FLIPS = 3; // 赠送翻牌次数 private const int MAX_INVITE_FLIPS = 2; // 邀请解锁翻牌次数 private const int TOTAL_MAX_FLIPS = 10; // 总最大翻牌次数 private const int NINTH_FLIP = 9; // 第9次翻牌 private const int TENTH_FLIP = 10; // 第10次翻牌 private const long NINTH_MIN_REWARD = 3000000; // 第9次最小奖励 300w private const long NINTH_MAX_REWARD = 7000000; // 第9次最大奖励 700w private const long TENTH_MIN_REWARD = 8000000; // 第10次最小奖励 800w private const long TENTH_MAX_REWARD = 12000000; // 第10次最大奖励 1200w public CardFlipService( ISqlSugarRepository cardFlipTaskRepository, ISqlSugarRepository inviteCodeRepository, ISqlSugarRepository invitationRecordRepository, ISqlSugarRepository premiumPackageRepository, ILogger logger) { _cardFlipTaskRepository = cardFlipTaskRepository; _inviteCodeRepository = inviteCodeRepository; _invitationRecordRepository = invitationRecordRepository; _premiumPackageRepository = premiumPackageRepository; _logger = logger; } /// /// 获取本周翻牌任务状态 /// public async Task GetWeeklyTaskStatusAsync() { var userId = CurrentUser.GetId(); var weekStart = GetWeekStartDate(DateTime.Now); // 获取或创建本周任务 var task = await GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: false); // 获取邀请码信息 var inviteCode = await _inviteCodeRepository._DbQueryable .Where(x => x.UserId == userId) .FirstAsync(); // 统计本周邀请人数 var invitedCount = await _invitationRecordRepository._DbQueryable .Where(x => x.InviterId == userId) .Where(x => x.InvitationTime >= weekStart) .CountAsync(); // 检查用户是否已被邀请 var isInvited = inviteCode?.IsUserInvited ?? false; var output = new CardFlipStatusOutput { TotalFlips = task?.TotalFlips ?? 0, RemainingFreeFlips = MAX_FREE_FLIPS - (task?.FreeFlipsUsed ?? 0), RemainingBonusFlips = MAX_BONUS_FLIPS - (task?.BonusFlipsUsed ?? 0), RemainingInviteFlips = MAX_INVITE_FLIPS - (task?.InviteFlipsUsed ?? 0), CanFlip = CanFlipCard(task), MyInviteCode = inviteCode?.Code, InvitedCount = invitedCount, IsInvited = isInvited, FlipRecords = BuildFlipRecords(task) }; // 生成提示信息 output.NextFlipTip = GenerateNextFlipTip(output); return output; } /// /// 翻牌 /// public async Task FlipCardAsync(FlipCardInput input) { var userId = CurrentUser.GetId(); var weekStart = GetWeekStartDate(DateTime.Now); if (input.FlipNumber < 1 || input.FlipNumber > TOTAL_MAX_FLIPS) { throw new UserFriendlyException($"翻牌序号必须在1-{TOTAL_MAX_FLIPS}之间"); } // 获取或创建本周任务 var task = await GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: true); // 验证翻牌次数 if (task.TotalFlips >= TOTAL_MAX_FLIPS) { throw new UserFriendlyException("本周翻牌次数已用完,请下周再来!"); } // 验证顺序翻牌 if (input.FlipNumber != task.TotalFlips + 1) { throw new UserFriendlyException("请按顺序翻牌!"); } // 判断翻牌类型 var flipType = DetermineFlipType(task); // 验证是否有足够的次数 if (!CanUseFlipType(task, flipType)) { throw new UserFriendlyException(GetFlipTypeErrorMessage(flipType)); } // 翻牌逻辑 var output = new FlipCardOutput { FlipNumber = input.FlipNumber, IsWin = false, ShowDoubleRewardTip = false }; // 前8次固定失败 if (input.FlipNumber <= 8) { output.IsWin = false; output.RewardDesc = "很遗憾,未中奖"; } // 第9次中奖 else if (input.FlipNumber == NINTH_FLIP) { var rewardAmount = GenerateRandomReward(NINTH_MIN_REWARD, NINTH_MAX_REWARD); output.IsWin = true; output.RewardAmount = rewardAmount; output.RewardDesc = $"恭喜获得尊享包 {rewardAmount / 10000}w tokens!"; output.ShowDoubleRewardTip = true; // 显示翻倍包提示 // 发放奖励 await GrantRewardAsync(userId, rewardAmount, $"翻牌活动第{input.FlipNumber}次中奖"); // 记录奖励 task.SetNinthReward(rewardAmount); } // 第10次中奖(翻倍) else if (input.FlipNumber == TENTH_FLIP) { var rewardAmount = GenerateRandomReward(TENTH_MIN_REWARD, TENTH_MAX_REWARD); output.IsWin = true; output.RewardAmount = rewardAmount; output.RewardDesc = $"恭喜获得尊享包 {rewardAmount / 10000}w tokens(翻倍奖励)!"; // 发放奖励 await GrantRewardAsync(userId, rewardAmount, $"翻牌活动第{input.FlipNumber}次中奖(翻倍)"); // 记录奖励 task.SetTenthReward(rewardAmount); } // 更新翻牌次数 task.IncrementFlip(flipType); await _cardFlipTaskRepository.UpdateAsync(task); // 计算剩余次数 output.RemainingFlips = TOTAL_MAX_FLIPS - task.TotalFlips; _logger.LogInformation($"用户 {userId} 完成第 {input.FlipNumber} 次翻牌,中奖:{output.IsWin}"); return output; } /// /// 使用邀请码解锁翻牌次数 /// public async Task UseInviteCodeAsync(UseInviteCodeInput input) { var userId = CurrentUser.GetId(); var weekStart = GetWeekStartDate(DateTime.Now); if (string.IsNullOrWhiteSpace(input.InviteCode)) { throw new UserFriendlyException("邀请码不能为空"); } // 查找邀请码 var inviteCode = await _inviteCodeRepository._DbQueryable .Where(x => x.Code == input.InviteCode) .FirstAsync(); if (inviteCode == null) { throw new UserFriendlyException("邀请码不存在"); } // 验证不能使用自己的邀请码 if (inviteCode.UserId == userId) { throw new UserFriendlyException("不能使用自己的邀请码"); } // 验证邀请码是否已被使用 if (inviteCode.IsUsed) { throw new UserFriendlyException("该邀请码已被使用"); } // 验证邀请码拥有者是否已被邀请 if (inviteCode.IsUserInvited) { throw new UserFriendlyException("该用户已被邀请,邀请码无效"); } // 检查当前用户是否已被邀请 var myInviteCode = await _inviteCodeRepository._DbQueryable .Where(x => x.UserId == userId) .FirstAsync(); if (myInviteCode?.IsUserInvited == true) { throw new UserFriendlyException("您已使用过邀请码,无法重复使用"); } // 获取本周任务 var task = await GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: true); // 验证是否已经使用了所有邀请解锁次数 if (task.InviteFlipsUsed >= MAX_INVITE_FLIPS) { throw new UserFriendlyException("本周邀请解锁次数已用完"); } // 标记邀请码为已使用 inviteCode.MarkAsUsed(userId); await _inviteCodeRepository.UpdateAsync(inviteCode); // 标记当前用户已被邀请 if (myInviteCode == null) { myInviteCode = new InviteCodeAggregateRoot(userId, GenerateInviteCode()); myInviteCode.MarkUserAsInvited(); await _inviteCodeRepository.InsertAsync(myInviteCode); } else { myInviteCode.MarkUserAsInvited(); await _inviteCodeRepository.UpdateAsync(myInviteCode); } // 创建邀请记录 var invitationRecord = new InvitationRecordAggregateRoot( inviteCode.UserId, userId, input.InviteCode); await _invitationRecordRepository.InsertAsync(invitationRecord); _logger.LogInformation($"用户 {userId} 使用邀请码 {input.InviteCode} 成功"); } /// /// 获取我的邀请码信息 /// public async Task GetMyInviteCodeAsync() { var userId = CurrentUser.GetId(); var weekStart = GetWeekStartDate(DateTime.Now); // 获取我的邀请码 var inviteCode = await _inviteCodeRepository._DbQueryable .Where(x => x.UserId == userId) .FirstAsync(); // 统计本周邀请人数 var invitedCount = await _invitationRecordRepository._DbQueryable .Where(x => x.InviterId == userId) .Where(x => x.InvitationTime >= weekStart) .CountAsync(); // 获取邀请历史 var invitationHistory = await _invitationRecordRepository._DbQueryable .Where(x => x.InviterId == userId) .OrderBy(x => x.InvitationTime, OrderByType.Desc) .Take(10) .Select(x => new InvitationHistoryItem { InvitedUserName = "用户***", // 脱敏处理 InvitationTime = x.InvitationTime, WeekDescription = GetWeekDescription(x.InvitationTime) }) .ToListAsync(); return new InviteCodeOutput { MyInviteCode = inviteCode?.Code, InvitedCount = invitedCount, IsInvited = inviteCode?.IsUserInvited ?? false, InvitationHistory = invitationHistory }; } /// /// 生成我的邀请码 /// public async Task GenerateMyInviteCodeAsync() { var userId = CurrentUser.GetId(); // 检查是否已有邀请码 var existingCode = await _inviteCodeRepository._DbQueryable .Where(x => x.UserId == userId) .FirstAsync(); if (existingCode != null) { return existingCode.Code; } // 生成新邀请码 var code = GenerateInviteCode(); var inviteCode = new InviteCodeAggregateRoot(userId, code); await _inviteCodeRepository.InsertAsync(inviteCode); _logger.LogInformation($"用户 {userId} 生成邀请码 {code}"); return code; } #region 私有辅助方法 /// /// 获取或创建本周任务 /// private async Task GetOrCreateWeeklyTaskAsync( Guid userId, DateTime weekStart, bool createIfNotExists) { var task = await _cardFlipTaskRepository._DbQueryable .Where(x => x.UserId == userId && x.WeekStartDate == weekStart) .FirstAsync(); if (task == null && createIfNotExists) { task = new CardFlipTaskAggregateRoot(userId, weekStart); await _cardFlipTaskRepository.InsertAsync(task); } return task; } /// /// 获取本周开始日期(周一) /// private DateTime GetWeekStartDate(DateTime date) { var dayOfWeek = (int)date.DayOfWeek; // 将周日(0)转换为7 if (dayOfWeek == 0) dayOfWeek = 7; // 计算本周一的日期 var monday = date.Date.AddDays(-(dayOfWeek - 1)); return monday; } /// /// 判断是否可以翻牌 /// private bool CanFlipCard(CardFlipTaskAggregateRoot? task) { if (task == null) return true; // 没有任务记录,可以开始翻牌 return task.TotalFlips < TOTAL_MAX_FLIPS; } /// /// 判断翻牌类型 /// private FlipType DetermineFlipType(CardFlipTaskAggregateRoot task) { if (task.FreeFlipsUsed < MAX_FREE_FLIPS) { return FlipType.Free; } else if (task.BonusFlipsUsed < MAX_BONUS_FLIPS) { return FlipType.Bonus; } else { return FlipType.Invite; } } /// /// 判断是否可以使用该翻牌类型 /// private bool CanUseFlipType(CardFlipTaskAggregateRoot task, FlipType flipType) { return flipType switch { FlipType.Free => task.FreeFlipsUsed < MAX_FREE_FLIPS, FlipType.Bonus => task.BonusFlipsUsed < MAX_BONUS_FLIPS, FlipType.Invite => task.InviteFlipsUsed < MAX_INVITE_FLIPS, _ => false }; } /// /// 获取翻牌类型错误提示 /// private string GetFlipTypeErrorMessage(FlipType flipType) { return flipType switch { FlipType.Free => "免费翻牌次数已用完", FlipType.Bonus => "赠送翻牌次数已用完", FlipType.Invite => "需要使用邀请码解锁更多次数", _ => "无法翻牌" }; } /// /// 构建翻牌记录列表 /// private List BuildFlipRecords(CardFlipTaskAggregateRoot? task) { var records = new List(); for (int i = 1; i <= TOTAL_MAX_FLIPS; i++) { var record = new CardFlipRecord { FlipNumber = i, IsFlipped = task != null && i <= task.TotalFlips, IsWin = false, FlipTypeDesc = GetFlipTypeDesc(i) }; // 设置中奖信息 if (task != null && i <= task.TotalFlips) { if (i == NINTH_FLIP && task.HasNinthReward) { record.IsWin = true; record.RewardAmount = task.NinthRewardAmount; } else if (i == TENTH_FLIP && task.HasTenthReward) { record.IsWin = true; record.RewardAmount = task.TenthRewardAmount; } } records.Add(record); } return records; } /// /// 获取翻牌类型描述 /// private string GetFlipTypeDesc(int flipNumber) { if (flipNumber <= MAX_FREE_FLIPS) { return "免费"; } else if (flipNumber <= MAX_FREE_FLIPS + MAX_BONUS_FLIPS) { return "赠送"; } else { return "邀请解锁"; } } /// /// 生成下次翻牌提示 /// private string GenerateNextFlipTip(CardFlipStatusOutput status) { if (status.TotalFlips >= TOTAL_MAX_FLIPS) { return "本周翻牌次数已用完,请下周再来!"; } if (status.RemainingFreeFlips > 0) { return $"还有{status.RemainingFreeFlips}次免费翻牌机会"; } else if (status.RemainingBonusFlips > 0) { return $"还有{status.RemainingBonusFlips}次赠送翻牌机会"; } else if (status.RemainingInviteFlips > 0) { if (status.TotalFlips == 8) { return "再邀请一个人,马上中奖!"; } return $"使用邀请码可解锁{status.RemainingInviteFlips}次翻牌"; } return "继续加油!"; } /// /// 生成随机奖励金额 /// private long GenerateRandomReward(long min, long max) { var random = new Random(); return (long)(random.NextDouble() * (max - min) + min); } /// /// 发放奖励 /// private async Task GrantRewardAsync(Guid userId, long amount, string description) { var premiumPackage = new PremiumPackageAggregateRoot(userId, amount, description) { PurchaseAmount = 0, // 奖励不需要付费 Remark = $"翻牌活动奖励:{amount / 10000}w tokens" }; await _premiumPackageRepository.InsertAsync(premiumPackage); _logger.LogInformation($"用户 {userId} 获得翻牌奖励 {amount / 10000}w tokens"); } /// /// 生成邀请码 /// private string GenerateInviteCode() { const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var random = new Random(); var code = new char[8]; for (int i = 0; i < code.Length; i++) { code[i] = chars[random.Next(chars.Length)]; } return new string(code); } /// /// 获取周描述 /// private string GetWeekDescription(DateTime date) { var weekStart = GetWeekStartDate(date); var weekEnd = weekStart.AddDays(6); return $"{weekStart:MM-dd} 至 {weekEnd:MM-dd}"; } #endregion }