开箱即用.NetCore + Quartz.Net + Vue定时任务管理UI

时间:2024-01-24 18:01:32

 Quartz.NET

  • Quartz.NET是一个功能齐全的开源作业调度系统,可用于从最小的应用程序到大型企业系统。
  • Quartz.NET是一个用C#编写的纯.NET库,是对JAVA开源调度框架Quartz的移植。目前支持.Net Core
  • Quartz.NET官方地址:https://www.quartz-scheduler.net/
  • Quartz.NetUI维护了常用作业添加、删除、修改、停止、启动功能,直接使用cron表达式设置作业执行间隔,有完整的日志记录。

 1.Quartz.NetUI 开发环境

.net core2.1及以上版本、vs2017、Quartz.NET 3.0.7 、 vue 2.0 、IView

 2.开箱即用、不依赖数据库

  • 直接运行Quartz.NetUI\Quartz.NET.Web目录下run.bat文件或部署项目
  • 登陆口令token位于appsettings.json节点token
  • 管理员帐号位于appsettings.json节点superToken

 3.主要代码文件

  • QuartzSettings                        文件夹由系统自动生成,与项目文件夹同级,存放作业配置信息及日志信息(发布时不需要发布或复制此文件夹)
  • TaskAuthorizeFilter.cs             帐号权限、AllowAnonymous过滤
  • QuartzNETExtension.cs         处理作业添加、删除、修改、停止、启动功能
  • FileQuartz.cs                          集中处理作业相关日志
  • HttpManager.cs                      接口处理
  • HealthController                      对外开放的健康检查接口,判断作业站点是否处理活动状态。
  • TaskOptions.cs                       作业相关字段
  • TaskBackGround/Index.cshtml     作业UI
  • task-index.js                            前端Vue+IView

4.在线演示地址

  • http://task.volcore.xyz 登陆口令:task123456
  • GitHub:https://github.com/cq-panda/Quartz.NetUI

