// Admin.NET 项目的版æƒã€å•†æ ‡ã€ä¸“利和其他相关æƒåˆ©å‡å—ç›¸åº”æ³•å¾‹æ³•è§„çš„ä¿æŠ¤ã€‚ä½¿ç”¨æœ¬é¡¹ç›®åº”éµå®ˆç›¸å…³æ³•律法规和许å¯è¯çš„è¦æ±‚。 // // 本项目主è¦éµå¾ª MIT 许å¯è¯å’Œ Apache 许å¯è¯ï¼ˆç‰ˆæœ¬ 2.0)进行分å‘和使用。许å¯è¯ä½äºŽæºä»£ç æ ‘æ ¹ç›®å½•ä¸çš„ LICENSE-MIT å’Œ LICENSE-APACHE 文件。 // // ä¸å¾—利用本项目从事å±å®³å›½å®¶å®‰å…¨ã€æ‰°ä¹±ç¤¾ä¼šç§©åºã€ä¾µçŠ¯ä»–äººåˆæ³•æƒç›Šç‰æ³•å¾‹æ³•è§„ç¦æ¢çš„æ´»åЍï¼ä»»ä½•基于本项目二次开å‘è€Œäº§ç”Ÿçš„ä¸€åˆ‡æ³•å¾‹çº çº·å’Œè´£ä»»ï¼Œæˆ‘ä»¬ä¸æ‰¿æ‹…ä»»ä½•è´£ä»»ï¼ namespace Admin.NET.Core.Service; /// <summary> /// 系统作业任务æœåŠ¡ 🧩 /// </summary> [ApiDescriptionSettings(Order = 320)] public class SysJobService : IDynamicApiController, ITransient { private readonly SqlSugarRepository<SysJobDetail> _sysJobDetailRep; private readonly SqlSugarRepository<SysJobTrigger> _sysJobTriggerRep; private readonly SqlSugarRepository<SysJobTriggerRecord> _sysJobTriggerRecordRep; private readonly SqlSugarRepository<SysJobCluster> _sysJobClusterRep; private readonly ISchedulerFactory _schedulerFactory; private readonly DynamicJobCompiler _dynamicJobCompiler; public SysJobService(SqlSugarRepository<SysJobDetail> sysJobDetailRep, SqlSugarRepository<SysJobTrigger> sysJobTriggerRep, SqlSugarRepository<SysJobTriggerRecord> sysJobTriggerRecordRep, SqlSugarRepository<SysJobCluster> sysJobClusterRep, ISchedulerFactory schedulerFactory, DynamicJobCompiler dynamicJobCompiler) { _sysJobDetailRep = sysJobDetailRep; _sysJobTriggerRep = sysJobTriggerRep; _sysJobTriggerRecordRep = sysJobTriggerRecordRep; _sysJobClusterRep = sysJobClusterRep; _schedulerFactory = schedulerFactory; _dynamicJobCompiler = dynamicJobCompiler; } /// <summary> /// 获å–作业分页列表 â° /// </summary> [DisplayName("获å–作业分页列表")] public async Task<SqlSugarPagedList<JobDetailOutput>> PageJobDetail(PageJobDetailInput input) { var jobDetails = await _sysJobDetailRep.AsQueryable() .WhereIF(!string.IsNullOrWhiteSpace(input.JobId), u => u.JobId.Contains(input.JobId)) .WhereIF(!string.IsNullOrWhiteSpace(input.Description), u => u.Description.Contains(input.Description)) .Select(d => new JobDetailOutput { JobDetail = d, }).ToPagedListAsync(input.Page, input.PageSize); await _sysJobDetailRep.AsSugarClient().ThenMapperAsync(jobDetails.Items, async u => { u.JobTriggers = await _sysJobTriggerRep.GetListAsync(t => t.JobId == u.JobDetail.JobId); }); // æå–䏿‹¬å·é‡Œé¢çš„傿•°å€¼ var rgx = new Regex(@"(?i)(?<=\[)(.*)(?=\])"); foreach (var job in jobDetails.Items) { foreach (var jobTrigger in job.JobTriggers) { jobTrigger.Args = rgx.Match(jobTrigger.Args ?? "").Value; } } return jobDetails; } /// <summary> /// æ·»åŠ ä½œä¸š â° /// </summary> /// <returns></returns> [ApiDescriptionSettings(Name = "AddJobDetail"), HttpPost] [DisplayName("æ·»åŠ ä½œä¸š")] public async Task AddJobDetail(AddJobDetailInput input) { var isExist = await _sysJobDetailRep.IsAnyAsync(u => u.JobId == input.JobId && u.Id != input.Id); if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006); // 动æ€åˆ›å»ºä½œä¸š Type jobType; switch (input.CreateType) { case JobCreateTypeEnum.Script when string.IsNullOrEmpty(input.ScriptCode): throw Oops.Oh(ErrorCodeEnum.D1701); case JobCreateTypeEnum.Script: { jobType = _dynamicJobCompiler.BuildJob(input.ScriptCode); if (jobType.GetCustomAttributes(typeof(JobDetailAttribute)).FirstOrDefault() is not JobDetailAttribute jobDetailAttribute) throw Oops.Oh(ErrorCodeEnum.D1702); if (jobDetailAttribute.JobId != input.JobId) throw Oops.Oh(ErrorCodeEnum.D1703); break; } case JobCreateTypeEnum.Http: jobType = typeof(HttpJob); break; default: throw new NotSupportedException(); } _schedulerFactory.AddJob( JobBuilder.Create(jobType) .LoadFrom(input.Adapt<SysJobDetail>()).SetJobType(jobType)); // 延迟一下ç‰å¾…æŒä¹…åŒ–å†™å…¥ï¼Œå†æ‰§è¡Œå…¶ä»–å—æ®µçš„æ›´æ–° await Task.Delay(500); await _sysJobDetailRep.AsUpdateable() .SetColumns(u => new SysJobDetail { CreateType = input.CreateType, ScriptCode = input.ScriptCode }) .Where(u => u.JobId == input.JobId).ExecuteCommandAsync(); } /// <summary> /// 更新作业 â° /// </summary> /// <returns></returns> [ApiDescriptionSettings(Name = "UpdateJobDetail"), HttpPost] [DisplayName("更新作业")] public async Task UpdateJobDetail(UpdateJobDetailInput input) { var isExist = await _sysJobDetailRep.IsAnyAsync(u => u.JobId == input.JobId && u.Id != input.Id); if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006); var sysJobDetail = await _sysJobDetailRep.GetFirstAsync(u => u.Id == input.Id); if (sysJobDetail.JobId != input.JobId) throw Oops.Oh(ErrorCodeEnum.D1704); var scheduler = _schedulerFactory.GetJob(sysJobDetail.JobId); var oldScriptCode = sysJobDetail.ScriptCode; // 旧脚本代ç input.Adapt(sysJobDetail); if (input.CreateType == JobCreateTypeEnum.Script) { if (string.IsNullOrEmpty(input.ScriptCode)) throw Oops.Oh(ErrorCodeEnum.D1701); if (input.ScriptCode != oldScriptCode) { // 动æ€åˆ›å»ºä½œä¸š var jobType = _dynamicJobCompiler.BuildJob(input.ScriptCode); if (jobType.GetCustomAttributes(typeof(JobDetailAttribute)).FirstOrDefault() is not JobDetailAttribute jobDetailAttribute) throw Oops.Oh(ErrorCodeEnum.D1702); if (jobDetailAttribute.JobId != input.JobId) throw Oops.Oh(ErrorCodeEnum.D1703); scheduler?.UpdateDetail(JobBuilder.Create(jobType).LoadFrom(sysJobDetail).SetJobType(jobType)); } } else { scheduler?.UpdateDetail(scheduler.GetJobBuilder().LoadFrom(sysJobDetail)); } // Tip: å‡å¦‚è¿™æ¬¡æ›´æ–°æœ‰å˜æ›´äº† JobIdï¼Œå˜æ›´ JobId åŽè§¦å‘çš„æŒä¹…化更新执行,会由于找ä¸åˆ° JobId 而更新ä¸åˆ°æ•°æ® // 延迟一下ç‰å¾…æŒä¹…åŒ–å†™å…¥ï¼Œå†æ‰§è¡Œå…¶ä»–å—æ®µçš„æ›´æ–° await Task.Delay(500); await _sysJobDetailRep.UpdateAsync(sysJobDetail); } /// <summary> /// åˆ é™¤ä½œä¸š â° /// </summary> /// <returns></returns> [ApiDescriptionSettings(Name = "DeleteJobDetail"), HttpPost] [DisplayName("åˆ é™¤ä½œä¸š")] public async Task DeleteJobDetail(DeleteJobDetailInput input) { _schedulerFactory.RemoveJob(input.JobId); // 如果 _schedulerFactory ä¸ä¸å˜åœ¨ JodIdï¼Œåˆ™æ— æ³•è§¦å‘æŒä¹…化,下é¢çš„代ç ç¡®ä¿ä½œä¸šå’Œè§¦å‘å™¨èƒ½è¢«åˆ é™¤ await _sysJobDetailRep.DeleteAsync(u => u.JobId == input.JobId); await _sysJobTriggerRep.DeleteAsync(u => u.JobId == input.JobId); } /// <summary> /// 获å–触å‘器列表 â° /// </summary> [DisplayName("获å–触å‘器列表")] public async Task<List<SysJobTrigger>> GetJobTriggerList([FromQuery] JobDetailInput input) { return await _sysJobTriggerRep.AsQueryable() .WhereIF(!string.IsNullOrWhiteSpace(input.JobId), u => u.JobId.Contains(input.JobId)) .ToListAsync(); } /// <summary> /// æ·»åŠ è§¦å‘器 â° /// </summary> /// <returns></returns> [ApiDescriptionSettings(Name = "AddJobTrigger"), HttpPost] [DisplayName("æ·»åŠ è§¦å‘器")] public async Task AddJobTrigger(AddJobTriggerInput input) { var isExist = await _sysJobTriggerRep.IsAnyAsync(u => u.TriggerId == input.TriggerId && u.Id != input.Id); if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006); var jobTrigger = input.Adapt<SysJobTrigger>(); jobTrigger.Args = "[" + jobTrigger.Args + "]"; var scheduler = _schedulerFactory.GetJob(input.JobId); scheduler?.AddTrigger(Triggers.Create(input.AssemblyName, input.TriggerType).LoadFrom(jobTrigger)); } /// <summary> /// 更新触å‘器 â° /// </summary> /// <returns></returns> [ApiDescriptionSettings(Name = "UpdateJobTrigger"), HttpPost] [DisplayName("更新触å‘器")] public async Task UpdateJobTrigger(UpdateJobTriggerInput input) { var isExist = await _sysJobTriggerRep.IsAnyAsync(u => u.TriggerId == input.TriggerId && u.Id != input.Id); if (isExist) throw Oops.Oh(ErrorCodeEnum.D1006); var jobTrigger = input.Adapt<SysJobTrigger>(); jobTrigger.Args = "[" + jobTrigger.Args + "]"; var scheduler = _schedulerFactory.GetJob(input.JobId); scheduler?.UpdateTrigger(Triggers.Create(input.AssemblyName, input.TriggerType).LoadFrom(jobTrigger)); } /// <summary> /// åˆ é™¤è§¦å‘器 â° /// </summary> /// <returns></returns> [ApiDescriptionSettings(Name = "DeleteJobTrigger"), HttpPost] [DisplayName("åˆ é™¤è§¦å‘器")] public async Task DeleteJobTrigger(DeleteJobTriggerInput input) { var scheduler = _schedulerFactory.GetJob(input.JobId); scheduler?.RemoveTrigger(input.TriggerId); // 如果 _schedulerFactory ä¸ä¸å˜åœ¨ JodIdï¼Œåˆ™æ— æ³•è§¦å‘æŒä¹…化,下行代ç ç¡®ä¿è§¦å‘å™¨èƒ½è¢«åˆ é™¤ await _sysJobTriggerRep.DeleteAsync(u => u.JobId == input.JobId && u.TriggerId == input.TriggerId); } /// <summary> /// æš‚åœæ‰€æœ‰ä½œä¸š â° /// </summary> /// <returns></returns> [DisplayName("æš‚åœæ‰€æœ‰ä½œä¸š")] public void PauseAllJob() { _schedulerFactory.PauseAll(); } /// <summary> /// å¯åŠ¨æ‰€æœ‰ä½œä¸š â° /// </summary> /// <returns></returns> [DisplayName("å¯åŠ¨æ‰€æœ‰ä½œä¸š")] public void StartAllJob() { _schedulerFactory.StartAll(); } /// <summary> /// æš‚åœä½œä¸š â° /// </summary> [DisplayName("æš‚åœä½œä¸š")] public void PauseJob(JobDetailInput input) { _schedulerFactory.TryPauseJob(input.JobId, out _); } /// <summary> /// å¯åŠ¨ä½œä¸š â° /// </summary> [DisplayName("å¯åŠ¨ä½œä¸š")] public void StartJob(JobDetailInput input) { _schedulerFactory.TryStartJob(input.JobId, out _); } /// <summary> /// å–æ¶ˆä½œä¸š â° /// </summary> [DisplayName("å–æ¶ˆä½œä¸š")] public void CancelJob(JobDetailInput input) { _schedulerFactory.TryCancelJob(input.JobId, out _); } /// <summary> /// 执行作业 â° /// </summary> /// <param name="input"></param> [DisplayName("执行作业")] public void RunJob(JobDetailInput input) { if (_schedulerFactory.TryRunJob(input.JobId, out _) != ScheduleResult.Succeed) throw Oops.Oh(ErrorCodeEnum.D1705); } /// <summary> /// æš‚åœè§¦å‘器 â° /// </summary> [DisplayName("æš‚åœè§¦å‘器")] public void PauseTrigger(JobTriggerInput input) { var scheduler = _schedulerFactory.GetJob(input.JobId); scheduler?.PauseTrigger(input.TriggerId); } /// <summary> /// å¯åŠ¨è§¦å‘器 â° /// </summary> [DisplayName("å¯åŠ¨è§¦å‘器")] public void StartTrigger(JobTriggerInput input) { var scheduler = _schedulerFactory.GetJob(input.JobId); scheduler?.StartTrigger(input.TriggerId); } /// <summary> /// 强制唤醒作业调度器 â° /// </summary> [DisplayName("强制唤醒作业调度器")] public void CancelSleep() { _schedulerFactory.CancelSleep(); } /// <summary> /// å¼ºåˆ¶è§¦å‘æ‰€æœ‰ä½œä¸šæŒä¹…化 â° /// </summary> [DisplayName("å¼ºåˆ¶è§¦å‘æ‰€æœ‰ä½œä¸šæŒä¹…化")] public void PersistAll() { _schedulerFactory.PersistAll(); } /// <summary> /// 获å–集群列表 â° /// </summary> [DisplayName("获å–集群列表")] public async Task<List<SysJobCluster>> GetJobClusterList() { return await _sysJobClusterRep.GetListAsync(); } /// <summary> /// 获å–作业触å‘器è¿è¡Œè®°å½•分页列表 â° /// </summary> [DisplayName("获å–作业触å‘器è¿è¡Œè®°å½•分页列表")] public async Task<SqlSugarPagedList<SysJobTriggerRecord>> PageJobTriggerRecord(PageJobTriggerRecordInput input) { return await _sysJobTriggerRecordRep.AsQueryable() .WhereIF(!string.IsNullOrWhiteSpace(input.JobId), u => u.JobId.Contains(input.JobId)) .WhereIF(!string.IsNullOrWhiteSpace(input.TriggerId), u => u.TriggerId.Contains(input.TriggerId)) .OrderByDescending(u => u.Id) .ToPagedListAsync(input.Page, input.PageSize); } }