From aec90ec9d622298f2a9fdf73aa7d439d2de8b10c Mon Sep 17 00:00:00 2001 From: chenchun Date: Thu, 23 Oct 2025 21:58:47 +0800 Subject: [PATCH 01/21] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=BF=BB?= =?UTF-8?q?=E7=89=8C=E6=B4=BB=E5=8A=A8=E5=85=A5=E5=8F=A3=E4=B8=8E=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E7=BB=84=E4=BB=B6=E5=A3=B0=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 Header Avatar 菜单新增翻牌活动(cardFlip)入口,并添加对应插槽 - 在 types/components.d.ts 中添加 CardFlipActivity 与 ElCollapseTransition 类型声明 - 在 .eslintrc-auto-import.json 中新增 ElMessage 与 ElMessageBox 自动导入 - 从 import_meta.d.ts 中移除 VITE_BUILD_COMPRESS 环境声明 - 在 YiAbpWebModule.cs 中添加相关 using 并保留数据库建表初始化的注释(CodeFirst.InitTables) --- .../Dtos/CardFlip/CardFlipStatusOutput.cs | 88 ++ .../Dtos/CardFlip/FlipCardInput.cs | 12 + .../Dtos/CardFlip/FlipCardOutput.cs | 37 + .../Dtos/CardFlip/InviteCodeOutput.cs | 48 + .../Dtos/CardFlip/UseInviteCodeInput.cs | 12 + .../IServices/ICardFlipService.cs | 41 + .../Services/CardFlipService.cs | 581 ++++++++++ .../Entities/CardFlipTaskAggregateRoot.cs | 160 +++ .../Entities/InvitationRecordAggregateRoot.cs | 76 ++ .../Entities/InviteCodeAggregateRoot.cs | 88 ++ Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs | 5 + Yi.Ai.Vue3/.eslintrc-auto-import.json | 2 + Yi.Ai.Vue3/src/api/cardFlip/index.ts | 33 + Yi.Ai.Vue3/src/api/cardFlip/types.ts | 57 + .../components/CardFlipActivity.vue | 1028 +++++++++++++++++ .../components/Header/components/Avatar.vue | 6 +- Yi.Ai.Vue3/types/components.d.ts | 2 + Yi.Ai.Vue3/types/import_meta.d.ts | 1 - 18 files changed, 2275 insertions(+), 2 deletions(-) create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/CardFlipStatusOutput.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/FlipCardInput.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/FlipCardOutput.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/InviteCodeOutput.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/UseInviteCodeInput.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/ICardFlipService.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/CardFlipTaskAggregateRoot.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/InvitationRecordAggregateRoot.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/InviteCodeAggregateRoot.cs create mode 100644 Yi.Ai.Vue3/src/api/cardFlip/index.ts create mode 100644 Yi.Ai.Vue3/src/api/cardFlip/types.ts create mode 100644 Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/CardFlipStatusOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/CardFlipStatusOutput.cs new file mode 100644 index 00000000..c1c218e7 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/CardFlipStatusOutput.cs @@ -0,0 +1,88 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip; + +/// +/// 翻牌任务状态输出 +/// +public class CardFlipStatusOutput +{ + /// + /// 本周总翻牌次数 + /// + public int TotalFlips { get; set; } + + /// + /// 剩余免费次数 + /// + public int RemainingFreeFlips { get; set; } + + /// + /// 剩余赠送次数 + /// + public int RemainingBonusFlips { get; set; } + + /// + /// 剩余邀请解锁次数 + /// + public int RemainingInviteFlips { get; set; } + + /// + /// 是否可以翻牌 + /// + public bool CanFlip { get; set; } + + /// + /// 用户的邀请码 + /// + public string? MyInviteCode { get; set; } + + /// + /// 本周邀请人数 + /// + public int InvitedCount { get; set; } + + /// + /// 是否已被邀请(被邀请后不可再提供邀请码) + /// + public bool IsInvited { get; set; } + + /// + /// 翻牌记录 + /// + public List FlipRecords { get; set; } = new(); + + /// + /// 下次可翻牌提示 + /// + public string? NextFlipTip { get; set; } +} + +/// +/// 翻牌记录 +/// +public class CardFlipRecord +{ + /// + /// 翻牌序号(1-10) + /// + public int FlipNumber { get; set; } + + /// + /// 是否已翻 + /// + public bool IsFlipped { get; set; } + + /// + /// 是否中奖 + /// + public bool IsWin { get; set; } + + /// + /// 奖励金额(token数) + /// + public long? RewardAmount { get; set; } + + /// + /// 翻牌类型描述 + /// + public string? FlipTypeDesc { get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/FlipCardInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/FlipCardInput.cs new file mode 100644 index 00000000..e974dfd6 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/FlipCardInput.cs @@ -0,0 +1,12 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip; + +/// +/// 翻牌输入 +/// +public class FlipCardInput +{ + /// + /// 翻牌序号(1-10) + /// + public int FlipNumber { get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/FlipCardOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/FlipCardOutput.cs new file mode 100644 index 00000000..af6e0167 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/FlipCardOutput.cs @@ -0,0 +1,37 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip; + +/// +/// 翻牌输出 +/// +public class FlipCardOutput +{ + /// + /// 翻牌序号(1-10) + /// + public int FlipNumber { get; set; } + + /// + /// 是否中奖 + /// + public bool IsWin { get; set; } + + /// + /// 奖励金额(token数) + /// + public long? RewardAmount { get; set; } + + /// + /// 奖励描述 + /// + public string? RewardDesc { get; set; } + + /// + /// 是否显示翻倍包提示(第9次中奖后显示) + /// + public bool ShowDoubleRewardTip { get; set; } + + /// + /// 剩余可翻次数 + /// + public int RemainingFlips { get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/InviteCodeOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/InviteCodeOutput.cs new file mode 100644 index 00000000..2531498a --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/InviteCodeOutput.cs @@ -0,0 +1,48 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip; + +/// +/// 邀请码信息输出 +/// +public class InviteCodeOutput +{ + /// + /// 我的邀请码 + /// + public string? MyInviteCode { get; set; } + + /// + /// 本周邀请人数 + /// + public int InvitedCount { get; set; } + + /// + /// 是否已被邀请 + /// + public bool IsInvited { get; set; } + + /// + /// 邀请历史记录 + /// + public List InvitationHistory { get; set; } = new(); +} + +/// +/// 邀请历史记录项 +/// +public class InvitationHistoryItem +{ + /// + /// 被邀请人昵称(脱敏) + /// + public string InvitedUserName { get; set; } = string.Empty; + + /// + /// 邀请时间 + /// + public DateTime InvitationTime { get; set; } + + /// + /// 本周所在 + /// + public string WeekDescription { get; set; } = string.Empty; +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/UseInviteCodeInput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/UseInviteCodeInput.cs new file mode 100644 index 00000000..e02cc6ab --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/UseInviteCodeInput.cs @@ -0,0 +1,12 @@ +namespace Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip; + +/// +/// 使用邀请码输入 +/// +public class UseInviteCodeInput +{ + /// + /// 邀请码 + /// + public string InviteCode { get; set; } = string.Empty; +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/ICardFlipService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/ICardFlipService.cs new file mode 100644 index 00000000..9dbc2544 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/IServices/ICardFlipService.cs @@ -0,0 +1,41 @@ +using Yi.Framework.AiHub.Application.Contracts.Dtos.CardFlip; + +namespace Yi.Framework.AiHub.Application.Contracts.IServices; + +/// +/// 翻牌服务接口 +/// +public interface ICardFlipService +{ + /// + /// 获取本周翻牌任务状态 + /// + /// + Task GetWeeklyTaskStatusAsync(); + + /// + /// 翻牌 + /// + /// 翻牌输入 + /// + Task FlipCardAsync(FlipCardInput input); + + /// + /// 使用邀请码解锁翻牌次数 + /// + /// 邀请码输入 + /// + Task UseInviteCodeAsync(UseInviteCodeInput input); + + /// + /// 获取我的邀请码信息 + /// + /// + Task GetMyInviteCodeAsync(); + + /// + /// 生成我的邀请码(如果没有) + /// + /// + Task GenerateMyInviteCodeAsync(); +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs new file mode 100644 index 00000000..56f0fc1d --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs @@ -0,0 +1,581 @@ +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 +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/CardFlipTaskAggregateRoot.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/CardFlipTaskAggregateRoot.cs new file mode 100644 index 00000000..4b795299 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/CardFlipTaskAggregateRoot.cs @@ -0,0 +1,160 @@ +using SqlSugar; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Yi.Framework.AiHub.Domain.Entities; + +/// +/// 翻牌任务记录 +/// +[SugarTable("Ai_CardFlipTask")] +[SugarIndex($"index_{nameof(UserId)}_{nameof(WeekStartDate)}", + nameof(UserId), OrderByType.Asc, + nameof(WeekStartDate), OrderByType.Desc)] +public class CardFlipTaskAggregateRoot : FullAuditedAggregateRoot +{ + public CardFlipTaskAggregateRoot() + { + } + + public CardFlipTaskAggregateRoot(Guid userId, DateTime weekStartDate) + { + UserId = userId; + WeekStartDate = weekStartDate.Date; // 确保只存储日期部分 + TotalFlips = 0; + FreeFlipsUsed = 0; + BonusFlipsUsed = 0; + InviteFlipsUsed = 0; + IsFirstFlipDone = false; + HasNinthReward = false; + HasTenthReward = false; + } + + /// + /// 用户ID + /// + public Guid UserId { get; set; } + + /// + /// 本周开始日期(每周一) + /// + public DateTime WeekStartDate { get; set; } + + /// + /// 总共已翻牌次数 + /// + public int TotalFlips { get; set; } + + /// + /// 已使用的免费次数(最多5次) + /// + public int FreeFlipsUsed { get; set; } + + /// + /// 已使用的赠送次数(最多3次) + /// + public int BonusFlipsUsed { get; set; } + + /// + /// 已使用的邀请解锁次数(最多2次) + /// + public int InviteFlipsUsed { get; set; } + + /// + /// 是否已完成首次翻牌(用于判断是否创建任务) + /// + public bool IsFirstFlipDone { get; set; } + + /// + /// 是否已获得第9次奖励 + /// + public bool HasNinthReward { get; set; } + + /// + /// 第9次奖励金额(300-700w) + /// + public long? NinthRewardAmount { get; set; } + + /// + /// 是否已获得第10次奖励 + /// + public bool HasTenthReward { get; set; } + + /// + /// 第10次奖励金额(800-1200w) + /// + public long? TenthRewardAmount { get; set; } + + /// + /// 备注信息 + /// + [SugarColumn(Length = 500, IsNullable = true)] + public string? Remark { get; set; } + + /// + /// 增加翻牌次数 + /// + /// 翻牌类型 + public void IncrementFlip(FlipType flipType) + { + TotalFlips++; + + switch (flipType) + { + case FlipType.Free: + FreeFlipsUsed++; + break; + case FlipType.Bonus: + BonusFlipsUsed++; + break; + case FlipType.Invite: + InviteFlipsUsed++; + break; + } + + if (!IsFirstFlipDone) + { + IsFirstFlipDone = true; + } + } + + /// + /// 记录第9次奖励 + /// + /// 奖励金额 + public void SetNinthReward(long amount) + { + HasNinthReward = true; + NinthRewardAmount = amount; + } + + /// + /// 记录第10次奖励 + /// + /// 奖励金额 + public void SetTenthReward(long amount) + { + HasTenthReward = true; + TenthRewardAmount = amount; + } +} + +/// +/// 翻牌类型枚举 +/// +public enum FlipType +{ + /// + /// 免费翻牌(1-5次) + /// + Free = 0, + + /// + /// 赠送翻牌(6-8次) + /// + Bonus = 1, + + /// + /// 邀请解锁翻牌(9-10次) + /// + Invite = 2 +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/InvitationRecordAggregateRoot.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/InvitationRecordAggregateRoot.cs new file mode 100644 index 00000000..6e1bea35 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/InvitationRecordAggregateRoot.cs @@ -0,0 +1,76 @@ +using SqlSugar; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Yi.Framework.AiHub.Domain.Entities; + +/// +/// 邀请记录 +/// +[SugarTable("Ai_InvitationRecord")] +[SugarIndex($"index_{nameof(InviterId)}_{nameof(InvitedUserId)}", + nameof(InviterId), OrderByType.Asc, + nameof(InvitedUserId), OrderByType.Asc)] +[SugarIndex($"index_{nameof(InvitedUserId)}", nameof(InvitedUserId), OrderByType.Asc)] +public class InvitationRecordAggregateRoot : FullAuditedAggregateRoot +{ + public InvitationRecordAggregateRoot() + { + } + + public InvitationRecordAggregateRoot(Guid inviterId, Guid invitedUserId, string inviteCode) + { + InviterId = inviterId; + InvitedUserId = invitedUserId; + InviteCode = inviteCode; + InvitationTime = DateTime.Now; + Status = InvitationStatus.Valid; + } + + /// + /// 邀请人ID + /// + public Guid InviterId { get; set; } + + /// + /// 被邀请人ID + /// + public Guid InvitedUserId { get; set; } + + /// + /// 使用的邀请码 + /// + [SugarColumn(Length = 50)] + public string InviteCode { get; set; } = string.Empty; + + /// + /// 邀请时间 + /// + public DateTime InvitationTime { get; set; } + + /// + /// 邀请状态(0=有效,1=已撤销) + /// + public InvitationStatus Status { get; set; } + + /// + /// 备注信息 + /// + [SugarColumn(Length = 500, IsNullable = true)] + public string? Remark { get; set; } +} + +/// +/// 邀请状态枚举 +/// +public enum InvitationStatus +{ + /// + /// 有效 + /// + Valid = 0, + + /// + /// 已撤销 + /// + Revoked = 1 +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/InviteCodeAggregateRoot.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/InviteCodeAggregateRoot.cs new file mode 100644 index 00000000..de569b49 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/InviteCodeAggregateRoot.cs @@ -0,0 +1,88 @@ +using SqlSugar; +using Volo.Abp.Domain.Entities.Auditing; + +namespace Yi.Framework.AiHub.Domain.Entities; + +/// +/// 用户邀请码 +/// +[SugarTable("Ai_InviteCode")] +[SugarIndex($"index_{nameof(UserId)}", nameof(UserId), OrderByType.Asc, true)] +[SugarIndex($"index_{nameof(Code)}", nameof(Code), OrderByType.Asc, true)] +public class InviteCodeAggregateRoot : FullAuditedAggregateRoot +{ + public InviteCodeAggregateRoot() + { + } + + public InviteCodeAggregateRoot(Guid userId, string code) + { + UserId = userId; + Code = code; + IsUsed = false; + IsUserInvited = false; + UsedCount = 0; + } + + /// + /// 用户ID(邀请码拥有者) + /// + public Guid UserId { get; set; } + + /// + /// 邀请码(唯一) + /// + [SugarColumn(Length = 50)] + public string Code { get; set; } = string.Empty; + + /// + /// 是否已被使用(一个邀请码只能被使用一次) + /// + public bool IsUsed { get; set; } + + /// + /// 邀请码拥有者是否已被他人邀请(被邀请后不可再提供邀请码) + /// + public bool IsUserInvited { get; set; } + + /// + /// 被使用次数(统计用) + /// + public int UsedCount { get; set; } + + /// + /// 使用时间 + /// + public DateTime? UsedTime { get; set; } + + /// + /// 使用人ID + /// + public Guid? UsedByUserId { get; set; } + + /// + /// 备注信息 + /// + [SugarColumn(Length = 500, IsNullable = true)] + public string? Remark { get; set; } + + /// + /// 标记邀请码已被使用 + /// + /// 使用者ID + public void MarkAsUsed(Guid usedByUserId) + { + IsUsed = true; + UsedTime = DateTime.Now; + UsedByUserId = usedByUserId; + UsedCount++; + } + + /// + /// 标记用户已被邀请 + /// + public void MarkUserAsInvited() + { + IsUserInvited = true; + } +} diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index 9e08520e..b53f7c11 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -29,6 +29,7 @@ using Volo.Abp.Swashbuckle; using Yi.Abp.Application; using Yi.Abp.SqlsugarCore; using Yi.Framework.AiHub.Application; +using Yi.Framework.AiHub.Domain.Entities; using Yi.Framework.AspNetCore; using Yi.Framework.AspNetCore.Authentication.OAuth; using Yi.Framework.AspNetCore.Authentication.OAuth.Gitee; @@ -46,6 +47,7 @@ using Yi.Framework.Rbac.Application; using Yi.Framework.Rbac.Domain.Authorization; using Yi.Framework.Rbac.Domain.Shared.Consts; using Yi.Framework.Rbac.Domain.Shared.Options; +using Yi.Framework.SqlSugarCore.Abstractions; using Yi.Framework.Stock.Application; using Yi.Framework.TenantManagement.Application; @@ -350,6 +352,9 @@ namespace Yi.Abp.Web var app = context.GetApplicationBuilder(); app.UseRouting(); + // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); + // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); + // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); //跨域 app.UseCors(DefaultCorsPolicyName); diff --git a/Yi.Ai.Vue3/.eslintrc-auto-import.json b/Yi.Ai.Vue3/.eslintrc-auto-import.json index af1083b7..313e6711 100644 --- a/Yi.Ai.Vue3/.eslintrc-auto-import.json +++ b/Yi.Ai.Vue3/.eslintrc-auto-import.json @@ -5,6 +5,8 @@ "ComputedRef": true, "DirectiveBinding": true, "EffectScope": true, + "ElMessage": true, + "ElMessageBox": true, "ExtractDefaultPropTypes": true, "ExtractPropTypes": true, "ExtractPublicPropTypes": true, diff --git a/Yi.Ai.Vue3/src/api/cardFlip/index.ts b/Yi.Ai.Vue3/src/api/cardFlip/index.ts new file mode 100644 index 00000000..570fb730 --- /dev/null +++ b/Yi.Ai.Vue3/src/api/cardFlip/index.ts @@ -0,0 +1,33 @@ +import { get, post } from '@/utils/request'; +import type { + CardFlipStatusOutput, + FlipCardInput, + FlipCardOutput, + UseInviteCodeInput, + InviteCodeOutput +} from './types'; + +// 获取本周翻牌任务状态 +export function getWeeklyTaskStatus() { + return get('/card-flip/weekly-task-status').json(); +} + +// 翻牌 +export function flipCard(data: FlipCardInput) { + return post('/card-flip/flip-card', data).json(); +} + +// 使用邀请码解锁翻牌次数 +export function useInviteCode(data: UseInviteCodeInput) { + return post('/card-flip/use-invite-code', data).json(); +} + +// 获取我的邀请码信息 +export function getMyInviteCode() { + return get('/card-flip/my-invite-code').json(); +} + +// 生成我的邀请码(如果没有) +export function generateMyInviteCode() { + return post('/card-flip/generate-my-invite-code').json(); +} diff --git a/Yi.Ai.Vue3/src/api/cardFlip/types.ts b/Yi.Ai.Vue3/src/api/cardFlip/types.ts new file mode 100644 index 00000000..91d452bb --- /dev/null +++ b/Yi.Ai.Vue3/src/api/cardFlip/types.ts @@ -0,0 +1,57 @@ +// 翻牌任务状态输出 +export interface CardFlipStatusOutput { + totalFlips: number; // 本周总翻牌次数 + remainingFreeFlips: number; // 剩余免费次数 + remainingBonusFlips: number; // 剩余赠送次数 + remainingInviteFlips: number; // 剩余邀请解锁次数 + canFlip: boolean; // 是否可以翻牌 + myInviteCode?: string; // 用户的邀请码 + invitedCount: number; // 本周邀请人数 + isInvited: boolean; // 是否已被邀请 + flipRecords: CardFlipRecord[]; // 翻牌记录 + nextFlipTip?: string; // 下次可翻牌提示 +} + +// 翻牌记录 +export interface CardFlipRecord { + flipNumber: number; // 翻牌序号(1-10) + isFlipped: boolean; // 是否已翻 + isWin: boolean; // 是否中奖 + rewardAmount?: number; // 奖励金额(token数) + flipTypeDesc?: string; // 翻牌类型描述 +} + +// 翻牌输入 +export interface FlipCardInput { + flipNumber: number; // 翻牌序号(1-10) +} + +// 翻牌输出 +export interface FlipCardOutput { + flipNumber: number; // 翻牌序号(1-10) + isWin: boolean; // 是否中奖 + rewardAmount?: number; // 奖励金额(token数) + rewardDesc?: string; // 奖励描述 + showDoubleRewardTip: boolean; // 是否显示翻倍包提示 + remainingFlips: number; // 剩余可翻次数 +} + +// 使用邀请码输入 +export interface UseInviteCodeInput { + inviteCode: string; // 邀请码 +} + +// 邀请码信息输出 +export interface InviteCodeOutput { + myInviteCode?: string; // 我的邀请码 + invitedCount: number; // 本周邀请人数 + isInvited: boolean; // 是否已被邀请 + invitationHistory: InvitationHistoryItem[]; // 邀请历史记录 +} + +// 邀请历史记录项 +export interface InvitationHistoryItem { + invitedUserName: string; // 被邀请人昵称(脱敏) + invitationTime: string; // 邀请时间 + weekDescription: string; // 本周所在 +} diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue new file mode 100644 index 00000000..28fb0f38 --- /dev/null +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue @@ -0,0 +1,1028 @@ + + + + + diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue index c4589079..7d76c2f0 100644 --- a/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue +++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/Avatar.vue @@ -69,7 +69,8 @@ const navItems = [ { name: 'rechargeLog', label: '充值记录', icon: 'Document' }, { name: 'usageStatistics', label: '用量统计', icon: 'Histogram' }, { name: 'premiumService', label: '尊享服务', icon: 'ColdDrink' }, - { name: 'dailyTask', label: '每日任务', icon: 'Trophy' } + { name: 'dailyTask', label: '每日任务', icon: 'Trophy' }, + { name: 'cardFlip', label: '翻牌活动', icon: 'Present' } // { name: 'usageStatistics2', label: '用量统计2', icon: 'Histogram' }, ]; function openDialog() { @@ -349,6 +350,9 @@ function onProductPackage() { + diff --git a/Yi.Ai.Vue3/types/components.d.ts b/Yi.Ai.Vue3/types/components.d.ts index 702505a4..64cfc0d4 100644 --- a/Yi.Ai.Vue3/types/components.d.ts +++ b/Yi.Ai.Vue3/types/components.d.ts @@ -10,6 +10,7 @@ declare module 'vue' { export interface GlobalComponents { AccountPassword: typeof import('./../src/components/LoginDialog/components/FormLogin/AccountPassword.vue')['default'] APIKeyManagement: typeof import('./../src/components/userPersonalCenter/components/APIKeyManagement.vue')['default'] + CardFlipActivity: typeof import('./../src/components/userPersonalCenter/components/CardFlipActivity.vue')['default'] DailyTask: typeof import('./../src/components/userPersonalCenter/components/DailyTask.vue')['default'] DeepThinking: typeof import('./../src/components/DeepThinking/index.vue')['default'] ElAlert: typeof import('element-plus/es')['ElAlert'] @@ -19,6 +20,7 @@ declare module 'vue' { ElCard: typeof import('element-plus/es')['ElCard'] ElCollapse: typeof import('element-plus/es')['ElCollapse'] ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] + ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition'] ElContainer: typeof import('element-plus/es')['ElContainer'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] diff --git a/Yi.Ai.Vue3/types/import_meta.d.ts b/Yi.Ai.Vue3/types/import_meta.d.ts index d8a60d41..b3e9d275 100644 --- a/Yi.Ai.Vue3/types/import_meta.d.ts +++ b/Yi.Ai.Vue3/types/import_meta.d.ts @@ -6,7 +6,6 @@ interface ImportMetaEnv { readonly VITE_WEB_ENV: string; readonly VITE_WEB_BASE_API: string; readonly VITE_API_URL: string; - readonly VITE_BUILD_COMPRESS: string; readonly VITE_SSO_SEVER_URL: string; readonly VITE_APP_VERSION: string; } From a1395d9a338d1102fbdc222e8387e717a16e9839 Mon Sep 17 00:00:00 2001 From: chenchun Date: Mon, 27 Oct 2025 21:57:26 +0800 Subject: [PATCH 02/21] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E7=BF=BB?= =?UTF-8?q?=E7=89=8C=E9=A1=BA=E5=BA=8F=E8=BF=BD=E8=B8=AA=E5=B9=B6=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E7=BF=BB=E7=89=8C/=E9=82=80=E8=AF=B7=E7=A0=81?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E5=88=B0=20Manager=EF=BC=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=89=8D=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 CardFlipStatusOutput 与前端 types 添加 FlipOrderIndex 字段以记录牌在翻牌顺序中的位置 - 在域实体 CardFlipTaskAggregateRoot 增加 FlippedOrder(Json 列)以保存用户实际翻牌顺序 - 将 CardFlipService 重构为调用 CardFlipManager 与 InviteCodeManager,移除大量内聚的业务实现与常量(职责下沉到 Manager) - 调整翻牌、使用邀请码和查询相关流程为 Manager 驱动,更新返回结构与提示文本 - 更新前端 CardFlipActivity 组件与 types,允许任意未翻的卡片被点击并显示翻牌顺序位置 - 若干文案、格式与日志细节修正 --- .claude/settings.local.json | 9 + Yi.Abp.Net8/.claude/settings.local.json | 9 + .../Dtos/CardFlip/CardFlipStatusOutput.cs | 5 + .../Services/CardFlipService.cs | 443 +++--------------- .../Entities/CardFlipTaskAggregateRoot.cs | 7 + .../Managers/CardFlipManager.cs | 308 ++++++++++++ .../Managers/InviteCodeManager.cs | 217 +++++++++ Yi.Ai.Vue3/src/api/cardFlip/types.ts | 1 + .../components/CardFlipActivity.vue | 9 +- 9 files changed, 633 insertions(+), 375 deletions(-) create mode 100644 .claude/settings.local.json create mode 100644 Yi.Abp.Net8/.claude/settings.local.json create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/CardFlipManager.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/InviteCodeManager.cs diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000..76069758 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "WebFetch(domain:www.donet5.com)" + ], + "deny": [], + "ask": [] + } +} diff --git a/Yi.Abp.Net8/.claude/settings.local.json b/Yi.Abp.Net8/.claude/settings.local.json new file mode 100644 index 00000000..11d555b0 --- /dev/null +++ b/Yi.Abp.Net8/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(dotnet build \"E:\\code\\github\\Yi\\Yi.Abp.Net8\\module\\ai-hub\\Yi.Framework.AiHub.Application\\Yi.Framework.AiHub.Application.csproj\" --no-restore)" + ], + "deny": [], + "ask": [] + } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/CardFlipStatusOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/CardFlipStatusOutput.cs index c1c218e7..3594dbb8 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/CardFlipStatusOutput.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/CardFlip/CardFlipStatusOutput.cs @@ -85,4 +85,9 @@ public class CardFlipRecord /// 翻牌类型描述 /// public string? FlipTypeDesc { get; set; } + + /// + /// 在翻牌顺序中的位置(1-10,表示第几个翻) + /// + public int FlipOrderIndex { get; set; } } diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs index 56f0fc1d..d0668225 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs @@ -1,52 +1,35 @@ 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.AiHub.Domain.Managers; 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 CardFlipManager _cardFlipManager; + private readonly InviteCodeManager _inviteCodeManager; 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, + CardFlipManager cardFlipManager, + InviteCodeManager inviteCodeManager, ISqlSugarRepository premiumPackageRepository, ILogger logger) { - _cardFlipTaskRepository = cardFlipTaskRepository; - _inviteCodeRepository = inviteCodeRepository; - _invitationRecordRepository = invitationRecordRepository; + _cardFlipManager = cardFlipManager; + _inviteCodeManager = inviteCodeManager; _premiumPackageRepository = premiumPackageRepository; _logger = logger; } @@ -57,32 +40,27 @@ public class CardFlipService : ApplicationService, ICardFlipService public async Task GetWeeklyTaskStatusAsync() { var userId = CurrentUser.GetId(); - var weekStart = GetWeekStartDate(DateTime.Now); + var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now); - // 获取或创建本周任务 - var task = await GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: false); + // 获取本周任务 + var task = await _cardFlipManager.GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: false); // 获取邀请码信息 - var inviteCode = await _inviteCodeRepository._DbQueryable - .Where(x => x.UserId == userId) - .FirstAsync(); + var inviteCode = await _inviteCodeManager.GetUserInviteCodeAsync(userId); // 统计本周邀请人数 - var invitedCount = await _invitationRecordRepository._DbQueryable - .Where(x => x.InviterId == userId) - .Where(x => x.InvitationTime >= weekStart) - .CountAsync(); + var invitedCount = await _inviteCodeManager.GetWeeklyInvitationCountAsync(userId, weekStart); // 检查用户是否已被邀请 - var isInvited = inviteCode?.IsUserInvited ?? false; + var isInvited = await _inviteCodeManager.IsUserInvitedAsync(userId); 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), + RemainingFreeFlips = CardFlipManager.MAX_FREE_FLIPS - (task?.FreeFlipsUsed ?? 0), + RemainingBonusFlips = CardFlipManager.MAX_BONUS_FLIPS - (task?.BonusFlipsUsed ?? 0), + RemainingInviteFlips = CardFlipManager.MAX_INVITE_FLIPS - (task?.InviteFlipsUsed ?? 0), + CanFlip = _cardFlipManager.CanFlipCard(task), MyInviteCode = inviteCode?.Code, InvitedCount = invitedCount, IsInvited = isInvited, @@ -101,90 +79,28 @@ public class CardFlipService : ApplicationService, ICardFlipService public async Task FlipCardAsync(FlipCardInput input) { var userId = CurrentUser.GetId(); - var weekStart = GetWeekStartDate(DateTime.Now); + var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now); - if (input.FlipNumber < 1 || input.FlipNumber > TOTAL_MAX_FLIPS) + // 执行翻牌逻辑(由Manager处理验证和翻牌) + var result = await _cardFlipManager.ExecuteFlipAsync(userId, input.FlipNumber, weekStart); + + // 如果中奖,发放奖励 + if (result.IsWin) { - throw new UserFriendlyException($"翻牌序号必须在1-{TOTAL_MAX_FLIPS}之间"); + await GrantRewardAsync(userId, result.RewardAmount, $"翻牌活动第{input.FlipNumber}次中奖"); } - // 获取或创建本周任务 - 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 + FlipNumber = result.FlipNumber, + IsWin = result.IsWin, + RewardAmount = result.RewardAmount, + RewardDesc = result.RewardDesc, + ShowDoubleRewardTip = result.ShowDoubleRewardTip, + RemainingFlips = CardFlipManager.TOTAL_MAX_FLIPS - input.FlipNumber }; - // 前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; } @@ -194,85 +110,21 @@ public class CardFlipService : ApplicationService, ICardFlipService 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 weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now); // 获取本周任务 - var task = await GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: true); + var task = await _cardFlipManager.GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: true); // 验证是否已经使用了所有邀请解锁次数 - if (task.InviteFlipsUsed >= MAX_INVITE_FLIPS) + if (task.InviteFlipsUsed >= CardFlipManager.MAX_INVITE_FLIPS) { throw new UserFriendlyException("本周邀请解锁次数已用完"); } - // 标记邀请码为已使用 - inviteCode.MarkAsUsed(userId); - await _inviteCodeRepository.UpdateAsync(inviteCode); + // 使用邀请码(由Manager处理验证和邀请逻辑) + await _inviteCodeManager.UseInviteCodeAsync(userId, input.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} 成功"); + _logger.LogInformation($"用户 {userId} 使用邀请码 {input.InviteCode} 解锁翻牌次数成功"); } /// @@ -281,38 +133,28 @@ public class CardFlipService : ApplicationService, ICardFlipService public async Task GetMyInviteCodeAsync() { var userId = CurrentUser.GetId(); - var weekStart = GetWeekStartDate(DateTime.Now); + var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now); // 获取我的邀请码 - var inviteCode = await _inviteCodeRepository._DbQueryable - .Where(x => x.UserId == userId) - .FirstAsync(); + var inviteCode = await _inviteCodeManager.GetUserInviteCodeAsync(userId); // 统计本周邀请人数 - var invitedCount = await _invitationRecordRepository._DbQueryable - .Where(x => x.InviterId == userId) - .Where(x => x.InvitationTime >= weekStart) - .CountAsync(); + var invitedCount = await _inviteCodeManager.GetWeeklyInvitationCountAsync(userId, weekStart); // 获取邀请历史 - 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(); + var invitationHistory = await _inviteCodeManager.GetInvitationHistoryAsync(userId, 10); return new InviteCodeOutput { MyInviteCode = inviteCode?.Code, InvitedCount = invitedCount, IsInvited = inviteCode?.IsUserInvited ?? false, - InvitationHistory = invitationHistory + InvitationHistory = invitationHistory.Select(x => new InvitationHistoryItem + { + InvitedUserName = x.InvitedUserName, + InvitationTime = x.InvitationTime, + WeekDescription = GetWeekDescription(x.InvitationTime) + }).ToList() }; } @@ -323,120 +165,14 @@ public class CardFlipService : ApplicationService, ICardFlipService { 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}"); + // 生成邀请码(由Manager处理) + var code = await _inviteCodeManager.GenerateInviteCodeForUserAsync(userId); 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 => "需要使用邀请码解锁更多次数", - _ => "无法翻牌" - }; - } - /// /// 构建翻牌记录列表 /// @@ -444,25 +180,32 @@ public class CardFlipService : ApplicationService, ICardFlipService { var records = new List(); - for (int i = 1; i <= TOTAL_MAX_FLIPS; i++) + // 获取已翻牌的顺序 + var flippedOrder = task != null ? _cardFlipManager.GetFlippedOrder(task) : new List(); + var flippedNumbers = new HashSet(flippedOrder); + + // 构建记录,按照原始序号1-10排列 + for (int i = 1; i <= CardFlipManager.TOTAL_MAX_FLIPS; i++) { var record = new CardFlipRecord { FlipNumber = i, - IsFlipped = task != null && i <= task.TotalFlips, + IsFlipped = flippedNumbers.Contains(i), IsWin = false, - FlipTypeDesc = GetFlipTypeDesc(i) + FlipTypeDesc = CardFlipManager.GetFlipTypeDesc(i), + // 设置在翻牌顺序中的位置(0表示未翻,>0表示第几个翻的) + FlipOrderIndex = flippedOrder.IndexOf(i) >= 0 ? flippedOrder.IndexOf(i) + 1 : 0 }; // 设置中奖信息 - if (task != null && i <= task.TotalFlips) + if (task != null && flippedNumbers.Contains(i)) { - if (i == NINTH_FLIP && task.HasNinthReward) + if (i == 9 && task.HasNinthReward) { record.IsWin = true; record.RewardAmount = task.NinthRewardAmount; } - else if (i == TENTH_FLIP && task.HasTenthReward) + else if (i == 10 && task.HasTenthReward) { record.IsWin = true; record.RewardAmount = task.TenthRewardAmount; @@ -475,33 +218,14 @@ public class CardFlipService : ApplicationService, ICardFlipService 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) + if (status.TotalFlips >= CardFlipManager.TOTAL_MAX_FLIPS) { - return "本周翻牌次数已用完,请下周再来!"; + return "本周翻牌次数已用完,请下周再来!"; } if (status.RemainingFreeFlips > 0) @@ -516,21 +240,13 @@ public class CardFlipService : ApplicationService, ICardFlipService { if (status.TotalFlips == 8) { - return "再邀请一个人,马上中奖!"; + return "再邀请一个人,马上中奖!"; } + return $"使用邀请码可解锁{status.RemainingInviteFlips}次翻牌"; } - return "继续加油!"; - } - - /// - /// 生成随机奖励金额 - /// - private long GenerateRandomReward(long min, long max) - { - var random = new Random(); - return (long)(random.NextDouble() * (max - min) + min); + return "继续加油!"; } /// @@ -541,7 +257,7 @@ public class CardFlipService : ApplicationService, ICardFlipService var premiumPackage = new PremiumPackageAggregateRoot(userId, amount, description) { PurchaseAmount = 0, // 奖励不需要付费 - Remark = $"翻牌活动奖励:{amount / 10000}w tokens" + Remark = $"翻牌活动奖励:{amount / 10000}w tokens" }; await _premiumPackageRepository.InsertAsync(premiumPackage); @@ -549,33 +265,16 @@ public class CardFlipService : ApplicationService, ICardFlipService _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 weekStart = CardFlipManager.GetWeekStartDate(date); var weekEnd = weekStart.AddDays(6); return $"{weekStart:MM-dd} 至 {weekEnd:MM-dd}"; } #endregion -} +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/CardFlipTaskAggregateRoot.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/CardFlipTaskAggregateRoot.cs index 4b795299..a13bb813 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/CardFlipTaskAggregateRoot.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/CardFlipTaskAggregateRoot.cs @@ -27,6 +27,7 @@ public class CardFlipTaskAggregateRoot : FullAuditedAggregateRoot IsFirstFlipDone = false; HasNinthReward = false; HasTenthReward = false; + FlippedOrder = new List(); } /// @@ -90,6 +91,12 @@ public class CardFlipTaskAggregateRoot : FullAuditedAggregateRoot [SugarColumn(Length = 500, IsNullable = true)] public string? Remark { get; set; } + /// + /// 已翻牌的顺序(存储用户实际翻牌的序号列表,如[3,7,1,5]表示依次翻了3号、7号、1号、5号牌) + /// + [SugarColumn(IsJson = true, IsNullable = true)] + public List? FlippedOrder { get; set; } + /// /// 增加翻牌次数 /// diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/CardFlipManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/CardFlipManager.cs new file mode 100644 index 00000000..faa6ec16 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/CardFlipManager.cs @@ -0,0 +1,308 @@ +using Microsoft.Extensions.Logging; +using Volo.Abp.Domain.Services; +using Yi.Framework.AiHub.Domain.Entities; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace Yi.Framework.AiHub.Domain.Managers; + +/// +/// 翻牌管理器 - 负责翻牌核心业务逻辑 +/// +public class CardFlipManager : DomainService +{ + private readonly ISqlSugarRepository _cardFlipTaskRepository; + private readonly ILogger _logger; + + // 翻牌规则配置 + public const int MAX_FREE_FLIPS = 5; // 免费翻牌次数 + public const int MAX_BONUS_FLIPS = 3; // 赠送翻牌次数 + public const int MAX_INVITE_FLIPS = 2; // 邀请解锁翻牌次数 + public 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 CardFlipManager( + ISqlSugarRepository cardFlipTaskRepository, + ILogger logger) + { + _cardFlipTaskRepository = cardFlipTaskRepository; + _logger = logger; + } + + /// + /// 获取或创建本周任务 + /// + public 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; + } + + /// + /// 获取已翻牌的顺序列表 + /// + public List GetFlippedOrder(CardFlipTaskAggregateRoot task) + { + return task.FlippedOrder ?? new List(); + } + + /// + /// 执行翻牌逻辑 + /// + /// 用户ID + /// 翻牌序号 + /// 本周开始日期 + /// 翻牌结果 + public async Task ExecuteFlipAsync(Guid userId, int flipNumber, DateTime weekStart) + { + // 验证翻牌序号 + if (flipNumber < 1 || 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("本周翻牌次数已用完,请下周再来!"); + } + + // 验证该牌是否已经翻过 + var flippedOrder = GetFlippedOrder(task); + if (flippedOrder.Contains(flipNumber)) + { + throw new UserFriendlyException($"第 {flipNumber} 号牌已经翻过了!"); + } + + // 判断翻牌类型 + var flipType = DetermineFlipType(task); + + // 验证是否有足够的次数 + if (!CanUseFlipType(task, flipType)) + { + throw new UserFriendlyException(GetFlipTypeErrorMessage(flipType)); + } + + // 计算翻牌结果 + var result = CalculateFlipResult(flipNumber); + + // 记录奖励 + if (result.IsWin) + { + if (flipNumber == NINTH_FLIP) + { + task.SetNinthReward(result.RewardAmount); + } + else if (flipNumber == TENTH_FLIP) + { + task.SetTenthReward(result.RewardAmount); + } + } + + // 更新翻牌次数 + task.IncrementFlip(flipType); + + // 记录翻牌顺序 + if (task.FlippedOrder == null) + { + task.FlippedOrder = new List(); + } + task.FlippedOrder.Add(flipNumber); + + await _cardFlipTaskRepository.UpdateAsync(task); + + _logger.LogInformation($"用户 {userId} 完成第 {flipNumber} 次翻牌,中奖:{result.IsWin}"); + + return result; + } + + /// + /// 判断是否可以翻牌 + /// + public bool CanFlipCard(CardFlipTaskAggregateRoot? task) + { + if (task == null) return true; // 没有任务记录,可以开始翻牌 + + return task.TotalFlips < TOTAL_MAX_FLIPS; + } + + /// + /// 判断翻牌类型 + /// + public 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; + } + } + + /// + /// 判断是否可以使用该翻牌类型 + /// + public 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 FlipResult CalculateFlipResult(int flipNumber) + { + var result = new FlipResult + { + FlipNumber = flipNumber, + IsWin = false, + ShowDoubleRewardTip = false + }; + + // 前8次固定失败 + if (flipNumber <= 8) + { + result.IsWin = false; + result.RewardDesc = "很遗憾,未中奖"; + } + // 第9次中奖 + else if (flipNumber == NINTH_FLIP) + { + var rewardAmount = GenerateRandomReward(NINTH_MIN_REWARD, NINTH_MAX_REWARD); + result.IsWin = true; + result.RewardAmount = rewardAmount; + result.RewardDesc = $"恭喜获得尊享包 {rewardAmount / 10000}w tokens!"; + result.ShowDoubleRewardTip = true; // 显示翻倍包提示 + } + // 第10次中奖(翻倍) + else if (flipNumber == TENTH_FLIP) + { + var rewardAmount = GenerateRandomReward(TENTH_MIN_REWARD, TENTH_MAX_REWARD); + result.IsWin = true; + result.RewardAmount = rewardAmount; + result.RewardDesc = $"恭喜获得尊享包 {rewardAmount / 10000}w tokens(翻倍奖励)!"; + } + + return result; + } + + /// + /// 获取翻牌类型错误提示 + /// + private string GetFlipTypeErrorMessage(FlipType flipType) + { + return flipType switch + { + FlipType.Free => "免费翻牌次数已用完", + FlipType.Bonus => "赠送翻牌次数已用完", + FlipType.Invite => "需要使用邀请码解锁更多次数", + _ => "无法翻牌" + }; + } + + /// + /// 生成随机奖励金额 + /// + private long GenerateRandomReward(long min, long max) + { + var random = new Random(); + return (long)(random.NextDouble() * (max - min) + min); + } + + /// + /// 获取本周开始日期(周一) + /// + public static 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; + } + + /// + /// 获取翻牌类型描述 + /// + public static string GetFlipTypeDesc(int flipNumber) + { + if (flipNumber <= MAX_FREE_FLIPS) + { + return "免费"; + } + else if (flipNumber <= MAX_FREE_FLIPS + MAX_BONUS_FLIPS) + { + return "赠送"; + } + else + { + return "邀请解锁"; + } + } +} + +/// +/// 翻牌结果 +/// +public class FlipResult +{ + /// + /// 翻牌序号 + /// + public int FlipNumber { get; set; } + + /// + /// 是否中奖 + /// + public bool IsWin { get; set; } + + /// + /// 奖励金额 + /// + public long RewardAmount { get; set; } + + /// + /// 奖励描述 + /// + public string RewardDesc { get; set; } = string.Empty; + + /// + /// 是否显示翻倍奖励提示 + /// + public bool ShowDoubleRewardTip { get; set; } +} diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/InviteCodeManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/InviteCodeManager.cs new file mode 100644 index 00000000..35188763 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/InviteCodeManager.cs @@ -0,0 +1,217 @@ +using Microsoft.Extensions.Logging; +using SqlSugar; +using Volo.Abp.Domain.Services; +using Yi.Framework.AiHub.Domain.Entities; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace Yi.Framework.AiHub.Domain.Managers; + +/// +/// 邀请码管理器 - 负责邀请码核心业务逻辑 +/// +public class InviteCodeManager : DomainService +{ + private readonly ISqlSugarRepository _inviteCodeRepository; + private readonly ISqlSugarRepository _invitationRecordRepository; + private readonly ILogger _logger; + + public InviteCodeManager( + ISqlSugarRepository inviteCodeRepository, + ISqlSugarRepository invitationRecordRepository, + ILogger logger) + { + _inviteCodeRepository = inviteCodeRepository; + _invitationRecordRepository = invitationRecordRepository; + _logger = logger; + } + + /// + /// 生成用户的邀请码 + /// + /// 用户ID + /// 邀请码 + public async Task GenerateInviteCodeForUserAsync(Guid userId) + { + // 检查是否已有邀请码 + var existingCode = await _inviteCodeRepository._DbQueryable + .Where(x => x.UserId == userId) + .FirstAsync(); + + if (existingCode != null) + { + return existingCode.Code; + } + + // 生成新邀请码 + var code = GenerateUniqueInviteCode(); + var inviteCode = new InviteCodeAggregateRoot(userId, code); + await _inviteCodeRepository.InsertAsync(inviteCode); + + _logger.LogInformation($"用户 {userId} 生成邀请码 {code}"); + + return code; + } + + /// + /// 获取用户的邀请码信息 + /// + public async Task GetUserInviteCodeAsync(Guid userId) + { + return await _inviteCodeRepository._DbQueryable + .Where(x => x.UserId == userId) + .FirstAsync(); + } + + /// + /// 统计用户本周邀请人数 + /// + public async Task GetWeeklyInvitationCountAsync(Guid userId, DateTime weekStart) + { + return await _invitationRecordRepository._DbQueryable + .Where(x => x.InviterId == userId) + .Where(x => x.InvitationTime >= weekStart) + .CountAsync(); + } + + /// + /// 获取邀请历史记录 + /// + public async Task> GetInvitationHistoryAsync(Guid userId, int limit = 10) + { + return await _invitationRecordRepository._DbQueryable + .Where(x => x.InviterId == userId) + .OrderBy(x => x.InvitationTime, OrderByType.Desc) + .Take(limit) + .Select(x => new InvitationHistoryDto + { + InvitedUserName = "用户***", // 脱敏处理 + InvitationTime = x.InvitationTime + }) + .ToListAsync(); + } + + /// + /// 使用邀请码 + /// + /// 使用者ID + /// 邀请码 + /// 邀请人ID + public async Task UseInviteCodeAsync(Guid userId, string inviteCode) + { + if (string.IsNullOrWhiteSpace(inviteCode)) + { + throw new UserFriendlyException("邀请码不能为空"); + } + + // 查找邀请码 + var inviteCodeEntity = await _inviteCodeRepository._DbQueryable + .Where(x => x.Code == inviteCode) + .FirstAsync(); + + if (inviteCodeEntity == null) + { + throw new UserFriendlyException("邀请码不存在"); + } + + // 验证不能使用自己的邀请码 + if (inviteCodeEntity.UserId == userId) + { + throw new UserFriendlyException("不能使用自己的邀请码"); + } + + // 验证邀请码是否已被使用 + if (inviteCodeEntity.IsUsed) + { + throw new UserFriendlyException("该邀请码已被使用"); + } + + // 验证邀请码拥有者是否已被邀请 + if (inviteCodeEntity.IsUserInvited) + { + throw new UserFriendlyException("该用户已被邀请,邀请码无效"); + } + + // 检查当前用户是否已被邀请 + var myInviteCode = await _inviteCodeRepository._DbQueryable + .Where(x => x.UserId == userId) + .FirstAsync(); + + if (myInviteCode?.IsUserInvited == true) + { + throw new UserFriendlyException("您已使用过邀请码,无法重复使用"); + } + + // 标记邀请码为已使用 + inviteCodeEntity.MarkAsUsed(userId); + await _inviteCodeRepository.UpdateAsync(inviteCodeEntity); + + // 标记当前用户已被邀请 + if (myInviteCode == null) + { + myInviteCode = new InviteCodeAggregateRoot(userId, GenerateUniqueInviteCode()); + myInviteCode.MarkUserAsInvited(); + await _inviteCodeRepository.InsertAsync(myInviteCode); + } + else + { + myInviteCode.MarkUserAsInvited(); + await _inviteCodeRepository.UpdateAsync(myInviteCode); + } + + // 创建邀请记录 + var invitationRecord = new InvitationRecordAggregateRoot( + inviteCodeEntity.UserId, + userId, + inviteCode); + await _invitationRecordRepository.InsertAsync(invitationRecord); + + _logger.LogInformation($"用户 {userId} 使用邀请码 {inviteCode} 成功"); + + return inviteCodeEntity.UserId; + } + + /// + /// 检查用户是否已被邀请 + /// + public async Task IsUserInvitedAsync(Guid userId) + { + var inviteCode = await _inviteCodeRepository._DbQueryable + .Where(x => x.UserId == userId) + .FirstAsync(); + + return inviteCode?.IsUserInvited ?? false; + } + + /// + /// 生成唯一邀请码 + /// + private string GenerateUniqueInviteCode() + { + 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); + } +} + +/// +/// 邀请历史记录DTO +/// +public class InvitationHistoryDto +{ + /// + /// 被邀请人名称(脱敏) + /// + public string InvitedUserName { get; set; } = string.Empty; + + /// + /// 邀请时间 + /// + public DateTime InvitationTime { get; set; } +} diff --git a/Yi.Ai.Vue3/src/api/cardFlip/types.ts b/Yi.Ai.Vue3/src/api/cardFlip/types.ts index 91d452bb..4683f9a3 100644 --- a/Yi.Ai.Vue3/src/api/cardFlip/types.ts +++ b/Yi.Ai.Vue3/src/api/cardFlip/types.ts @@ -19,6 +19,7 @@ export interface CardFlipRecord { isWin: boolean; // 是否中奖 rewardAmount?: number; // 奖励金额(token数) flipTypeDesc?: string; // 翻牌类型描述 + flipOrderIndex: number; // 在翻牌顺序中的位置(1-10,表示第几个翻) } // 翻牌输入 diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue index 28fb0f38..00cd173b 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/CardFlipActivity.vue @@ -35,7 +35,10 @@ async function fetchTaskStatus() { // 翻牌 async function handleFlipCard(record: CardFlipRecord) { - if (!record.isFlipped && record.flipNumber === (taskData.value?.totalFlips || 0) + 1) { + // 检查是否可以翻牌(任何未翻过的牌都可以) + const canFlip = !record.isFlipped && (taskData.value?.canFlip || false); + + if (canFlip) { // 检查是否需要使用邀请码 if (record.flipNumber > 8 && (taskData.value?.remainingInviteFlips || 0) > 0) { const usedInviteFlips = 2 - (taskData.value?.remainingInviteFlips || 2); @@ -195,8 +198,8 @@ function getCardClass(record: CardFlipRecord): string[] { classes.push('card-flipping'); } - // 可点击的卡片 - if (!record.isFlipped && record.flipNumber === (taskData.value?.totalFlips || 0) + 1) { + // 可点击的卡片(任何未翻过的牌都可以点击) + if (!record.isFlipped && (taskData.value?.canFlip || false)) { classes.push('card-clickable'); } else if (!record.isFlipped) { classes.push('card-locked'); From acb359ec334009f51a1c883d2499ee3eef895883 Mon Sep 17 00:00:00 2001 From: chenchun Date: Mon, 27 Oct 2025 22:01:57 +0800 Subject: [PATCH 03/21] =?UTF-8?q?style:=20=E5=88=A0=E9=99=A4=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E7=9A=84=20SqlSugar=20InitTables=20=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E5=B9=B6=E8=B0=83=E6=95=B4=E6=B3=A8=E9=87=8A=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 Yi.Abp.Web/YiAbpWebModule.cs 中移除两行多余的注释,调整剩余注释的空格格式,清理代码注释,不影响程序逻辑。 --- Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs index b53f7c11..89a44565 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Web/YiAbpWebModule.cs @@ -352,9 +352,8 @@ namespace Yi.Abp.Web var app = context.GetApplicationBuilder(); app.UseRouting(); - // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); - // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); - // app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); + //app.ApplicationServices.GetRequiredService().SqlSugarClient.CodeFirst.InitTables(); + //跨域 app.UseCors(DefaultCorsPolicyName); From c6425ca206c67b433fdda004197b4263c782c567 Mon Sep 17 00:00:00 2001 From: chenchun Date: Tue, 28 Oct 2025 16:02:01 +0800 Subject: [PATCH 04/21] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E5=BC=82=E5=B8=B8=E6=8F=90=E7=A4=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将抛出异常的消息从 "OpenAI对话异常{StatusCode}" 修改为更详细的中文提示,包含 StatusCode 与 Response 内容,便于排查。未改变逻辑,仅调整异常文本。 --- .../Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs index 1552af59..f46b2883 100644 --- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs +++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/AiGateWay/Impl/ThorClaude/Chats/AnthropicChatCompletionsService.cs @@ -80,7 +80,7 @@ public class AnthropicChatCompletionsService( options.Endpoint, response.StatusCode, error); - throw new Exception("OpenAI对话异常" + response.StatusCode.ToString()); + throw new Exception( $"恭喜你运气爆棚遇到了错误,尊享包对话异常:StatusCode【{response.StatusCode}】,Response【{error}】"); } var value = From dd3f6325bb15b4008db60ed8272626b67604db1f Mon Sep 17 00:00:00 2001 From: Gsh <15170702455@163.com> Date: Wed, 29 Oct 2025 00:17:36 +0800 Subject: [PATCH 05/21] =?UTF-8?q?fix:=20=E4=B8=AA=E4=BA=BA=E4=B8=AD?= =?UTF-8?q?=E5=BF=83=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../userPersonalCenter/NavDialog.vue | 70 +++- .../components/APIKeyManagement.vue | 9 +- .../components/CardFlipActivity.vue | 381 +++++++++++------- .../components/DailyTask.vue | 2 + .../components/PremiumService.vue | 2 + .../components/RechargeLog.vue | 18 +- .../components/SupportModelList.vue | 2 + .../components/UsageStatistics.vue | 11 +- .../components/UserManagement.vue | 2 + .../components/Header/components/Avatar.vue | 21 +- 10 files changed, 366 insertions(+), 152 deletions(-) diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/NavDialog.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/NavDialog.vue index 99b8b485..dba47a6f 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/NavDialog.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/NavDialog.vue @@ -1,4 +1,5 @@ diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/DailyTask.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/DailyTask.vue index f9b2341f..c459944a 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/DailyTask.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/DailyTask.vue @@ -189,6 +189,8 @@ function getProgressColor(task: DailyTaskItem): string { .daily-task-container { padding: 20px; min-height: 400px; + height: 100%; + overflow-y: auto; } .task-header { diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/PremiumService.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/PremiumService.vue index 9e94f3bf..7032219d 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/PremiumService.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/PremiumService.vue @@ -273,6 +273,8 @@ function onProductPackage() { .premium-service { padding: 20px; position: relative; + height: 100%; + overflow-y: auto; } .header { diff --git a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/RechargeLog.vue b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/RechargeLog.vue index 15f8c28c..2632751e 100644 --- a/Yi.Ai.Vue3/src/components/userPersonalCenter/components/RechargeLog.vue +++ b/Yi.Ai.Vue3/src/components/userPersonalCenter/components/RechargeLog.vue @@ -104,6 +104,11 @@ function contactCustomerService() { innerVisibleContact.value = !innerVisibleContact.value; } +// 暴露方法给父组件使用 +defineExpose({ + contactCustomerService, +}); + // 过滤和排序后的数据 const filteredData = computed(() => { let data = [...logData.value]; @@ -253,7 +258,6 @@ onMounted(() => { { @@ -273,7 +277,7 @@ onMounted(() => { show-overflow-tooltip prop="rechargeAmount" label="金额(元)" - width="110" + min-width="110" sortable="custom" > --> - +