Files
jinko.yikatong/Src/Dlhis.ScheduledTask/Packs/JobPack.cs

78 lines
3.5 KiB
C#

using System.Linq.Expressions;
using System.Reflection;
using Dlhis.ScheduledTask.Attributes;
using Hangfire;
using Hangfire.Common;
using Kurisu.AspNetCore.Startup;
using Kurisu.AspNetCore.Utils.Extensions;
namespace Dlhis.ScheduledTask.Packs;
/// <summary>
/// 表示一个任务包,用于初始化并使用 Hangfire 调度定时任务。
/// </summary>
public class JobPack : BaseAppPack
{
/// <summary>
/// 获取任务包的执行顺序。此值决定任务包的执行顺序。
/// </summary>
public override int Order => 999;
/// <summary>
/// 调用任务包以发现并调度定时任务。
/// </summary>
/// <param name="serviceProvider">用于解析依赖项的服务提供器。</param>
public override void Invoke(IServiceProvider serviceProvider)
{
// 获取日志记录器和主机环境服务
var logger = serviceProvider.GetService<ILogger<JobPack>>();
var env = serviceProvider.GetService<IHostEnvironment>();
// 发现所有继承自 BaseJob 且具有 RecurringJobAttribute 的类型
var jobs = Assembly.GetEntryAssembly()!.GetTypes()
.Where(x => x.IsAssignableTo(typeof(BaseJob)) && x != typeof(BaseJob))
.Where(x => x.IsDefined(typeof(RecurringJobAttribute)))
// 如果当前环境为开发环境,仅筛选出 RunOnDevelopment 为 true 的任务类型
.WhereIf(env.IsDevelopment(), x => x.GetCustomAttribute<RecurringJobAttribute>()!.RunOnDevelopment)
.ToList();
// 如果未发现任何任务,则直接返回
if (jobs.IsEmpty())
{
return;
}
// 使用反射获取 Job.FromExpression 私有方法
var setJob = typeof(Job).GetMethod(nameof(Job.FromExpression), BindingFlags.NonPublic | BindingFlags.Static, new[] { typeof(LambdaExpression), typeof(Type), typeof(string) });
// 获取 RecurringJobManager 的单例实例
var m = (Lazy<RecurringJobManager>)typeof(RecurringJob).GetField("Instance", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null)!;
// 确保所有任务的唯一键不重复
(jobs.Select(job => job.GetCustomAttribute<RecurringJobAttribute>()!.UniqueJobKey).Distinct().Count() != jobs.Count).ThrowIfTrue("任务key重复");
// 记录发现的任务数量
logger.LogInformation("一共获取{count}个调度任务", jobs.Count);
// 遍历每个发现的任务并进行调度
foreach (var job in jobs)
{
// 获取任务的 RecurringJobAttribute
var recurringSetting = job.GetCustomAttribute<RecurringJobAttribute>()!;
// 确保 Cron 表达式已定义
recurringSetting.Cron.IsEmpty().ThrowIfTrue(job.Name + "尚未定义Cron");
// 创建调用任务 TodoAsync 方法的表达式
var parameterExpression = Expression.Parameter(job);
var todo = job.GetMethod(nameof(BaseJob.TodoAsync))!;
var todoExp = Expression.Call(parameterExpression, todo);
// 使用私有方法 Job.FromExpression 创建一个新的 Hangfire 任务
var nJob = (Job)setJob!.Invoke(null, new object[] { Expression.Lambda(todoExp), job, recurringSetting.Queue });
// 在 RecurringJobManager 中添加或更新定时任务
m.Value.AddOrUpdate(recurringSetting.UniqueJobKey, nJob, recurringSetting.Cron, new RecurringJobOptions
{
TimeZone = TimeZoneInfo.Local
});
}
}
}