2026-04-06 14:13:43 +08:00
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using YD_Event.Core.Entities;
|
|
|
|
|
|
using YD_Event.Core.Enum;
|
|
|
|
|
|
|
|
|
|
|
|
namespace YD_Event.Application.Event;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 赛事状态自动更新后台服务
|
2026-05-11 12:53:00 +08:00
|
|
|
|
/// 每10秒执行一次,自动更新赛事状态
|
2026-04-06 14:13:43 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class EventStatusUpdateService : BackgroundService
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
|
|
private readonly ILogger<EventStatusUpdateService> _logger;
|
|
|
|
|
|
private readonly PeriodicTimer _timer;
|
2026-05-11 12:53:00 +08:00
|
|
|
|
private int _executionCount = 0;
|
2026-04-06 14:13:43 +08:00
|
|
|
|
|
|
|
|
|
|
public EventStatusUpdateService(
|
|
|
|
|
|
IServiceProvider serviceProvider,
|
|
|
|
|
|
ILogger<EventStatusUpdateService> logger)
|
|
|
|
|
|
{
|
|
|
|
|
|
_serviceProvider = serviceProvider;
|
|
|
|
|
|
_logger = logger;
|
2026-05-11 12:53:00 +08:00
|
|
|
|
_timer = new PeriodicTimer(TimeSpan.FromSeconds(10));
|
2026-04-06 14:13:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
|
|
|
|
{
|
2026-05-11 12:53:00 +08:00
|
|
|
|
_logger.LogInformation("赛事状态自动更新服务已启动,每10秒执行一次");
|
2026-04-06 14:13:43 +08:00
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
do
|
|
|
|
|
|
{
|
|
|
|
|
|
await UpdateEventStatusesAsync(stoppingToken);
|
|
|
|
|
|
} while (await _timer.WaitForNextTickAsync(stoppingToken));
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (OperationCanceledException)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogInformation("赛事状态自动更新服务已停止");
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "赛事状态自动更新服务发生异常");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private async Task UpdateEventStatusesAsync(CancellationToken stoppingToken)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogDebug("开始执行赛事状态更新...");
|
|
|
|
|
|
|
|
|
|
|
|
using var scope = _serviceProvider.CreateScope();
|
|
|
|
|
|
var repository = scope.ServiceProvider.GetRequiredService<ISqlSugarRepository<EventInfo>>();
|
|
|
|
|
|
|
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
|
var eventsToUpdate = new List<EventInfo>();
|
2026-05-11 12:53:00 +08:00
|
|
|
|
|
|
|
|
|
|
_executionCount++;
|
|
|
|
|
|
|
|
|
|
|
|
// 每执行6次(约1分钟)做一次全量检查,其他时候只检查时间窗口内的赛事
|
|
|
|
|
|
var isFullCheck = _executionCount % 6 == 1;
|
|
|
|
|
|
|
|
|
|
|
|
List<EventInfo> candidateEvents;
|
2026-04-06 14:13:43 +08:00
|
|
|
|
|
2026-05-11 12:53:00 +08:00
|
|
|
|
if (isFullCheck)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogDebug("执行全量赛事状态检查...");
|
|
|
|
|
|
candidateEvents = await repository.AsQueryable()
|
|
|
|
|
|
.Where(e => !e.DeleteMark && e.Status == AvailabilityStatus.Enable)
|
|
|
|
|
|
.ToListAsync(stoppingToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 优化查询:只查询可能需要更新状态的赛事
|
|
|
|
|
|
// 逻辑:只查询那些当前时间接近关键时间点(报名开始/结束、比赛开始/结束)的赛事
|
|
|
|
|
|
// 设置一个时间窗口:当前时间前后各1分钟,这样只会处理即将或刚发生状态变化的赛事
|
|
|
|
|
|
var timeWindow = TimeSpan.FromMinutes(1);
|
|
|
|
|
|
var windowStart = now.Add(-timeWindow);
|
|
|
|
|
|
var windowEnd = now.Add(timeWindow);
|
|
|
|
|
|
|
|
|
|
|
|
candidateEvents = await repository.AsQueryable()
|
|
|
|
|
|
.Where(e => !e.DeleteMark && e.Status == AvailabilityStatus.Enable)
|
|
|
|
|
|
.Where(e =>
|
|
|
|
|
|
// 报名开始时间在时间窗口内
|
|
|
|
|
|
(e.RegistrationStartTime >= windowStart && e.RegistrationStartTime <= windowEnd) ||
|
|
|
|
|
|
// 报名结束时间在时间窗口内
|
|
|
|
|
|
(e.RegistrationEndTime >= windowStart && e.RegistrationEndTime <= windowEnd) ||
|
|
|
|
|
|
// 比赛开始时间在时间窗口内
|
|
|
|
|
|
(e.MatchStartTime >= windowStart && e.MatchStartTime <= windowEnd) ||
|
|
|
|
|
|
// 比赛结束时间在时间窗口内
|
|
|
|
|
|
(e.MatchEndTime >= windowStart && e.MatchEndTime <= windowEnd)
|
|
|
|
|
|
)
|
|
|
|
|
|
.ToListAsync(stoppingToken);
|
|
|
|
|
|
}
|
2026-04-06 14:13:43 +08:00
|
|
|
|
|
2026-05-11 12:53:00 +08:00
|
|
|
|
foreach (var evt in candidateEvents)
|
2026-04-06 14:13:43 +08:00
|
|
|
|
{
|
|
|
|
|
|
var newStatus = CalculateEventStatus(evt, now);
|
|
|
|
|
|
if (evt.EventStatus != newStatus)
|
|
|
|
|
|
{
|
|
|
|
|
|
evt.EventStatus = newStatus;
|
|
|
|
|
|
evt.UpdatedTime = now;
|
|
|
|
|
|
eventsToUpdate.Add(evt);
|
|
|
|
|
|
_logger.LogInformation("赛事 {EventId}({EventName}) 状态更新: {OldStatus} -> {NewStatus}",
|
|
|
|
|
|
evt.Id, evt.EventName, evt.EventStatus, newStatus);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (eventsToUpdate.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
await repository.UpdateRangeAsync(eventsToUpdate);
|
|
|
|
|
|
_logger.LogInformation("成功更新 {Count} 个赛事的状态", eventsToUpdate.Count);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogDebug("没有需要更新状态的赛事");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError(ex, "更新赛事状态时发生异常");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据时间计算赛事状态
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="evt">赛事信息</param>
|
|
|
|
|
|
/// <param name="now">当前时间</param>
|
|
|
|
|
|
/// <returns>计算后的赛事状态</returns>
|
|
|
|
|
|
public static EventStatus CalculateEventStatus(EventInfo evt, DateTime now)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (now < evt.RegistrationStartTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
return EventStatus.NotStarted;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (now >= evt.RegistrationStartTime && now <= evt.RegistrationEndTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
return EventStatus.Enrolling;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (now > evt.RegistrationEndTime && now < evt.MatchStartTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
return EventStatus.RegistrationEnded;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (now >= evt.MatchStartTime && now <= evt.MatchEndTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
return EventStatus.Ongoing;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (now > evt.MatchEndTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
return EventStatus.Ended;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return EventStatus.NotStarted;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void Dispose()
|
|
|
|
|
|
{
|
|
|
|
|
|
_timer.Dispose();
|
|
|
|
|
|
base.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|