using Dapper;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using VueWebCoreApi.Quartz;
using VueWebCoreApi.Tools;
namespace VueWebCoreApi.Extensions
{
///
/// Quartz.NET 扩展方法类
/// 封装Quartz任务的初始化、增删改查、启停、立即执行等核心操作
/// 解决了Scheduler启动、内存与数据库状态同步、Cron表达式兼容、Key匹配等核心问题
///
public static class QuartzNETExtension
{
///
/// 全局返回信息对象(统一接口返回格式)
/// 包含code(状态码)、count(数量)、message(提示信息)、data(数据)
///
public static ToMessage mes = new ToMessage();
///
/// 内存级任务列表缓存
/// 用于减少数据库查询,同时保证内存与数据库状态一致
///
private static List _taskList = new List();
///
/// 扩展IApplicationBuilder,初始化Quartz任务调度器并加载所有任务
/// 【核心修复】解决Quartz默认不启动Scheduler的问题
///
/// ASP.NET Core应用构建器
/// Web主机环境
/// 应用构建器(链式调用)
public static IApplicationBuilder UseQuartz(this IApplicationBuilder app, IWebHostEnvironment env)
{
// 从DI容器获取依赖服务
var services = app.ApplicationServices;
// Quartz调度器工厂
var schedulerFactory = services.GetService();
// 自定义Quartz仓储(数据库操作)
var quartzRepo = services.GetService();
// 1. 核心修复:Quartz默认不会自动启动Scheduler,必须显式启动
var scheduler = schedulerFactory.GetScheduler().GetAwaiter().GetResult();
if (!scheduler.IsStarted)
{
scheduler.Start().GetAwaiter().GetResult();
}
// 2. 从数据库加载所有配置的任务
var taskList = quartzRepo.GetAllTasksAsync().GetAwaiter().GetResult();
if (taskList.Count == 0)
{
//无任务时记录启动日志
quartzRepo.WriteStartLogAsync($"{DateTime.Now:yyyy-MM-dd HH:mm:ss},没有默认配置任务").GetAwaiter().GetResult();
return app;
}
// 3. 遍历任务并初始化(记录失败数和异常信息)
int errorCount = 0;
string errorMsg = string.Empty;
// 初始化内存任务列表
_taskList = taskList;
foreach (var task in taskList)
{
try
{
//var result = task.AddJob(schedulerFactory, true, services.GetService()).GetAwaiter().GetResult();
// 调用TaskOptions的AddJob方法添加任务到调度器
// 传入仓储用于数据库操作,传入JobFactory用于自定义Job实例化
var result = task.AddJob(schedulerFactory, true, services.GetService(), quartzRepo).GetAwaiter().GetResult();
}
catch (Exception ex)
{
// 记录单个任务初始化失败信息
errorCount++;
errorMsg += $"作业:{task.TaskName},异常:{ex.Message};";
}
}
// 4. 记录整体初始化结果日志
var content = $"成功:{taskList.Count - errorCount}个,失败{errorCount}个,异常:{errorMsg}";
quartzRepo.WriteStartLogAsync($"{DateTime.Now:yyyy-MM-dd HH:mm:ss},{content}").GetAwaiter().GetResult();
return app;
}
///
/// 获取调度器中所有任务,并同步内存/数据库状态(核心修复:解决内存列表与数据库不一致问题)
///
/// 调度器工厂
/// Quartz仓储
/// 最新的任务列表
public static async Task> GetJobs(this ISchedulerFactory schedulerFactory, QuartzRepository quartzRepo)
{
var list = new List();
try
{
var scheduler = await schedulerFactory.GetScheduler();
// 确保Scheduler处于启动状态(防御性编程)
if (!scheduler.IsStarted) await scheduler.Start();
// 1. 获取所有任务分组(Quartz任务按分组管理)
var groups = await scheduler.GetJobGroupNames();
foreach (var groupName in groups)
{
// 2. 获取分组下所有JobKey(任务唯一标识:名称+分组)
var jobKeys = await scheduler.GetJobKeys(GroupMatcher.GroupEquals(groupName));
foreach (var jobKey in jobKeys)
{
// 3. 核心修复:优先从数据库获取最新任务(避免内存数据过期)
var dbTask = await quartzRepo.TaskExists(jobKey.Name, jobKey.Group);
// 数据库无数据时,降级从内存列表获取
var task = dbTask.FirstOrDefault() ?? _taskList.FirstOrDefault(x => x.GroupName == jobKey.Group && x.TaskName == jobKey.Name);
// 过滤无效任务
if (task == null) continue;
// 4. 获取任务关联的触发器(Quartz任务通过触发器触发执行)
var triggers = await scheduler.GetTriggersOfJob(jobKey);
foreach (var trigger in triggers)
{
// 5. 同步触发器实际状态到任务对象,并更新到数据库
task.Status = (int)await scheduler.GetTriggerState(trigger.Key);
await quartzRepo.UpdateTaskAsync(task); // 状态同步到库
// 6. 同步任务最后执行时间
if (trigger.GetPreviousFireTimeUtc() != null)
{
// 优先从触发器获取最后执行时间(UTC转本地时间)
task.LastRunTime = trigger.GetPreviousFireTimeUtc()?.LocalDateTime;
await quartzRepo.UpdateTaskLastRunTimeAsync(task.TaskName, task.GroupName, task.LastRunTime.Value);
}
else
{
// 触发器无记录时,从执行日志中获取最后执行时间
var logs = await quartzRepo.GetJobRunLogAsync(task.TaskName, task.GroupName, 1, 1);
if (logs.Count > 0 && DateTime.TryParse(logs[0].BeginDate, out var lastRunTime))
{
task.LastRunTime = lastRunTime;
await quartzRepo.UpdateTaskLastRunTimeAsync(task.TaskName, task.GroupName, lastRunTime);
}
}
}
list.Add(task);
}
}
// 7. 同步内存列表为最新数据(保证后续操作使用最新状态)
_taskList = list;
}
catch (Exception ex)
{
mes.code = "300";
mes.count = 0;
mes.message = ex.Message + ex.StackTrace;
mes.data = null;
await quartzRepo.WriteStartLogAsync($"获取作业异常:{ex.Message}{ex.StackTrace}");
return new List();
}
return list;
}
///
/// 新增Quartz任务(初始化/手动添加通用)
///
/// 任务配置项
/// 调度器工厂
/// 是否为初始化阶段(初始化时不重复入库)
/// Job工厂(自定义Job实例化逻辑)
/// Quartz仓储
/// 统一返回结果(ToMessage)
public static async Task