- CardFlipTaskAggregateRoot.cs
- 用 WinRecords(Dictionary<int,long>) 替代原先第8/9/10次的各自字段,且以 JSON 存库(SugarColumn IsJson)。
- 构造函数初始化 WinRecords。
- 新增 SetWinReward(int flipCount, long amount) 统一记录中奖。
- CardFlipService.cs
- 读取并展示 WinRecords,按翻牌顺序映射中奖信息(不再依赖单独的第8/9/10字段)。
- CardFlipManager.cs
- 重构中奖逻辑:
- 前7次翻牌改为 50% 概率可中奖,奖励范围 1w–3w(新增配置常量 FREE_*)。
- 统一通过 SetWinReward 记录任意次的中奖金额。
- GenerateRandomReward 根据最小值自动选单位(1w 或 100w)。
- 邀请类型翻牌校验由“仅统计被填写次数”改为“统计本周作为邀请人或被邀请人的邀请记录数量”(双方都计入),并据此判断是否可解锁邀请翻牌次数。
- InviteCodeManager.cs
- 使用邀请码时:
- 验证规则调整:一个账号只能填写别人的邀请码一次(hasUsedOthersCode 检查)。
- 邀请记录的语义变化:当使用邀请码时,邀请记录同时代表邀请人和被邀请人各增加一次翻牌机会。
- 不再将邀请码标记为单次已用;改为增加 UsedCount(一个邀请码可以被多人使用)。
- 优化日志与提示信息。
- InviteCodeAggregateRoot.cs
- 移除 IsUsed、UsedTime、UsedByUserId、MarkAsUsed 等单次使用相关字段/方法。
- 保留 IsUserInvited(被邀请后不能再作为被邀请者使用)和 UsedCount(统计多人使用次数)。
注意事项
- 这是数据结构与业务逻辑的变更,数据库表结构发生变化(新增 WinRecords JSON 字段,移除若干字段)。上线前请准备相应的迁移脚本或数据迁移方案,确保历史中奖数据平滑迁移到 WinRecords。
- 变更会影响相关单元/集成测试、前端展示字段,需同步更新对应测试与界面展示逻辑。
277 lines
9.5 KiB
C#
277 lines
9.5 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.Extensions.Logging;
|
||
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;
|
||
|
||
/// <summary>
|
||
/// 翻牌服务 - 应用层组合服务
|
||
/// </summary>
|
||
[Authorize]
|
||
public class CardFlipService : ApplicationService, ICardFlipService
|
||
{
|
||
private readonly CardFlipManager _cardFlipManager;
|
||
private readonly InviteCodeManager _inviteCodeManager;
|
||
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||
private readonly ILogger<CardFlipService> _logger;
|
||
|
||
public CardFlipService(
|
||
CardFlipManager cardFlipManager,
|
||
InviteCodeManager inviteCodeManager,
|
||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
||
ILogger<CardFlipService> logger)
|
||
{
|
||
_cardFlipManager = cardFlipManager;
|
||
_inviteCodeManager = inviteCodeManager;
|
||
_premiumPackageRepository = premiumPackageRepository;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取本周翻牌任务状态
|
||
/// </summary>
|
||
public async Task<CardFlipStatusOutput> GetWeeklyTaskStatusAsync()
|
||
{
|
||
var userId = CurrentUser.GetId();
|
||
var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now);
|
||
|
||
// 获取本周任务
|
||
var task = await _cardFlipManager.GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: false);
|
||
|
||
// 获取邀请码信息
|
||
var inviteCode = await _inviteCodeManager.GetUserInviteCodeAsync(userId);
|
||
|
||
// 统计本周邀请人数
|
||
var invitedCount = await _inviteCodeManager.GetWeeklyInvitationCountAsync(userId, weekStart);
|
||
|
||
// 检查用户是否已被邀请
|
||
var isInvited = await _inviteCodeManager.IsUserInvitedAsync(userId);
|
||
|
||
var output = new CardFlipStatusOutput
|
||
{
|
||
TotalFlips = task?.TotalFlips ?? 0,
|
||
RemainingFreeFlips = CardFlipManager.MAX_FREE_FLIPS - (task?.FreeFlipsUsed ?? 0),
|
||
RemainingBonusFlips = 0, // 已废弃
|
||
RemainingInviteFlips = CardFlipManager.MAX_INVITE_FLIPS - (task?.InviteFlipsUsed ?? 0),
|
||
CanFlip = _cardFlipManager.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 = CardFlipManager.GetWeekStartDate(DateTime.Now);
|
||
|
||
// 执行翻牌逻辑(由Manager处理验证和翻牌)
|
||
var result = await _cardFlipManager.ExecuteFlipAsync(userId, input.FlipNumber, weekStart);
|
||
|
||
// 如果中奖,发放奖励
|
||
if (result.IsWin)
|
||
{
|
||
await GrantRewardAsync(userId, result.RewardAmount, $"翻牌活动第{input.FlipNumber}次中奖");
|
||
}
|
||
|
||
// 构建输出
|
||
var output = new FlipCardOutput
|
||
{
|
||
FlipNumber = result.FlipNumber,
|
||
IsWin = result.IsWin,
|
||
RewardAmount = result.RewardAmount,
|
||
RewardDesc = result.RewardDesc,
|
||
RemainingFlips = CardFlipManager.TOTAL_MAX_FLIPS - input.FlipNumber
|
||
};
|
||
|
||
return output;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用邀请码解锁翻牌次数
|
||
/// </summary>
|
||
public async Task UseInviteCodeAsync(UseInviteCodeInput input)
|
||
{
|
||
var userId = CurrentUser.GetId();
|
||
var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now);
|
||
|
||
// 获取本周任务
|
||
var task = await _cardFlipManager.GetOrCreateWeeklyTaskAsync(userId, weekStart, createIfNotExists: true);
|
||
|
||
// 验证是否已经使用了所有邀请解锁次数
|
||
if (task.InviteFlipsUsed >= CardFlipManager.MAX_INVITE_FLIPS)
|
||
{
|
||
throw new UserFriendlyException("本周邀请解锁次数已用完");
|
||
}
|
||
|
||
// 使用邀请码(由Manager处理验证和邀请逻辑)
|
||
await _inviteCodeManager.UseInviteCodeAsync(userId, input.InviteCode);
|
||
|
||
_logger.LogInformation($"用户 {userId} 使用邀请码 {input.InviteCode} 解锁翻牌次数成功");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取我的邀请码信息
|
||
/// </summary>
|
||
public async Task<InviteCodeOutput> GetMyInviteCodeAsync()
|
||
{
|
||
var userId = CurrentUser.GetId();
|
||
var weekStart = CardFlipManager.GetWeekStartDate(DateTime.Now);
|
||
|
||
// 获取我的邀请码
|
||
var inviteCode = await _inviteCodeManager.GetUserInviteCodeAsync(userId);
|
||
|
||
// 统计本周邀请人数
|
||
var invitedCount = await _inviteCodeManager.GetWeeklyInvitationCountAsync(userId, weekStart);
|
||
|
||
// 获取邀请历史
|
||
var invitationHistory = await _inviteCodeManager.GetInvitationHistoryAsync(userId, 10);
|
||
|
||
return new InviteCodeOutput
|
||
{
|
||
MyInviteCode = inviteCode?.Code,
|
||
InvitedCount = invitedCount,
|
||
IsInvited = inviteCode?.IsUserInvited ?? false,
|
||
InvitationHistory = invitationHistory.Select(x => new InvitationHistoryItem
|
||
{
|
||
InvitedUserName = x.InvitedUserName,
|
||
InvitationTime = x.InvitationTime,
|
||
WeekDescription = GetWeekDescription(x.InvitationTime)
|
||
}).ToList()
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成我的邀请码
|
||
/// </summary>
|
||
public async Task<string> GenerateMyInviteCodeAsync()
|
||
{
|
||
var userId = CurrentUser.GetId();
|
||
|
||
// 生成邀请码(由Manager处理)
|
||
var code = await _inviteCodeManager.GenerateInviteCodeForUserAsync(userId);
|
||
|
||
return code;
|
||
}
|
||
|
||
#region 私有辅助方法
|
||
|
||
/// <summary>
|
||
/// 构建翻牌记录列表
|
||
/// </summary>
|
||
private List<CardFlipRecord> BuildFlipRecords(CardFlipTaskAggregateRoot? task)
|
||
{
|
||
var records = new List<CardFlipRecord>();
|
||
|
||
// 获取已翻牌的顺序
|
||
var flippedOrder = task != null ? _cardFlipManager.GetFlippedOrder(task) : new List<int>();
|
||
var flippedNumbers = new HashSet<int>(flippedOrder);
|
||
|
||
// 获取中奖记录
|
||
var winRecords = task?.WinRecords ?? new Dictionary<int, long>();
|
||
|
||
// 构建记录,按照原始序号1-10排列
|
||
for (int i = 1; i <= CardFlipManager.TOTAL_MAX_FLIPS; i++)
|
||
{
|
||
var record = new CardFlipRecord
|
||
{
|
||
FlipNumber = i,
|
||
IsFlipped = flippedNumbers.Contains(i),
|
||
IsWin = false,
|
||
FlipTypeDesc = CardFlipManager.GetFlipTypeDesc(i),
|
||
// 设置在翻牌顺序中的位置(0表示未翻,>0表示第几个翻的)
|
||
FlipOrderIndex = flippedOrder.IndexOf(i) >= 0 ? flippedOrder.IndexOf(i) + 1 : 0
|
||
};
|
||
|
||
// 设置中奖信息
|
||
// 判断这张卡是第几次翻的
|
||
if (task != null && flippedNumbers.Contains(i))
|
||
{
|
||
var flipOrderIndex = flippedOrder.IndexOf(i) + 1; // 第几次翻的(1-based)
|
||
|
||
// 检查这次翻牌是否中奖
|
||
if (winRecords.TryGetValue(flipOrderIndex, out var rewardAmount))
|
||
{
|
||
record.IsWin = true;
|
||
record.RewardAmount = rewardAmount;
|
||
}
|
||
}
|
||
|
||
records.Add(record);
|
||
}
|
||
|
||
return records;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成下次翻牌提示
|
||
/// </summary>
|
||
private string GenerateNextFlipTip(CardFlipStatusOutput status)
|
||
{
|
||
if (status.TotalFlips >= CardFlipManager.TOTAL_MAX_FLIPS)
|
||
{
|
||
return "本周翻牌次数已用完,请下周再来!";
|
||
}
|
||
|
||
if (status.RemainingFreeFlips > 0)
|
||
{
|
||
return $"本周您还有{status.RemainingFreeFlips}次免费翻牌机会";
|
||
}
|
||
else if (status.RemainingInviteFlips > 0)
|
||
{
|
||
if (status.TotalFlips >= 7)
|
||
{
|
||
return $"本周使用他人邀请码可解锁{status.RemainingInviteFlips}次翻牌,且必中大奖!每次中奖最大额度将翻倍!";
|
||
}
|
||
|
||
return $"本周使用他人邀请码可解锁{status.RemainingInviteFlips}次翻牌,必中大奖!每次中奖最大额度将翻倍!";
|
||
}
|
||
|
||
return "继续加油!";
|
||
}
|
||
|
||
/// <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 GetWeekDescription(DateTime date)
|
||
{
|
||
var weekStart = CardFlipManager.GetWeekStartDate(date);
|
||
var weekEnd = weekStart.AddDays(6);
|
||
|
||
return $"{weekStart:MM-dd} 至 {weekEnd:MM-dd}";
|
||
}
|
||
|
||
#endregion
|
||
} |