5、作业添加、删除、修改、停止、启动功能

 

        /// <summary>
        /// 添加作业
        /// </summary>
        /// <param name="taskOptions"></param>
        /// <param name="schedulerFactory"></param>
        /// <param name="init">是否初始化,否=需要重新生成配置文件,是=不重新生成配置文件</param>
        /// <returns></returns>
        public static async Task<object> AddJob(this TaskOptions taskOptions, ISchedulerFactory schedulerFactory, bool init = false)
        {
            try
            {
                (bool, string) validExpression = taskOptions.Interval.IsValidExpression();
                if (!validExpression.Item1)
                    return new { status = false, msg = validExpression.Item2 };

                (bool, object) result = taskOptions.Exists(init);
                if (!result.Item1)
                    return result.Item2;
                if (!init)
                {
                    _taskList.Add(taskOptions);
                    FileQuartz.WriteJobConfig(_taskList);
                }

                IJobDetail job = JobBuilder.Create<HttpResultful>()
               .WithIdentity(taskOptions.TaskName, taskOptions.GroupName)
              .Build();
                ITrigger trigger = TriggerBuilder.Create()
                   .WithIdentity(taskOptions.TaskName, taskOptions.GroupName)
                   .StartNow().WithDescription(taskOptions.Describe)
                   .WithCronSchedule(taskOptions.Interval)
                   .Build();
                IScheduler scheduler = await schedulerFactory.GetScheduler();
                await scheduler.ScheduleJob(job, trigger);
                if (taskOptions.Status == (int)TriggerState.Normal)
                {
                    await scheduler.Start();
                }
                else
                {
                    await schedulerFactory.Pause(taskOptions);
                    FileQuartz.WriteStartLog($"作业:{taskOptions.TaskName},分组:{taskOptions.GroupName},新建时未启动原因,状态为:{taskOptions.Status}");
                }
                if (!init)
                    FileQuartz.WriteJobAction(JobAction.新增, taskOptions.TaskName, taskOptions.GroupName);
            }
            catch (Exception ex)
            {
                return new { status = false, msg = ex.Message };
            }
            return new { status = true };
        }

        /// <summary>
        /// 移除作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskName"></param>
        /// <param name="groupName"></param>
        /// <returns></returns>
        public static Task<object> Remove(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.TaskName, taskOptions.GroupName, JobAction.删除, taskOptions);
        }

        /// <summary>
        /// 更新作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Update(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.TaskName, taskOptions.GroupName, JobAction.修改, taskOptions);
        }

        /// <summary>
        /// 暂停作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Pause(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.TaskName, taskOptions.GroupName, JobAction.暂停, taskOptions);
        }

        /// <summary>
        /// 启动作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Start(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.TaskName, taskOptions.GroupName, JobAction.开启, taskOptions);
        }

        /// <summary>
        /// 立即执行一次作业
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static Task<object> Run(this ISchedulerFactory schedulerFactory, TaskOptions taskOptions)
        {
            return schedulerFactory.TriggerAction(taskOptions.TaskName, taskOptions.GroupName, JobAction.立即执行, taskOptions);
        }

        public static object ModifyTaskEntity(this TaskOptions taskOptions, ISchedulerFactory schedulerFactory, JobAction action)
        {
            TaskOptions options = null;
            object result = null;
            switch (action)
            {
                case JobAction.删除:
                    for (int i = 0; i < _taskList.Count; i++)
                    {
                        options = _taskList[i];
                        if (options.TaskName == taskOptions.TaskName && options.GroupName == taskOptions.GroupName)
                        {
                            _taskList.RemoveAt(i);
                        }
                    }
                    break;
                case JobAction.修改:
                    options = _taskList.Where(x => x.TaskName == taskOptions.TaskName && x.GroupName == taskOptions.GroupName).FirstOrDefault();
                    //移除以前的配置
                    if (options != null)
                    {
                        _taskList.Remove(options);
                    }

                    //生成任务并添加新配置
                    result = taskOptions.AddJob(schedulerFactory, false).GetAwaiter().GetResult();
                    break;
                case JobAction.暂停:
                case JobAction.开启:
                case JobAction.停止:
                case JobAction.立即执行:
                    options = _taskList.Where(x => x.TaskName == taskOptions.TaskName && x.GroupName == taskOptions.GroupName).FirstOrDefault();
                    if (action == JobAction.暂停)
                    {
                        options.Status = (int)TriggerState.Paused;
                    }
                    else if (action == JobAction.停止)
                    {
                        options.Status = (int)action;
                    }
                    else
                    {
                        options.Status = (int)TriggerState.Normal;
                    }
                    break;
            }
            //生成配置文件
            FileQuartz.WriteJobConfig(_taskList);
            FileQuartz.WriteJobAction(action, taskOptions.TaskName, taskOptions.GroupName, "操作对象:"+JsonConvert.SerializeObject(taskOptions));
            return result;
        }

        /// <summary>
        /// 触发新增、删除、修改、暂停、启用、立即执行事件
        /// </summary>
        /// <param name="schedulerFactory"></param>
        /// <param name="taskName"></param>
        /// <param name="groupName"></param>
        /// <param name="action"></param>
        /// <param name="taskOptions"></param>
        /// <returns></returns>
        public static async Task<object> TriggerAction(this ISchedulerFactory schedulerFactory, string taskName, string groupName, JobAction action, TaskOptions taskOptions = null)
        {
            string errorMsg = "";
            try
            {
                IScheduler scheduler = await schedulerFactory.GetScheduler();
                List<JobKey> jobKeys = scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName)).Result.ToList();
                if (jobKeys == null || jobKeys.Count() == 0)
                {
                    errorMsg = $"未找到分组[{groupName}]";
                    return new { status = false, msg = errorMsg };
                }
                JobKey jobKey = jobKeys.Where(s => scheduler.GetTriggersOfJob(s).Result.Any(x => (x as CronTriggerImpl).Name == taskName)).FirstOrDefault();
                if (jobKey == null)
                {
                    errorMsg = $"未找到触发器[{taskName}]";
                    return new { status = false, msg = errorMsg };
                }
                var triggers = await scheduler.GetTriggersOfJob(jobKey);
                ITrigger trigger = triggers?.Where(x => (x as CronTriggerImpl).Name == taskName).FirstOrDefault();

                if (trigger == null)
                {
                    errorMsg = $"未找到触发器[{taskName}]";
                    return new { status = false, msg = errorMsg };
                }
                object result = null;
                switch (action)
                {
                    case JobAction.删除:
                    case JobAction.修改:
                        await scheduler.PauseTrigger(trigger.Key);
                        await scheduler.UnscheduleJob(trigger.Key);// 移除触发器
                        await scheduler.DeleteJob(trigger.JobKey);
                        result = taskOptions.ModifyTaskEntity(schedulerFactory, action);
                        break;
                    case JobAction.暂停:
                    case JobAction.停止:
                    case JobAction.开启:
                        result = taskOptions.ModifyTaskEntity(schedulerFactory, action);
                        if (action == JobAction.暂停)
                        {
                            await scheduler.PauseTrigger(trigger.Key);
                        }
                        else if (action == JobAction.开启)
                        {
                            await scheduler.ResumeTrigger(trigger.Key);
                            //   await scheduler.RescheduleJob(trigger.Key, trigger);
                        }
                        else
                        {
                            await scheduler.Shutdown();
                        }
                        break;
                    case JobAction.立即执行:
                        await scheduler.TriggerJob(jobKey);
                        break;
                }
                return result ?? new { status = true, msg = $"作业{action.ToString()}成功" };
            }
            catch (Exception ex)
            {
                errorMsg = ex.Message;
                return new { status = false, msg = ex.Message };
            }
            finally
            {
                FileQuartz.WriteJobAction(action, taskName, groupName, errorMsg);
            }
        }

 

  

 

作业UI部份截图

作业列表

新建作业

 

修改作业

查看日志

配置文件QuartzSettings

配置文件QuartzSettings由系统自动生成,所在位置与当前项目同级,生成文件包括作业参数配置及日志文件初始化

配置文件目录结构

├─Constant

│      QuartzFileInfo.cs

│      

├─Controllers

│      HealthController.cs

│      HomeController.cs

│      TaskBackGroundController.cs

│      

├─Enum

│      JobAction.cs

│      

├─Extensions

│      ConvertPath.cs

│      QuartzNETExtension.cs

│      

├─Filters

│      TaskAuthorizeFilter.cs

│      

├─Models

│      TaskLog.cs

│      TaskOptions.cs

│      

├─Utility

│      FileHelper.cs

│      FileQuartz.cs  

│      HttpManager.cs

│      HttpResultful.cs

│      TaskCurrent.cs

│      

├─Views  

│─TaskBackGround

│          Index.cshtml

│          

└─wwwroot

    │      task_index.css       

    │      

    ├─iView

    │      iview.min.js

    │      

    ├─js

    │      task-index.js               

    └─vue

            vue.js