200 lines
7.6 KiB
C#
200 lines
7.6 KiB
C#
using Medallion.Threading;
|
||
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.DailyTask;
|
||
using Yi.Framework.AiHub.Domain.Entities;
|
||
using Yi.Framework.AiHub.Domain.Entities.Chat;
|
||
using Yi.Framework.AiHub.Domain.Entities.Model;
|
||
using Yi.Framework.AiHub.Domain.Extensions;
|
||
using Yi.Framework.AiHub.Domain.Managers;
|
||
using Yi.Framework.AiHub.Domain.Shared.Consts;
|
||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||
|
||
namespace Yi.Framework.AiHub.Application.Services;
|
||
|
||
/// <summary>
|
||
/// 每日任务服务
|
||
/// </summary>
|
||
[Authorize]
|
||
public class DailyTaskService : ApplicationService
|
||
{
|
||
private readonly ISqlSugarRepository<DailyTaskRewardRecordAggregateRoot> _dailyTaskRepository;
|
||
private readonly ISqlSugarRepository<MessageAggregateRoot> _messageRepository;
|
||
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
|
||
private readonly ILogger<DailyTaskService> _logger;
|
||
private IDistributedLockProvider DistributedLock => LazyServiceProvider.LazyGetRequiredService<IDistributedLockProvider>();
|
||
private readonly ISqlSugarRepository<AiModelEntity> _aiModelRepository;
|
||
// 任务配置
|
||
private readonly Dictionary<int, (long RequiredTokens, long RewardTokens, string Name, string Description)>
|
||
_taskConfigs = new()
|
||
{
|
||
{ 1, (10000000, 1000000, "尊享包1000w token任务", "累积使用尊享包 1000w token") }, // 1000w消耗 -> 100w奖励
|
||
{ 2, (30000000, 2000000, "尊享包3000w token任务", "累积使用尊享包 3000w token") } // 3000w消耗 -> 200w奖励
|
||
};
|
||
|
||
public DailyTaskService(
|
||
ISqlSugarRepository<DailyTaskRewardRecordAggregateRoot> dailyTaskRepository,
|
||
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
|
||
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
|
||
ILogger<DailyTaskService> logger, ISqlSugarRepository<AiModelEntity> aiModelRepository)
|
||
{
|
||
_dailyTaskRepository = dailyTaskRepository;
|
||
_messageRepository = messageRepository;
|
||
_premiumPackageRepository = premiumPackageRepository;
|
||
_logger = logger;
|
||
_aiModelRepository = aiModelRepository;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取今日任务状态
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<DailyTaskStatusOutput> GetTodayTaskStatusAsync()
|
||
{
|
||
var userId = CurrentUser.GetId();
|
||
var today = DateTime.Today;
|
||
|
||
// 1. 统计今日尊享包Token消耗量
|
||
var todayConsumed = await GetTodayPremiumTokenConsumptionAsync(userId, today);
|
||
|
||
// 2. 查询今日已领取的任务
|
||
var claimedTasks = await _dailyTaskRepository._DbQueryable
|
||
.Where(x => x.UserId == userId && x.TaskDate == today)
|
||
.Select(x => new { x.TaskLevel, x.IsRewarded })
|
||
.ToListAsync();
|
||
|
||
// 3. 构建任务列表
|
||
var tasks = new List<DailyTaskItem>();
|
||
foreach (var (level, config) in _taskConfigs)
|
||
{
|
||
var claimed = claimedTasks.FirstOrDefault(x => x.TaskLevel == level);
|
||
int status;
|
||
|
||
if (claimed != null && claimed.IsRewarded)
|
||
{
|
||
status = 2; // 已领取
|
||
}
|
||
else if (todayConsumed >= config.RequiredTokens)
|
||
{
|
||
status = 1; // 可领取
|
||
}
|
||
else
|
||
{
|
||
status = 0; // 未完成
|
||
}
|
||
|
||
var progress = todayConsumed >= config.RequiredTokens
|
||
? 100
|
||
: Math.Round((decimal)todayConsumed / config.RequiredTokens * 100, 2);
|
||
|
||
tasks.Add(new DailyTaskItem
|
||
{
|
||
Level = level,
|
||
Name = config.Name,
|
||
Description = config.Description,
|
||
RequiredTokens = config.RequiredTokens,
|
||
RewardTokens = config.RewardTokens,
|
||
Status = status,
|
||
Progress = progress
|
||
});
|
||
}
|
||
|
||
return new DailyTaskStatusOutput
|
||
{
|
||
TodayConsumedTokens = todayConsumed,
|
||
Tasks = tasks
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 领取任务奖励
|
||
/// </summary>
|
||
/// <param name="input"></param>
|
||
/// <returns></returns>
|
||
public async Task ClaimTaskRewardAsync(ClaimTaskRewardInput input)
|
||
{
|
||
var userId = CurrentUser.GetId();
|
||
//自旋等待,防抖
|
||
await using var handle =
|
||
await DistributedLock.AcquireLockAsync($"Yi:AiHub:ClaimTaskRewardLock:{userId}");
|
||
|
||
|
||
var today = DateTime.Today;
|
||
|
||
// 1. 验证任务等级
|
||
if (!_taskConfigs.TryGetValue(input.TaskLevel, out var taskConfig))
|
||
{
|
||
throw new UserFriendlyException($"无效的任务等级: {input.TaskLevel}");
|
||
}
|
||
|
||
// 2. 检查是否已领取
|
||
var existingRecord = await _dailyTaskRepository._DbQueryable
|
||
.Where(x => x.UserId == userId && x.TaskDate == today && x.TaskLevel == input.TaskLevel)
|
||
.FirstAsync();
|
||
|
||
if (existingRecord != null)
|
||
{
|
||
throw new UserFriendlyException("今日该任务奖励已领取,请明天再来!");
|
||
}
|
||
|
||
// 3. 验证今日Token消耗是否达标
|
||
var todayConsumed = await GetTodayPremiumTokenConsumptionAsync(userId, today);
|
||
if (todayConsumed < taskConfig.RequiredTokens)
|
||
{
|
||
throw new UserFriendlyException(
|
||
$"Token消耗未达标!需要 {taskConfig.RequiredTokens / 10000}w,当前 {todayConsumed / 10000}w");
|
||
}
|
||
|
||
// 4. 创建奖励包(使用 PremiumPackageManager)
|
||
|
||
var premiumPackage =
|
||
new PremiumPackageAggregateRoot(userId, taskConfig.RewardTokens, $"每日任务:{taskConfig.Name}")
|
||
{
|
||
PurchaseAmount = 0, // 奖励不需要付费
|
||
Remark = $"{today:yyyy-MM-dd} 每日任务奖励"
|
||
};
|
||
|
||
await _premiumPackageRepository.InsertAsync(premiumPackage);
|
||
|
||
// 5. 记录领取记录
|
||
var record = new DailyTaskRewardRecordAggregateRoot(userId, input.TaskLevel, today, taskConfig.RewardTokens)
|
||
{
|
||
Remark = $"完成任务{input.TaskLevel},名称:{taskConfig.Name},消耗 {todayConsumed / 10000}w token"
|
||
};
|
||
|
||
await _dailyTaskRepository.InsertAsync(record);
|
||
|
||
_logger.LogInformation(
|
||
$"用户 {userId} 领取每日任务 {input.TaskLevel} 奖励成功,获得 {taskConfig.RewardTokens / 10000}w tokens");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取今日尊享包Token消耗量
|
||
/// </summary>
|
||
/// <param name="userId">用户ID</param>
|
||
/// <param name="today">今日日期</param>
|
||
/// <returns>消耗的Token总数</returns>
|
||
private async Task<long> GetTodayPremiumTokenConsumptionAsync(Guid userId, DateTime today)
|
||
{
|
||
var tomorrow = today.AddDays(1);
|
||
|
||
// 查询今日所有使用尊享包模型的消息(role=system 表示消耗)
|
||
// 先获取所有尊享模型的ModelId列表
|
||
var premiumModelIds = await _aiModelRepository._DbQueryable
|
||
.Where(x => x.IsPremium)
|
||
.Select(x => x.ModelId)
|
||
.ToListAsync();
|
||
|
||
var totalTokens = await _messageRepository._DbQueryable
|
||
.Where(x => x.UserId == userId)
|
||
.Where(x => x.Role == "system") // system角色表示实际消耗
|
||
.Where(x => premiumModelIds.Contains(x.ModelId)) // 尊享包模型
|
||
.Where(x => x.CreationTime >= today && x.CreationTime < tomorrow)
|
||
.SumAsync(x => x.TokenUsage.TotalTokenCount);
|
||
|
||
return totalTokens;
|
||
}
|
||
} |