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; /// /// 表示一个任务包,用于初始化并使用 Hangfire 调度定时任务。 /// public class JobPack : BaseAppPack { /// /// 获取任务包的执行顺序。此值决定任务包的执行顺序。 /// public override int Order => 999; /// /// 调用任务包以发现并调度定时任务。 /// /// 用于解析依赖项的服务提供器。 public override void Invoke(IServiceProvider serviceProvider) { // 获取日志记录器和主机环境服务 var logger = serviceProvider.GetService>(); var env = serviceProvider.GetService(); // 发现所有继承自 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()!.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)typeof(RecurringJob).GetField("Instance", BindingFlags.NonPublic | BindingFlags.Static)!.GetValue(null)!; // 确保所有任务的唯一键不重复 (jobs.Select(job => job.GetCustomAttribute()!.UniqueJobKey).Distinct().Count() != jobs.Count).ThrowIfTrue("任务key重复"); // 记录发现的任务数量 logger.LogInformation("一共获取{count}个调度任务", jobs.Count); // 遍历每个发现的任务并进行调度 foreach (var job in jobs) { // 获取任务的 RecurringJobAttribute var recurringSetting = job.GetCustomAttribute()!; // 确保 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 }); } } }