using FreeSql;
using LiveForum.Code.Redis.Contract;
using LiveForum.IService.Others;
using LiveForum.Model;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace LiveForum.Service.ScheduledJobs
{
///
/// 主播送花数量重置任务
/// 每月1号凌晨3点执行,清空所有主播的送花数量,然后从T_FlowerRecords表重新统计当月数据
///
public class StreamerFlowerResetJob : IScheduledJobService
{
private readonly IBaseRepository _streamersRepository;
private readonly IBaseRepository _flowerRecordsRepository;
private readonly IRedisService _redisService;
private readonly ILogger _logger;
private const int BatchSize = 500; // 每批处理500条记录
private const string FLOWER_COUNT_KEY_PREFIX = "flower_count:";
///
/// 构造函数
///
/// 主播仓储
/// 送花记录仓储
/// Redis服务
/// 日志记录器
public StreamerFlowerResetJob(
IBaseRepository streamersRepository,
IBaseRepository flowerRecordsRepository,
IRedisService redisService,
ILogger logger)
{
_streamersRepository = streamersRepository;
_flowerRecordsRepository = flowerRecordsRepository;
_redisService = redisService;
_logger = logger;
}
///
/// 重置主播送花数量(每月清零并重新统计当月数据)
///
///
public async Task ResetStreamerFlowerCountAsync()
{
var startTime = DateTime.Now;
_logger.LogInformation("[定时任务] 开始执行主播送花数量重置任务,开始时间:{StartTime}", startTime);
try
{
// 1. 获取当前月份(yyyy-MM格式)
var currentMonth = DateTime.Now.ToString("yyyy-MM");
_logger.LogInformation("[定时任务] 当前月份:{CurrentMonth}", currentMonth);
// 2. 将所有主播的FlowerCount清零(批量更新)
_logger.LogInformation("[定时任务] 开始将所有主播的送花数量清零...");
var resetCount = await _streamersRepository.UpdateDiy
.Set(x => x.FlowerCount == 0)
.ExecuteAffrowsAsync();
_logger.LogInformation("[定时任务] 已清零 {ResetCount} 个主播的送花数量", resetCount);
// 2.5 清除Redis中所有主播的花数缓存
_logger.LogInformation("[定时任务] 开始清除Redis花数缓存...");
var allStreamers = await _streamersRepository.Select.ToListAsync();
var clearedCount = 0;
foreach (var streamer in allStreamers)
{
var cacheKey = $"{FLOWER_COUNT_KEY_PREFIX}Streamer:{streamer.Id}";
await _redisService.RemoveAsync(cacheKey);
clearedCount++;
}
_logger.LogInformation("[定时任务] 已清除 {ClearedCount} 个主播的Redis花数缓存", clearedCount);
// 3. 从T_FlowerRecords表统计当月数据
_logger.LogInformation("[定时任务] 开始统计当月送花记录...");
// 先查询所有符合条件的记录
var flowerRecords = await _flowerRecordsRepository.Select
.Where(x => x.SendMonth == currentMonth && x.TargetType == "Streamer")
.ToListAsync();
// 在内存中分组并统计
var monthlyStats = flowerRecords
.GroupBy(x => x.TargetId)
.Select(g => new
{
TargetId = g.Key,
TotalFlowers = g.Sum(x => x.FlowerCount)
})
.ToList();
_logger.LogInformation("[定时任务] 统计完成,共有 {Count} 个主播有当月送花记录", monthlyStats.Count);
if (!monthlyStats.Any())
{
_logger.LogInformation("[定时任务] 当月没有送花记录,任务完成");
return;
}
// 4. 分批更新主播的FlowerCount
var totalCount = monthlyStats.Count;
var processedCount = 0;
var batchNumber = 0;
while (processedCount < totalCount)
{
batchNumber++;
var batch = monthlyStats.Skip(processedCount).Take(BatchSize).ToList();
var batchSize = batch.Count;
_logger.LogInformation("[定时任务] 开始处理第 {BatchNumber} 批,本批 {BatchSize} 条记录", batchNumber, batchSize);
// 批量更新本批主播的送花数量
foreach (var stat in batch)
{
await _streamersRepository.UpdateDiy
.Set(x => x.FlowerCount == stat.TotalFlowers)
.Where(x => x.Id == stat.TargetId)
.ExecuteAffrowsAsync();
}
processedCount += batchSize;
_logger.LogInformation("[定时任务] 第 {BatchNumber} 批处理完成,已处理 {ProcessedCount}/{TotalCount} 条记录",
batchNumber, processedCount, totalCount);
}
var endTime = DateTime.Now;
var duration = endTime - startTime;
_logger.LogInformation("[定时任务] 主播送花数量重置任务执行完成!总耗时:{Duration}秒,共处理 {TotalCount} 个主播",
duration.TotalSeconds, totalCount);
}
catch (Exception ex)
{
_logger.LogError(ex, "[定时任务] 主播送花数量重置任务执行失败!");
throw; // 重新抛出异常,让Hangfire记录失败状态
}
}
}
}