// Admin.NET 项目的版æƒã€å•†æ ‡ã€ä¸“利和其他相关æƒåˆ©å‡å—ç›¸åº”æ³•å¾‹æ³•è§„çš„ä¿æŠ¤ã€‚ä½¿ç”¨æœ¬é¡¹ç›®åº”éµå®ˆç›¸å…³æ³•律法规和许å¯è¯çš„è¦æ±‚。 // // 本项目主è¦éµå¾ª MIT 许å¯è¯å’Œ Apache 许å¯è¯ï¼ˆç‰ˆæœ¬ 2.0)进行分å‘和使用。许å¯è¯ä½äºŽæºä»£ç æ ‘æ ¹ç›®å½•ä¸çš„ LICENSE-MIT å’Œ LICENSE-APACHE 文件。 // // ä¸å¾—利用本项目从事å±å®³å›½å®¶å®‰å…¨ã€æ‰°ä¹±ç¤¾ä¼šç§©åºã€ä¾µçŠ¯ä»–äººåˆæ³•æƒç›Šç‰æ³•å¾‹æ³•è§„ç¦æ¢çš„æ´»åЍï¼ä»»ä½•基于本项目二次开å‘è€Œäº§ç”Ÿçš„ä¸€åˆ‡æ³•å¾‹çº çº·å’Œè´£ä»»ï¼Œæˆ‘ä»¬ä¸æ‰¿æ‹…ä»»ä½•è´£ä»»ï¼ namespace Admin.NET.Core.Service; /// <summary> /// 作业æŒä¹…化(数æ®åº“) /// </summary> public class DbJobPersistence : IJobPersistence { private readonly IServiceScopeFactory _serviceScopeFactory; public DbJobPersistence(IServiceScopeFactory serviceScopeFactory) { _serviceScopeFactory = serviceScopeFactory; } /// <summary> /// 作业调度æœåŠ¡å¯åŠ¨æ—¶ /// </summary> /// <param name="stoppingToken"></param> /// <returns></returns> /// <exception cref="NotSupportedException"></exception> public async Task<IEnumerable<SchedulerBuilder>> PreloadAsync(CancellationToken stoppingToken) { using var scope = _serviceScopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew(); var dynamicJobCompiler = scope.ServiceProvider.GetRequiredService<DynamicJobCompiler>(); // èŽ·å–æ‰€æœ‰å®šä¹‰çš„作业 var allJobs = App.EffectiveTypes.ScanToBuilders().ToList(); // 若数æ®åº“ä¸å˜åœ¨ä»»ä½•作业,则直接返回 if (!db.Queryable<SysJobDetail>().Any(u => true)) return allJobs; // é历所有定义的作业 foreach (var schedulerBuilder in allJobs) { // 获å–ä½œä¸šä¿¡æ¯æž„建器 var jobBuilder = schedulerBuilder.GetJobBuilder(); // åŠ è½½æ•°æ®åº“æ•°æ® var dbDetail = await db.Queryable<SysJobDetail>().FirstAsync(u => u.JobId == jobBuilder.JobId, stoppingToken); if (dbDetail == null) continue; // åŒæ¥æ•°æ®åº“æ•°æ® jobBuilder.LoadFrom(dbDetail); // 获å–作业的所有数æ®åº“的触å‘器 var dbTriggers = await db.Queryable<SysJobTrigger>().Where(u => u.JobId == jobBuilder.JobId).ToListAsync(stoppingToken); // é历所有作业触å‘器 foreach (var (_, triggerBuilder) in schedulerBuilder.GetEnumerable()) { // åŠ è½½æ•°æ®åº“æ•°æ® var dbTrigger = dbTriggers.FirstOrDefault(u => u.JobId == jobBuilder.JobId && u.TriggerId == triggerBuilder.TriggerId); if (dbTrigger == null) continue; triggerBuilder.LoadFrom(dbTrigger).Updated(); // æ ‡è®°æ›´æ–° } // é历所有éžç¼–译时定义的触å‘å™¨åŠ å…¥åˆ°ä½œä¸šä¸ foreach (var dbTrigger in dbTriggers) { if (schedulerBuilder.GetTriggerBuilder(dbTrigger.TriggerId)?.JobId == jobBuilder.JobId) continue; var triggerBuilder = TriggerBuilder.Create(dbTrigger.TriggerId).LoadFrom(dbTrigger); schedulerBuilder.AddTriggerBuilder(triggerBuilder); // å…ˆæ·»åŠ triggerBuilder.Updated(); // 冿 ‡è®°æ›´æ–° } // æ ‡è®°æ›´æ–° schedulerBuilder.Updated(); } // èŽ·å–æ•°æ®åº“所有通过脚本创建的作业 var allDbScriptJobs = await db.Queryable<SysJobDetail>().Where(u => u.CreateType != JobCreateTypeEnum.BuiltIn).ToListAsync(stoppingToken); foreach (var dbDetail in allDbScriptJobs) { // 动æ€åˆ›å»ºä½œä¸š Type jobType = dbDetail.CreateType switch { JobCreateTypeEnum.Script => dynamicJobCompiler.BuildJob(dbDetail.ScriptCode), JobCreateTypeEnum.Http => typeof(HttpJob), _ => throw new NotSupportedException(), }; // åŠ¨æ€æž„建的 jobType 的程åºé›†åç§°ä¸ºéšæœºåç§°ï¼Œéœ€é‡æ–°è®¾ç½® dbDetail.AssemblyName = jobType.Assembly.FullName!.Split(',')[0]; var jobBuilder = JobBuilder.Create(jobType).LoadFrom(dbDetail); // å¼ºè¡Œè®¾ç½®ä¸ºä¸æ‰«æ IJob 实现类 [Trigger] 特性触å‘器,å¦åˆ™ SchedulerBuilder.Create ä¼šå†æ¬¡æ‰«æï¼Œå¯¼è‡´é‡å¤æ·»åŠ åŒå触å‘器 jobBuilder.SetIncludeAnnotations(false); // 获å–作业的所有数æ®åº“的触å‘å™¨åŠ å…¥åˆ°ä½œä¸šä¸ var dbTriggers = await db.Queryable<SysJobTrigger>().Where(u => u.JobId == jobBuilder.JobId).ToListAsync(); var triggerBuilders = dbTriggers.Select(u => TriggerBuilder.Create(u.TriggerId).LoadFrom(u).Updated()); var schedulerBuilder = SchedulerBuilder.Create(jobBuilder, triggerBuilders.ToArray()); // æ ‡è®°æ›´æ–° schedulerBuilder.Updated(); allJobs.Add(schedulerBuilder); } return allJobs; } /// <summary> /// 作业计划åˆå§‹åŒ–通知 /// </summary> /// <param name="builder"></param> /// <param name="stoppingToken"></param> /// <returns></returns> public Task<SchedulerBuilder> OnLoadingAsync(SchedulerBuilder builder, CancellationToken stoppingToken) { return Task.FromResult(builder); } /// <summary> /// 作业计划Schedulerçš„JobDetailå˜åŒ–æ—¶ /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task OnChangedAsync(PersistenceContext context) { using var scope = _serviceScopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew(); var jobDetail = context.JobDetail.Adapt<SysJobDetail>(); switch (context.Behavior) { case PersistenceBehavior.Appended: await db.Insertable(jobDetail).ExecuteCommandAsync(); break; case PersistenceBehavior.Updated: await db.Updateable(jobDetail).WhereColumns(u => new { u.JobId }).IgnoreColumns(u => new { u.Id, u.CreateType, u.ScriptCode }).ExecuteCommandAsync(); break; case PersistenceBehavior.Removed: await db.Deleteable<SysJobDetail>().Where(u => u.JobId == jobDetail.JobId).ExecuteCommandAsync(); break; } } /// <summary> /// 作业计划Scheduler的触å‘器Triggerå˜åŒ–æ—¶ /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task OnTriggerChangedAsync(PersistenceTriggerContext context) { using var scope = _serviceScopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew(); var jobTrigger = context.Trigger.Adapt<SysJobTrigger>(); switch (context.Behavior) { case PersistenceBehavior.Appended: await db.Insertable(jobTrigger).ExecuteCommandAsync(); break; case PersistenceBehavior.Updated: await db.Updateable(jobTrigger).WhereColumns(u => new { u.TriggerId, u.JobId }).IgnoreColumns(u => new { u.Id }).ExecuteCommandAsync(); break; case PersistenceBehavior.Removed: await db.Deleteable<SysJobTrigger>().Where(u => u.TriggerId == jobTrigger.TriggerId && u.JobId == jobTrigger.JobId).ExecuteCommandAsync(); break; } } /// <summary> /// 作业触å‘器è¿è¡Œè®°å½• /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task OnExecutionRecordAsync(PersistenceExecutionRecordContext context) { using var scope = _serviceScopeFactory.CreateScope(); var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>().CopyNew(); var jobTriggerRecord = context.Timeline.Adapt<SysJobTriggerRecord>(); await db.Insertable(jobTriggerRecord).ExecuteCommandAsync(); } }