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');