Files
Yi.Framework/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/CardFlipService.cs
chenchun aec90ec9d6 feat: 新增翻牌活动入口与全局组件声明
- 在 Header Avatar 菜单新增翻牌活动(cardFlip)入口,并添加对应插槽 <card-flip-activity/>
- 在 types/components.d.ts 中添加 CardFlipActivity 与 ElCollapseTransition 类型声明
- 在 .eslintrc-auto-import.json 中新增 ElMessage 与 ElMessageBox 自动导入
- 从 import_meta.d.ts 中移除 VITE_BUILD_COMPRESS 环境声明
- 在 YiAbpWebModule.cs 中添加相关 using 并保留数据库建表初始化的注释(CodeFirst.InitTables)
2025-10-23 21:58:47 +08:00

582 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using 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;
/// <summary>
/// 翻牌服务
/// </summary>
[Authorize]
public class CardFlipService : ApplicationService, ICardFlipService
{
private readonly ISqlSugarRepository<CardFlipTaskAggregateRoot> _cardFlipTaskRepository;
private readonly ISqlSugarRepository<InviteCodeAggregateRoot> _inviteCodeRepository;
private readonly ISqlSugarRepository<InvitationRecordAggregateRoot> _invitationRecordRepository;
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
private readonly ILogger<CardFlipService> _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<CardFlipTaskAggregateRoot> cardFlipTaskRepository,
ISqlSugarRepository<InviteCodeAggregateRoot> inviteCodeRepository,
ISqlSugarRepository<InvitationRecordAggregateRoot> invitationRecordRepository,
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
ILogger<CardFlipService> logger)
{
_cardFlipTaskRepository = cardFlipTaskRepository;
_inviteCodeRepository = inviteCodeRepository;
_invitationRecordRepository = invitationRecordRepository;
_premiumPackageRepository = premiumPackageRepository;
_logger = logger;
}
/// <summary>
/// 获取本周翻牌任务状态
/// </summary>
public async Task<CardFlipStatusOutput> 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;
}
/// <summary>
/// 翻牌
/// </summary>
public async Task<FlipCardOutput> 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;
}
/// <summary>
/// 使用邀请码解锁翻牌次数
/// </summary>
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} 成功");
}
/// <summary>
/// 获取我的邀请码信息
/// </summary>
public async Task<InviteCodeOutput> 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
};
}
/// <summary>
/// 生成我的邀请码
/// </summary>
public async Task<string> 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
/// <summary>
/// 获取或创建本周任务
/// </summary>
private async Task<CardFlipTaskAggregateRoot> 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;
}
/// <summary>
/// 获取本周开始日期(周一)
/// </summary>
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;
}
/// <summary>
/// 判断是否可以翻牌
/// </summary>
private bool CanFlipCard(CardFlipTaskAggregateRoot? task)
{
if (task == null) return true; // 没有任务记录,可以开始翻牌
return task.TotalFlips < TOTAL_MAX_FLIPS;
}
/// <summary>
/// 判断翻牌类型
/// </summary>
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;
}
}
/// <summary>
/// 判断是否可以使用该翻牌类型
/// </summary>
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
};
}
/// <summary>
/// 获取翻牌类型错误提示
/// </summary>
private string GetFlipTypeErrorMessage(FlipType flipType)
{
return flipType switch
{
FlipType.Free => "免费翻牌次数已用完",
FlipType.Bonus => "赠送翻牌次数已用完",
FlipType.Invite => "需要使用邀请码解锁更多次数",
_ => "无法翻牌"
};
}
/// <summary>
/// 构建翻牌记录列表
/// </summary>
private List<CardFlipRecord> BuildFlipRecords(CardFlipTaskAggregateRoot? task)
{
var records = new List<CardFlipRecord>();
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;
}
/// <summary>
/// 获取翻牌类型描述
/// </summary>
private string GetFlipTypeDesc(int flipNumber)
{
if (flipNumber <= MAX_FREE_FLIPS)
{
return "免费";
}
else if (flipNumber <= MAX_FREE_FLIPS + MAX_BONUS_FLIPS)
{
return "赠送";
}
else
{
return "邀请解锁";
}
}
/// <summary>
/// 生成下次翻牌提示
/// </summary>
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 "继续加油!";
}
/// <summary>
/// 生成随机奖励金额
/// </summary>
private long GenerateRandomReward(long min, long max)
{
var random = new Random();
return (long)(random.NextDouble() * (max - min) + min);
}
/// <summary>
/// 发放奖励
/// </summary>
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");
}
/// <summary>
/// 生成邀请码
/// </summary>
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);
}
/// <summary>
/// 获取周描述
/// </summary>
private string GetWeekDescription(DateTime date)
{
var weekStart = GetWeekStartDate(date);
var weekEnd = weekStart.AddDays(6);
return $"{weekStart:MM-dd} 至 {weekEnd:MM-dd}";
}
#endregion
}