// 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);
}
}