feat: 引入基于 Redis 的每日任务配置缓存

将每日任务配置从硬编码字典改为通过 IDistributedCache 从 Redis 获取
新增默认任务配置作为兜底,在缓存不存在时自动写入
统一任务配置读取逻辑,支持后续动态调整任务等级与奖励
不影响现有任务流程与业务规则,仅增强配置灵活性
This commit is contained in:
ccnetcore
2026-01-06 22:13:18 +08:00
parent 6053899516
commit e680ac4cac
2 changed files with 97 additions and 24 deletions

View File

@@ -0,0 +1,43 @@
namespace Yi.Framework.AiHub.Application.Contracts.Dtos.DailyTask;
/// <summary>
/// 每日任务配置缓存DTO
/// </summary>
public class DailyTaskConfigCacheDto
{
/// <summary>
/// 任务配置列表
/// </summary>
public List<DailyTaskConfigItem> Tasks { get; set; } = new();
}
/// <summary>
/// 每日任务配置项
/// </summary>
public class DailyTaskConfigItem
{
/// <summary>
/// 任务等级
/// </summary>
public int Level { get; set; }
/// <summary>
/// 需要消耗的Token数量
/// </summary>
public long RequiredTokens { get; set; }
/// <summary>
/// 奖励的Token数量
/// </summary>
public long RewardTokens { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// 任务描述
/// </summary>
public string Description { get; set; } = string.Empty;
}

View File

@@ -1,8 +1,10 @@
using Medallion.Threading;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using SqlSugar;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Abp.Users;
using Yi.Framework.AiHub.Application.Contracts.Dtos.DailyTask;
using Yi.Framework.AiHub.Domain.Entities;
@@ -25,27 +27,33 @@ public class DailyTaskService : ApplicationService
private readonly ISqlSugarRepository<MessageAggregateRoot> _messageRepository;
private readonly ISqlSugarRepository<PremiumPackageAggregateRoot> _premiumPackageRepository;
private readonly ILogger<DailyTaskService> _logger;
private readonly IDistributedCache<DailyTaskConfigCacheDto> _taskConfigCache;
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奖励
};
private const string TaskConfigCacheKey = "AiHub:DailyTaskConfig";
// 默认任务配置当Redis中没有配置时使用
private static readonly List<DailyTaskConfigItem> DefaultTaskConfigs = new()
{
new DailyTaskConfigItem { Level = 1, RequiredTokens = 10000000, RewardTokens = 1000000, Name = "尊享包1000w token任务", Description = "累积使用尊享包 1000w token" },
new DailyTaskConfigItem { Level = 2, RequiredTokens = 30000000, RewardTokens = 2000000, Name = "尊享包3000w token任务", Description = "累积使用尊享包 3000w token" }
};
public DailyTaskService(
ISqlSugarRepository<DailyTaskRewardRecordAggregateRoot> dailyTaskRepository,
ISqlSugarRepository<MessageAggregateRoot> messageRepository,
ISqlSugarRepository<PremiumPackageAggregateRoot> premiumPackageRepository,
ILogger<DailyTaskService> logger, ISqlSugarRepository<AiModelEntity> aiModelRepository)
ILogger<DailyTaskService> logger,
ISqlSugarRepository<AiModelEntity> aiModelRepository,
IDistributedCache<DailyTaskConfigCacheDto> taskConfigCache)
{
_dailyTaskRepository = dailyTaskRepository;
_messageRepository = messageRepository;
_premiumPackageRepository = premiumPackageRepository;
_logger = logger;
_aiModelRepository = aiModelRepository;
_taskConfigCache = taskConfigCache;
}
/// <summary>
@@ -57,20 +65,23 @@ public class DailyTaskService : ApplicationService
var userId = CurrentUser.GetId();
var today = DateTime.Today;
// 1. 统计今日尊享包Token消耗量
// 1. 获取任务配置
var taskConfigs = await GetTaskConfigsAsync();
// 2. 统计今日尊享包Token消耗量
var todayConsumed = await GetTodayPremiumTokenConsumptionAsync(userId, today);
// 2. 查询今日已领取的任务
// 3. 查询今日已领取的任务
var claimedTasks = await _dailyTaskRepository._DbQueryable
.Where(x => x.UserId == userId && x.TaskDate == today)
.Select(x => new { x.TaskLevel, x.IsRewarded })
.ToListAsync();
// 3. 构建任务列表
// 4. 构建任务列表
var tasks = new List<DailyTaskItem>();
foreach (var (level, config) in _taskConfigs)
foreach (var config in taskConfigs)
{
var claimed = claimedTasks.FirstOrDefault(x => x.TaskLevel == level);
var claimed = claimedTasks.FirstOrDefault(x => x.TaskLevel == config.Level);
int status;
if (claimed != null && claimed.IsRewarded)
@@ -92,7 +103,7 @@ public class DailyTaskService : ApplicationService
tasks.Add(new DailyTaskItem
{
Level = level,
Level = config.Level,
Name = config.Name,
Description = config.Description,
RequiredTokens = config.RequiredTokens,
@@ -120,17 +131,20 @@ public class DailyTaskService : ApplicationService
//自旋等待,防抖
await using var handle =
await DistributedLock.AcquireLockAsync($"Yi:AiHub:ClaimTaskRewardLock:{userId}");
var today = DateTime.Today;
// 1. 验证任务等级
if (!_taskConfigs.TryGetValue(input.TaskLevel, out var taskConfig))
// 1. 获取任务配置
var taskConfigs = await GetTaskConfigsAsync();
var taskConfig = taskConfigs.FirstOrDefault(x => x.Level == input.TaskLevel);
// 2. 验证任务等级
if (taskConfig == null)
{
throw new UserFriendlyException($"无效的任务等级: {input.TaskLevel}");
}
// 2. 检查是否已领取
// 3. 检查是否已领取
var existingRecord = await _dailyTaskRepository._DbQueryable
.Where(x => x.UserId == userId && x.TaskDate == today && x.TaskLevel == input.TaskLevel)
.FirstAsync();
@@ -140,7 +154,7 @@ public class DailyTaskService : ApplicationService
throw new UserFriendlyException("今日该任务奖励已领取,请明天再来!");
}
// 3. 验证今日Token消耗是否达标
// 4. 验证今日Token消耗是否达标
var todayConsumed = await GetTodayPremiumTokenConsumptionAsync(userId, today);
if (todayConsumed < taskConfig.RequiredTokens)
{
@@ -148,18 +162,17 @@ public class DailyTaskService : ApplicationService
$"Token消耗未达标需要 {taskConfig.RequiredTokens / 10000}w当前 {todayConsumed / 10000}w");
}
// 4. 创建奖励包(使用 PremiumPackageManager
// 5. 创建奖励包
var premiumPackage =
new PremiumPackageAggregateRoot(userId, taskConfig.RewardTokens, $"每日任务:{taskConfig.Name}")
{
PurchaseAmount = 0, // 奖励不需要付费
PurchaseAmount = 0,
Remark = $"{today:yyyy-MM-dd} 每日任务奖励"
};
await _premiumPackageRepository.InsertAsync(premiumPackage);
// 5. 记录领取记录
// 6. 记录领取记录
var record = new DailyTaskRewardRecordAggregateRoot(userId, input.TaskLevel, today, taskConfig.RewardTokens)
{
Remark = $"完成任务{input.TaskLevel},名称:{taskConfig.Name},消耗 {todayConsumed / 10000}w token"
@@ -197,4 +210,21 @@ public class DailyTaskService : ApplicationService
return totalTokens;
}
/// <summary>
/// 从Redis获取任务配置如果不存在则写入默认配置
/// </summary>
private async Task<List<DailyTaskConfigItem>> GetTaskConfigsAsync()
{
var cacheData = await _taskConfigCache.GetOrAddAsync(
TaskConfigCacheKey,
() => Task.FromResult(new DailyTaskConfigCacheDto { Tasks = DefaultTaskConfigs }),
() => new DistributedCacheEntryOptions
{
// 不设置过期时间,永久缓存,需要手动更新
}
);
return cacheData?.Tasks ?? DefaultTaskConfigs;
}
}