You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

279 lines
13 KiB

using Mapster;
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using System.Reflection;
using Wood.Data.Repository;
using Wood.Entity;
using Wood.Entity.SystemManage;
using Wood.EventBus;
using Wood.Util;
namespace Wood.AutoJob
{
public class AutoJobCenter
{
#region 扫描任务计划
public async void ScanJob()
{
using var scope = GlobalContext.ServiceProvider!.CreateScope();
var jobTriggerRepository = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<JobTriggerEntity>>();
var jobDetailRepository = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<JobDetailEntity>>();
var eventBus = scope.ServiceProvider.GetRequiredService<IEventBus>();
var types = Assembly.GetAssembly(typeof(AutoJobCenter))!.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.GetCustomAttributes(typeof(JobDetailAttribute), false).Any())
.ToList();
foreach (var item in types)
{
string jobId = "";
var jobDetailAttr = item.GetCustomAttribute<JobDetailAttribute>();
if (jobDetailAttr != null)
{
var detail = jobDetailAttr.Adapt<JobDetailEntity>();
detail.JobType = item.FullName;
detail.AssemblyName = item.Assembly.FullName;
jobId = detail.JobId!;
var dbDetail = await jobDetailRepository.GetFirstAsync(it => it.JobId == detail.JobId);
if (!jobDetailRepository.IsAny(it => it.JobId == detail.JobId))
jobDetailRepository.Insert(detail);
else
{
if (dbDetail.Description != detail.Description || dbDetail.GroupName != detail.GroupName)
{
dbDetail.GroupName = detail.GroupName;
dbDetail.Description = detail.Description;
await jobDetailRepository.UpdateAsync(dbDetail);
}
}
}
else
continue;
var secondsAtAttr = item.GetCustomAttribute<PeriodSecondsAttribute>();
JobTriggerEntity? entity = null;
if (secondsAtAttr != null && entity == null)
{
entity = secondsAtAttr.Adapt<JobTriggerEntity>();
entity.TriggerType = TriggerTypeEnum.PeriodSeconds;
}
var minutesAttr = item.GetCustomAttribute<PeriodMinutesAttribute>();
if (minutesAttr != null && entity == null)
{
entity = minutesAttr.Adapt<JobTriggerEntity>();
entity.TriggerType = TriggerTypeEnum.PeriodMinutes;
}
var dailyAtAttr = item.GetCustomAttribute<DailyAtAttribute>();
if (dailyAtAttr != null && entity == null)
{
entity = dailyAtAttr.Adapt<JobTriggerEntity>();
entity.TriggerType = TriggerTypeEnum.DailyAt;
}
var cronAttr = item.GetCustomAttribute<CronAttribute>();
if (cronAttr != null && entity == null)
{
entity = cronAttr.Adapt<JobTriggerEntity>();
entity.TriggerType = TriggerTypeEnum.Cron;
}
if (entity != null)
{
entity.JobId = jobId;
entity.Status = TriggerStatusEnum.Ready;
var trigger = jobTriggerRepository.GetFirst(it => it.TriggerId == entity.TriggerId);
if (trigger == null)
jobTriggerRepository.Insert(entity);
else
{
//只有触发器类型 或者 参数 或者 说明 变更时需要更新触发器数据
if (trigger.TriggerType != entity.TriggerType || trigger.Args != entity.Args || trigger.Description != entity.Description)
{
entity.Id = trigger.Id;
entity.CreateTime = trigger.CreateTime;
entity.CreateUserId = trigger.CreateUserId;
jobTriggerRepository.Update(entity.Adapt(trigger));
}
}
}
}
}
#endregion
#region 添加任务计划
public async Task AddScheduleJob()
{
using var scope = GlobalContext.ServiceProvider!.CreateScope();
var schedulerFactory = scope.ServiceProvider.GetRequiredService<ISchedulerFactory>();
var jobTriggerRepository = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<JobTriggerEntity>>();
var jobDetailRepository = scope.ServiceProvider.GetRequiredService<SqlSugarRepository<JobDetailEntity>>();
var jobDetailList = jobDetailRepository.AsQueryable().ClearFilter().ToList();
var scheduler = await schedulerFactory.GetScheduler();
foreach (var detail in jobDetailList)
{
var jobTrigger = await jobTriggerRepository.GetFirstAsync(it => it.JobId == detail.JobId);
var jobType = this.GetType().Assembly.GetType(detail.JobType!);
if (jobType != null)
{
IJobDetail job = JobBuilder.Create(jobType!).WithIdentity(detail.JobId!, detail.GroupName!).Build();
job.JobDataMap.Add("DetailId", detail.Id);
job.JobDataMap.Add("TriggerId", jobTrigger.Id);
var trigger = CreateTrigger(jobTrigger, detail);
//更新下次执行时间
jobTrigger.NextRunTime = trigger.GetNextFireTimeUtc()?.DateTime.AddHours(8);
jobTrigger.Status = TriggerStatusEnum.Ready;
await jobTriggerRepository.UpdateAsync(jobTrigger);
await scheduler.ScheduleJob(job, CreateTrigger(jobTrigger, detail));
}
else
{
jobTrigger.Status = TriggerStatusEnum.Ineffective;
await jobTriggerRepository.UpdateAsync(jobTrigger);
}
}
await scheduler.Start();
}
/// <summary>
/// 创建trigger
/// </summary>
/// <param name="jobTrigger"></param>
/// <param name="jobDetail"></param>
/// <returns></returns>
public static ITrigger CreateTrigger(JobTriggerEntity jobTrigger, JobDetailEntity jobDetail)
{
/*
* Cron
withMisfireHandlingInstructionFireAndProceed [MISFIRE_INSTRUCTION_FIRE_ONCE_NOW](默认)
——以当前时间为触发频率立刻触发一次执行
——然后按照Cron频率依次执行
withMisfireHandlingInstructionDoNothing [MISFIRE_INSTRUCTION_DO_NOTHING ]
——不触发立即执行
——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
withMisfireHandlingInstructionIgnoreMisfires [MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY]
——以错过的第一个频率时间立刻开始执行
——重做错过的所有频率周期后
——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行
即: 忽略所有的超时状态,按照触发器的策略执行。
WithSimpleSchedule
withMisfireHandlingInstructionNowWithExistingCount(默认)
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
withMisfireHandlingInstructionFireNow
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
withMisfireHandlingInstructionIgnoreMisfires
——以错过的第一个频率时间立刻开始执行
——重做错过的所有频率周期
——当下一次触发频率发生时间大于当前时间以后,按照Interval的依次执行剩下的频率
——共执行RepeatCount+1次
withMisfireHandlingInstructionNextWithExistingCount
——不触发立即执行
——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数
——以startTime为基准计算周期频率,并得到FinalTime
——即使中间出现pause,resume以后保持FinalTime时间不变
withMisfireHandlingInstructionNextWithRemainingCount
——不触发立即执行
——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数
——以startTime为基准计算周期频率,并得到FinalTime
——即使中间出现pause,resume以后保持FinalTime时间不变
withMisfireHandlingInstructionNowWithRemainingCount
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
——此指令导致trigger忘记原始设置的starttime和repeat-count
——触发器的repeat-count将被设置为剩余的次数
——这样会导致后面无法获得原始设定的starttime和repeat-count值
*/
if (jobTrigger.StartTime == null)
jobTrigger.StartTime = DateTime.Now;
DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(jobTrigger.StartTime, 1);
TriggerBuilder triggerBuilder = TriggerBuilder.Create()
.WithIdentity(jobTrigger.TriggerId!, jobDetail.GroupName!);
if (jobTrigger.TriggerType == Entity.TriggerTypeEnum.DailyAt)
{
string[] times = jobTrigger.Args!.Split(":");
if (times.Length == 1)
{
if (!jobTrigger.RunOnStart)
triggerBuilder = triggerBuilder.StartAt(starRunTime).WithCronSchedule($"0 0 {times[0]} * * ?", x => x.WithMisfireHandlingInstructionDoNothing());
else
triggerBuilder = triggerBuilder.StartAt(starRunTime).WithCronSchedule($"0 0 {times[0]} * * ?", x => x.WithMisfireHandlingInstructionFireAndProceed());
}
else
{
if (!jobTrigger.RunOnStart)
triggerBuilder = triggerBuilder.StartAt(starRunTime).WithCronSchedule($"0 {times[1]} {times[0]} * * ?", x => x.WithMisfireHandlingInstructionDoNothing());
else
triggerBuilder = triggerBuilder.StartAt(starRunTime).WithCronSchedule($"0 {times[1]} {times[0]} * * ?", x => x.WithMisfireHandlingInstructionFireAndProceed());
}
}
else if (jobTrigger.TriggerType == Entity.TriggerTypeEnum.PeriodMinutes)
{
int interval = jobTrigger.Args!.ToInt();
if (!jobTrigger.RunOnStart)
starRunTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddMinutes(interval), 1);
triggerBuilder = triggerBuilder.StartAt(starRunTime)
.WithSimpleSchedule(x => x
.WithIntervalInMinutes(interval) // 每xm分钟重复一次
.RepeatForever()); // 无限重复;
}
else if (jobTrigger.TriggerType == Entity.TriggerTypeEnum.PeriodSeconds)
{
int interval = jobTrigger.Args!.ToInt();
if (!jobTrigger.RunOnStart)
starRunTime = DateBuilder.NextGivenSecondDate(DateTime.Now.AddSeconds(interval), 1);
triggerBuilder = triggerBuilder.WithSimpleSchedule(x => x
.WithIntervalInSeconds(interval) // 每x秒重复一次
.RepeatForever()); // 无限重复;
}
else if (jobTrigger.TriggerType == Entity.TriggerTypeEnum.Cron)
{
if (!jobTrigger.RunOnStart)
triggerBuilder = triggerBuilder.StartAt(starRunTime).WithCronSchedule(jobTrigger.Args!, x => x.WithMisfireHandlingInstructionDoNothing());
else
triggerBuilder = triggerBuilder.StartAt(starRunTime).WithCronSchedule(jobTrigger.Args!, x => x.WithMisfireHandlingInstructionFireAndProceed());
}
else
throw Oops.Oh("AutoJob不支持的触发器类型!");
return triggerBuilder!.Build();
}
#endregion
}
}