using Microsoft.EntityFrameworkCore;
using MilliSim.Common.Models;
using MilliSim.Data;
namespace MilliSim.Services;
///
/// 后台定时任务:每天检查即将到期(7 天内)的授权,给被授权用户发送提醒通知。
/// 使用 EventId = "auth-expiring-{authId}-{expiryDate}" 进行幂等,避免重复发送。
///
public class AuthorizationNotificationJob : IHostedService, IDisposable
{
private readonly IServiceProvider _serviceProvider;
private Timer? _timer;
// 检查间隔:6 小时(既能在到期日及时触发,又避免频繁扫描)
private static readonly TimeSpan CheckInterval = TimeSpan.FromHours(6);
// 提前提醒天数
private const int ExpiringSoonDays = 7;
public AuthorizationNotificationJob(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public Task StartAsync(CancellationToken cancellationToken)
{
// 启动后 30 秒执行第一次,之后每 6 小时一次
_timer = new Timer(DoWork, null, TimeSpan.FromSeconds(30), CheckInterval);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
private async void DoWork(object? state)
{
try
{
using var scope = _serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService();
var notification = scope.ServiceProvider.GetRequiredService();
var now = DateTime.Now;
var deadline = now.AddDays(ExpiringSoonDays);
// 查询 7 天内即将到期、且尚未过期的有效授权
var expiringSoon = await db.Authorizations
.Where(a => a.Status == AuthorizationStatus.Active
&& a.EndTime > now
&& a.EndTime <= deadline)
.ToListAsync();
foreach (var auth in expiringSoon)
{
var daysRemaining = Math.Max(1, (int)Math.Ceiling((auth.EndTime - now).TotalDays));
// 按到期日期去重:同一天内只发一次
var eventId = $"auth-expiring-{auth.Id}-{auth.EndTime:yyyyMMdd}";
try
{
await notification.CreateNotificationAsync(
NotificationType.Authorization,
"授权即将到期",
$"您的客户级授权将在 {daysRemaining} 天后到期({auth.EndTime:yyyy-MM-dd}),请及时联系销售续期。",
userId: auth.UserId,
eventId: eventId);
}
catch
{
// 单条失败不影响其他通知
}
}
}
catch
{
// 后台任务异常不应影响应用运行
}
}
}