MilliSim/backend/MilliSim.Api/Services/AuthorizationNotificationJob.cs

88 lines
3.0 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.EntityFrameworkCore;
using MilliSim.Common.Models;
using MilliSim.Data;
namespace MilliSim.Services;
/// <summary>
/// 后台定时任务每天检查即将到期7 天内)的授权,给被授权用户发送提醒通知。
/// 使用 EventId = "auth-expiring-{authId}-{expiryDate}" 进行幂等,避免重复发送。
/// </summary>
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<MilliSimDbContext>();
var notification = scope.ServiceProvider.GetRequiredService<INotificationService>();
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
{
// 后台任务异常不应影响应用运行
}
}
}