From 87c937a385424b5c4fc8a2b3c04b4248b7b7c065 Mon Sep 17 00:00:00 2001
From: sikadi <13315935+sikadi_love@user.noreply.gitee.com>
Date: Sun, 19 Nov 2023 20:37:07 +0800
Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 46 ++++
Dockerfile | 20 ++
pom.xml | 101 ++++++++
.../java/com/fate/job/FateJobApplication.java | 22 ++
.../com/fate/job/config/ScheduleConfig.java | 57 +++++
.../fate/job/controller/SysJobController.java | 149 +++++++++++
.../job/controller/SysJobLogController.java | 81 ++++++
src/main/java/com/fate/job/domain/SysJob.java | 179 +++++++++++++
.../java/com/fate/job/domain/SysJobLog.java | 165 ++++++++++++
.../com/fate/job/mapper/SysJobLogMapper.java | 70 +++++
.../com/fate/job/mapper/SysJobMapper.java | 74 ++++++
.../fate/job/service/ISysJobLogService.java | 60 +++++
.../com/fate/job/service/ISysJobService.java | 113 +++++++++
.../job/service/SysJobLogServiceImpl.java | 83 ++++++
.../fate/job/service/SysJobServiceImpl.java | 239 ++++++++++++++++++
src/main/java/com/fate/job/task/MyTask.java | 24 ++
.../com/fate/job/util/AbstractQuartzJob.java | 97 +++++++
.../java/com/fate/job/util/CronUtils.java | 56 ++++
.../java/com/fate/job/util/JobInvokeUtil.java | 165 ++++++++++++
.../QuartzDisallowConcurrentExecution.java | 18 ++
.../com/fate/job/util/QuartzJobExecution.java | 16 ++
.../java/com/fate/job/util/ScheduleUtils.java | 121 +++++++++
src/main/resources/banner.txt | 2 +
src/main/resources/bootstrap.yml | 25 ++
src/main/resources/logback.xml | 74 ++++++
.../resources/mapper/job/SysJobLogMapper.xml | 102 ++++++++
.../resources/mapper/job/SysJobMapper.xml | 123 +++++++++
27 files changed, 2282 insertions(+)
create mode 100644 .gitignore
create mode 100644 Dockerfile
create mode 100644 pom.xml
create mode 100644 src/main/java/com/fate/job/FateJobApplication.java
create mode 100644 src/main/java/com/fate/job/config/ScheduleConfig.java
create mode 100644 src/main/java/com/fate/job/controller/SysJobController.java
create mode 100644 src/main/java/com/fate/job/controller/SysJobLogController.java
create mode 100644 src/main/java/com/fate/job/domain/SysJob.java
create mode 100644 src/main/java/com/fate/job/domain/SysJobLog.java
create mode 100644 src/main/java/com/fate/job/mapper/SysJobLogMapper.java
create mode 100644 src/main/java/com/fate/job/mapper/SysJobMapper.java
create mode 100644 src/main/java/com/fate/job/service/ISysJobLogService.java
create mode 100644 src/main/java/com/fate/job/service/ISysJobService.java
create mode 100644 src/main/java/com/fate/job/service/SysJobLogServiceImpl.java
create mode 100644 src/main/java/com/fate/job/service/SysJobServiceImpl.java
create mode 100644 src/main/java/com/fate/job/task/MyTask.java
create mode 100644 src/main/java/com/fate/job/util/AbstractQuartzJob.java
create mode 100644 src/main/java/com/fate/job/util/CronUtils.java
create mode 100644 src/main/java/com/fate/job/util/JobInvokeUtil.java
create mode 100644 src/main/java/com/fate/job/util/QuartzDisallowConcurrentExecution.java
create mode 100644 src/main/java/com/fate/job/util/QuartzJobExecution.java
create mode 100644 src/main/java/com/fate/job/util/ScheduleUtils.java
create mode 100644 src/main/resources/banner.txt
create mode 100644 src/main/resources/bootstrap.yml
create mode 100644 src/main/resources/logback.xml
create mode 100644 src/main/resources/mapper/job/SysJobLogMapper.xml
create mode 100644 src/main/resources/mapper/job/SysJobMapper.xml
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8b4f1fe
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,46 @@
+Dockerfile######################################################################
+# Build Tools
+
+.gradle
+/build/
+!gradle/wrapper/gradle-wrapper.jar
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+
+######################################################################
+# IDE
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### JRebel ###
+rebel.xml
+### NetBeans ###
+nbproject/private/
+build/*
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+######################################################################
+# Others
+*.log
+*.xml.versionsBackup
+*.swp
+
+!*/build/*.java
+!*/build/*.html
+!*/build/*.xml
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..d1c90ad
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+FROM anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/openjdk:17-8.6
+
+
+# 暴露端口号
+EXPOSE 9203/tcp
+
+
+# 挂载目录位置
+VOLUME /home/logs/fate-job
+
+#构造 复制外部文件到docker 内部
+COPY target/fate-modules-job.jar /home/app.jar
+
+# 工作目录 exec -it 进来就是默认这个目录1gitn1
+WORKDIR /home
+
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > .etc.timezone
+
+# 启动java程序
+CMD ["java","-Dfile.encoding=UTF-8","-jar","/home/app.jar"]
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a897c06
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,101 @@
+
+
+
+ com.fate
+ fate-modules
+ 3.6.3
+
+ 4.0.0
+
+ fate-modules-job
+
+
+ fate-modules-job定时任务
+
+ 3.6.3
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-sentinel
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ ${swagger.fox.version}
+
+
+
+
+ org.quartz-scheduler
+ quartz
+
+
+ com.mchange
+ c3p0
+
+
+
+
+
+
+ com.mysql
+ mysql-connector-j
+ 8.0.33
+
+
+
+
+ com.fate
+ fate-common-log
+
+
+
+
+ com.fate
+ fate-common-swagger
+
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/fate/job/FateJobApplication.java b/src/main/java/com/fate/job/FateJobApplication.java
new file mode 100644
index 0000000..3ee72e4
--- /dev/null
+++ b/src/main/java/com/fate/job/FateJobApplication.java
@@ -0,0 +1,22 @@
+package com.fate.job;
+
+import com.fate.common.security.annotation.EnableCustomConfig;
+import com.fate.common.security.annotation.EnableMyFeignClients;
+import com.fate.common.swagger.annotation.EnableCustomSwagger2;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/**
+ * 定时任务
+ *
+ * @author fate
+ */
+@EnableCustomConfig
+@EnableCustomSwagger2
+@EnableMyFeignClients
+@SpringBootApplication
+public class FateJobApplication {
+ public static void main (String[] args) {
+ SpringApplication.run(FateJobApplication.class, args);
+ }
+}
diff --git a/src/main/java/com/fate/job/config/ScheduleConfig.java b/src/main/java/com/fate/job/config/ScheduleConfig.java
new file mode 100644
index 0000000..1c69b91
--- /dev/null
+++ b/src/main/java/com/fate/job/config/ScheduleConfig.java
@@ -0,0 +1,57 @@
+//package com.fate.job.config;
+//
+//import java.util.Properties;
+//import javax.sql.DataSource;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+//
+///**
+// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效)
+// *
+// * @author fate
+// */
+//@Configuration
+//public class ScheduleConfig
+//{
+// @Bean
+// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource)
+// {
+// SchedulerFactoryBean factory = new SchedulerFactoryBean();
+// factory.setDataSource(dataSource);
+//
+// // quartz参数
+// Properties prop = new Properties();
+// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
+// prop.put("org.quartz.scheduler.instanceId", "AUTO");
+// // 线程池配置
+// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
+// prop.put("org.quartz.threadPool.threadCount", "20");
+// prop.put("org.quartz.threadPool.threadPriority", "5");
+// // JobStore配置
+// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
+// // 集群配置
+// prop.put("org.quartz.jobStore.isClustered", "true");
+// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
+// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
+// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
+//
+// // sqlserver 启用
+// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
+// prop.put("org.quartz.jobStore.misfireThreshold", "12000");
+// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
+// factory.setQuartzProperties(prop);
+//
+// factory.setSchedulerName("RuoyiScheduler");
+// // 延时启动
+// factory.setStartupDelay(1);
+// factory.setApplicationContextSchedulerContextKey("applicationContextKey");
+// // 可选,QuartzScheduler
+// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
+// factory.setOverwriteExistingJobs(true);
+// // 设置自动启动,默认为true
+// factory.setAutoStartup(true);
+//
+// return factory;
+// }
+//}
diff --git a/src/main/java/com/fate/job/controller/SysJobController.java b/src/main/java/com/fate/job/controller/SysJobController.java
new file mode 100644
index 0000000..b7e6e60
--- /dev/null
+++ b/src/main/java/com/fate/job/controller/SysJobController.java
@@ -0,0 +1,149 @@
+package com.fate.job.controller;
+
+import com.fate.common.core.constant.Constants;
+import com.fate.common.core.exception.job.TaskException;
+import com.fate.common.core.utils.StringUtils;
+import com.fate.common.core.utils.poi.ExcelUtil;
+import com.fate.common.core.web.controller.BaseController;
+import com.fate.common.core.domain.Result;
+import com.fate.common.core.web.page.TableDataInfo;
+import com.fate.common.log.annotation.Log;
+import com.fate.common.log.enums.BusinessType;
+import com.fate.common.security.annotation.RequiresPermissions;
+import com.fate.common.security.utils.SecurityUtils;
+import com.fate.job.domain.SysJob;
+import com.fate.job.service.ISysJobService;
+import com.fate.job.util.CronUtils;
+import com.fate.job.util.ScheduleUtils;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 调度任务信息操作处理
+ *
+ * @author fate
+ */
+@RestController
+@RequestMapping("/job")
+public class SysJobController extends BaseController {
+ @Autowired
+ private ISysJobService jobService;
+
+ /**
+ * 查询定时任务列表
+ */
+ @RequiresPermissions("monitor:job:list")
+ @GetMapping("/list")
+ public Result> list (SysJob sysJob) {
+ startPage();
+ List list = jobService.selectJobList(sysJob);
+ return getDataTable(list);
+ }
+
+ /**
+ * 导出定时任务列表
+ */
+ @RequiresPermissions("monitor:job:export")
+ @Log(title = "定时任务", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export (HttpServletResponse response, SysJob sysJob) {
+ List list = jobService.selectJobList(sysJob);
+ ExcelUtil util = new ExcelUtil(SysJob.class);
+ util.exportExcel(response, list, "定时任务");
+ }
+
+ /**
+ * 获取定时任务详细信息
+ */
+ @RequiresPermissions("monitor:job:query")
+ @GetMapping(value = "/{jobId}")
+ public Result getInfo (@PathVariable("jobId") Long jobId) {
+ return success(jobService.selectJobById(jobId));
+ }
+
+ /**
+ * 新增定时任务
+ */
+ @RequiresPermissions("monitor:job:add")
+ @Log(title = "定时任务", businessType = BusinessType.INSERT)
+ @PostMapping
+ public Result add (@RequestBody SysJob job) throws SchedulerException, TaskException {
+ if (!CronUtils.isValid(job.getCronExpression())) {
+ return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确");
+ } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
+ } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS})) {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
+ } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
+ } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规");
+ } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) {
+ return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
+ }
+ job.setCreateBy(SecurityUtils.getUsername());
+ return toAjax(jobService.insertJob(job));
+ }
+
+ /**
+ * 修改定时任务
+ */
+ @RequiresPermissions("monitor:job:edit")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping
+ public Result edit (@RequestBody SysJob job) throws SchedulerException, TaskException {
+ if (!CronUtils.isValid(job.getCronExpression())) {
+ return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确");
+ } else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用");
+ } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS})) {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用");
+ } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[]{Constants.HTTP, Constants.HTTPS})) {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用");
+ } else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规");
+ } else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) {
+ return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内");
+ }
+ job.setUpdateBy(SecurityUtils.getUsername());
+ return toAjax(jobService.updateJob(job));
+ }
+
+ /**
+ * 定时任务状态修改
+ */
+ @RequiresPermissions("monitor:job:changeStatus")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping("/changeStatus")
+ public Result changeStatus (@RequestBody SysJob job) throws SchedulerException {
+ SysJob newJob = jobService.selectJobById(job.getJobId());
+ newJob.setStatus(job.getStatus());
+ return toAjax(jobService.changeStatus(newJob));
+ }
+
+ /**
+ * 定时任务立即执行一次
+ */
+ @RequiresPermissions("monitor:job:changeStatus")
+ @Log(title = "定时任务", businessType = BusinessType.UPDATE)
+ @PutMapping("/run")
+ public Result run (@RequestBody SysJob job) throws SchedulerException {
+ boolean result = jobService.run(job);
+ return result ? success() : error("任务不存在或已过期!");
+ }
+
+ /**
+ * 删除定时任务
+ */
+ @RequiresPermissions("monitor:job:remove")
+ @Log(title = "定时任务", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{jobIds}")
+ public Result remove (@PathVariable Long[] jobIds) throws SchedulerException, TaskException {
+ jobService.deleteJobByIds(jobIds);
+ return success();
+ }
+}
diff --git a/src/main/java/com/fate/job/controller/SysJobLogController.java b/src/main/java/com/fate/job/controller/SysJobLogController.java
new file mode 100644
index 0000000..25ece4c
--- /dev/null
+++ b/src/main/java/com/fate/job/controller/SysJobLogController.java
@@ -0,0 +1,81 @@
+package com.fate.job.controller;
+
+import com.fate.common.core.utils.poi.ExcelUtil;
+import com.fate.common.core.web.controller.BaseController;
+import com.fate.common.core.domain.Result;
+import com.fate.common.core.web.page.TableDataInfo;
+import com.fate.common.log.annotation.Log;
+import com.fate.common.log.enums.BusinessType;
+import com.fate.common.security.annotation.RequiresPermissions;
+import com.fate.job.domain.SysJobLog;
+import com.fate.job.service.ISysJobLogService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 调度日志操作处理
+ *
+ * @author fate
+ */
+@RestController
+@RequestMapping("/job/log")
+public class SysJobLogController extends BaseController {
+ @Autowired
+ private ISysJobLogService jobLogService;
+
+ /**
+ * 查询定时任务调度日志列表
+ */
+ @RequiresPermissions("monitor:job:list")
+ @GetMapping("/list")
+ public Result> list (SysJobLog sysJobLog) {
+ startPage();
+ List list = jobLogService.selectJobLogList(sysJobLog);
+ return getDataTable(list);
+ }
+
+ /**
+ * 导出定时任务调度日志列表
+ */
+ @RequiresPermissions("monitor:job:export")
+ @Log(title = "任务调度日志", businessType = BusinessType.EXPORT)
+ @PostMapping("/export")
+ public void export (HttpServletResponse response, SysJobLog sysJobLog) {
+ List list = jobLogService.selectJobLogList(sysJobLog);
+ ExcelUtil util = new ExcelUtil(SysJobLog.class);
+ util.exportExcel(response, list, "调度日志");
+ }
+
+ /**
+ * 根据调度编号获取详细信息
+ */
+ @RequiresPermissions("monitor:job:query")
+ @GetMapping(value = "/{jobLogId}")
+ public Result getInfo (@PathVariable Long jobLogId) {
+ return success(jobLogService.selectJobLogById(jobLogId));
+ }
+
+ /**
+ * 删除定时任务调度日志
+ */
+ @RequiresPermissions("monitor:job:remove")
+ @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE)
+ @DeleteMapping("/{jobLogIds}")
+ public Result remove (@PathVariable Long[] jobLogIds) {
+ return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
+ }
+
+ /**
+ * 清空定时任务调度日志
+ */
+ @RequiresPermissions("monitor:job:remove")
+ @Log(title = "调度日志", businessType = BusinessType.CLEAN)
+ @DeleteMapping("/clean")
+ public Result clean () {
+ jobLogService.cleanJobLog();
+ return success();
+ }
+}
diff --git a/src/main/java/com/fate/job/domain/SysJob.java b/src/main/java/com/fate/job/domain/SysJob.java
new file mode 100644
index 0000000..a1dea36
--- /dev/null
+++ b/src/main/java/com/fate/job/domain/SysJob.java
@@ -0,0 +1,179 @@
+package com.fate.job.domain;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fate.common.core.annotation.Excel;
+import com.fate.common.core.annotation.Excel.ColumnType;
+import com.fate.common.core.constant.ScheduleConstants;
+import com.fate.common.core.utils.StringUtils;
+import com.fate.common.core.web.domain.BaseEntity;
+import com.fate.job.util.CronUtils;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.Size;
+import java.util.Date;
+
+/**
+ * 定时任务调度表 sys_job
+ *
+ * @author fate
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SysJob extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 任务ID
+ */
+ @Excel(name = "任务序号", cellType = ColumnType.NUMERIC)
+ private Long jobId;
+
+ /**
+ * 任务名称
+ */
+ @Excel(name = "任务名称")
+ private String jobName;
+
+ /**
+ * 任务组名
+ */
+ @Excel(name = "任务组名")
+ private String jobGroup;
+
+ /**
+ * 调用目标字符串
+ */
+ @Excel(name = "调用目标字符串")
+ private String invokeTarget;
+
+ /**
+ * cron执行表达式
+ */
+ @Excel(name = "执行表达式 ")
+ private String cronExpression;
+
+ /**
+ * cron计划策略
+ */
+ @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
+ private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
+
+ /**
+ * 是否并发执行(0允许 1禁止)
+ */
+ @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
+ private String concurrent;
+
+ /**
+ * 任务状态(0正常 1暂停)
+ */
+ @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
+ private String status;
+
+ public Long getJobId () {
+ return jobId;
+ }
+
+ public void setJobId (Long jobId) {
+ this.jobId = jobId;
+ }
+
+ @NotBlank(message = "任务名称不能为空")
+ @Size(min = 0, max = 64, message = "任务名称不能超过64个字符")
+ public String getJobName () {
+ return jobName;
+ }
+
+ public void setJobName (String jobName) {
+ this.jobName = jobName;
+ }
+
+ public String getJobGroup () {
+ return jobGroup;
+ }
+
+ public void setJobGroup (String jobGroup) {
+ this.jobGroup = jobGroup;
+ }
+
+ @NotBlank(message = "调用目标字符串不能为空")
+ @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符")
+ public String getInvokeTarget () {
+ return invokeTarget;
+ }
+
+ public void setInvokeTarget (String invokeTarget) {
+ this.invokeTarget = invokeTarget;
+ }
+
+ @NotBlank(message = "Cron执行表达式不能为空")
+ @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
+ public String getCronExpression () {
+ return cronExpression;
+ }
+
+ public void setCronExpression (String cronExpression) {
+ this.cronExpression = cronExpression;
+ }
+
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ public Date getNextValidTime () {
+ if (StringUtils.isNotEmpty(cronExpression)) {
+ return CronUtils.getNextExecution(cronExpression);
+ }
+ return null;
+ }
+
+ public String getMisfirePolicy () {
+ return misfirePolicy;
+ }
+
+ public void setMisfirePolicy (String misfirePolicy) {
+ this.misfirePolicy = misfirePolicy;
+ }
+
+ public String getConcurrent () {
+ return concurrent;
+ }
+
+ public void setConcurrent (String concurrent) {
+ this.concurrent = concurrent;
+ }
+
+ public String getStatus () {
+ return status;
+ }
+
+ public void setStatus (String status) {
+ this.status = status;
+ }
+
+ @Override
+ public String toString () {
+ return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobId", getJobId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("cronExpression", getCronExpression())
+ .append("nextValidTime", getNextValidTime())
+ .append("misfirePolicy", getMisfirePolicy())
+ .append("concurrent", getConcurrent())
+ .append("status", getStatus())
+ .append("createBy", getCreateBy())
+ .append("createTime", getCreateTime())
+ .append("updateBy", getUpdateBy())
+ .append("updateTime", getUpdateTime())
+ .append("remark", getRemark())
+ .toString();
+ }
+}
diff --git a/src/main/java/com/fate/job/domain/SysJobLog.java b/src/main/java/com/fate/job/domain/SysJobLog.java
new file mode 100644
index 0000000..8a87e93
--- /dev/null
+++ b/src/main/java/com/fate/job/domain/SysJobLog.java
@@ -0,0 +1,165 @@
+package com.fate.job.domain;
+
+import com.fate.common.core.annotation.Excel;
+import com.fate.common.core.web.domain.BaseEntity;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+import java.util.Date;
+
+/**
+ * 定时任务调度日志表 sys_job_log
+ *
+ * @author fate
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class SysJobLog extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * ID
+ */
+ @Excel(name = "日志序号")
+ private Long jobLogId;
+
+ /**
+ * 任务名称
+ */
+ @Excel(name = "任务名称")
+ private String jobName;
+
+ /**
+ * 任务组名
+ */
+ @Excel(name = "任务组名")
+ private String jobGroup;
+
+ /**
+ * 调用目标字符串
+ */
+ @Excel(name = "调用目标字符串")
+ private String invokeTarget;
+
+ /**
+ * 日志信息
+ */
+ @Excel(name = "日志信息")
+ private String jobMessage;
+
+ /**
+ * 执行状态(0正常 1失败)
+ */
+ @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败")
+ private String status;
+
+ /**
+ * 异常信息
+ */
+ @Excel(name = "异常信息")
+ private String exceptionInfo;
+
+ /**
+ * 开始时间
+ */
+ private Date startTime;
+
+ /**
+ * 停止时间
+ */
+ private Date stopTime;
+
+ public Long getJobLogId () {
+ return jobLogId;
+ }
+
+ public void setJobLogId (Long jobLogId) {
+ this.jobLogId = jobLogId;
+ }
+
+ public String getJobName () {
+ return jobName;
+ }
+
+ public void setJobName (String jobName) {
+ this.jobName = jobName;
+ }
+
+ public String getJobGroup () {
+ return jobGroup;
+ }
+
+ public void setJobGroup (String jobGroup) {
+ this.jobGroup = jobGroup;
+ }
+
+ public String getInvokeTarget () {
+ return invokeTarget;
+ }
+
+ public void setInvokeTarget (String invokeTarget) {
+ this.invokeTarget = invokeTarget;
+ }
+
+ public String getJobMessage () {
+ return jobMessage;
+ }
+
+ public void setJobMessage (String jobMessage) {
+ this.jobMessage = jobMessage;
+ }
+
+ public String getStatus () {
+ return status;
+ }
+
+ public void setStatus (String status) {
+ this.status = status;
+ }
+
+ public String getExceptionInfo () {
+ return exceptionInfo;
+ }
+
+ public void setExceptionInfo (String exceptionInfo) {
+ this.exceptionInfo = exceptionInfo;
+ }
+
+ public Date getStartTime () {
+ return startTime;
+ }
+
+ public void setStartTime (Date startTime) {
+ this.startTime = startTime;
+ }
+
+ public Date getStopTime () {
+ return stopTime;
+ }
+
+ public void setStopTime (Date stopTime) {
+ this.stopTime = stopTime;
+ }
+
+ @Override
+ public String toString () {
+ return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+ .append("jobLogId", getJobLogId())
+ .append("jobName", getJobName())
+ .append("jobGroup", getJobGroup())
+ .append("jobMessage", getJobMessage())
+ .append("status", getStatus())
+ .append("exceptionInfo", getExceptionInfo())
+ .append("startTime", getStartTime())
+ .append("stopTime", getStopTime())
+ .toString();
+ }
+}
diff --git a/src/main/java/com/fate/job/mapper/SysJobLogMapper.java b/src/main/java/com/fate/job/mapper/SysJobLogMapper.java
new file mode 100644
index 0000000..13703af
--- /dev/null
+++ b/src/main/java/com/fate/job/mapper/SysJobLogMapper.java
@@ -0,0 +1,70 @@
+package com.fate.job.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fate.job.domain.SysJobLog;
+
+import java.util.List;
+
+/**
+ * 调度任务日志信息 数据层
+ *
+ * @author fate
+ */
+public interface SysJobLogMapper extends BaseMapper {
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ *
+ * @return 调度任务日志集合
+ */
+ public List selectJobLogList (SysJobLog jobLog);
+
+ /**
+ * 查询所有调度任务日志
+ *
+ * @return 调度任务日志列表
+ */
+ public List selectJobLogAll ();
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ *
+ * @return 调度任务日志对象信息
+ */
+ public SysJobLog selectJobLogById (Long jobLogId);
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ *
+ * @return 结果
+ */
+ public int insertJobLog (SysJobLog jobLog);
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的数据ID
+ *
+ * @return 结果
+ */
+ public int deleteJobLogByIds (Long[] logIds);
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ *
+ * @return 结果
+ */
+ public int deleteJobLogById (Long jobId);
+
+ /**
+ * 清空任务日志
+ */
+ public void cleanJobLog ();
+}
diff --git a/src/main/java/com/fate/job/mapper/SysJobMapper.java b/src/main/java/com/fate/job/mapper/SysJobMapper.java
new file mode 100644
index 0000000..5ac7e9b
--- /dev/null
+++ b/src/main/java/com/fate/job/mapper/SysJobMapper.java
@@ -0,0 +1,74 @@
+package com.fate.job.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fate.job.domain.SysJob;
+
+import java.util.List;
+
+/**
+ * 调度任务信息 数据层
+ *
+ * @author fate
+ */
+public interface SysJobMapper extends BaseMapper {
+ /**
+ * 查询调度任务日志集合
+ *
+ * @param job 调度信息
+ *
+ * @return 操作日志集合
+ */
+ public List selectJobList (SysJob job);
+
+ /**
+ * 查询所有调度任务
+ *
+ * @return 调度任务列表
+ */
+ public List selectJobAll ();
+
+ /**
+ * 通过调度ID查询调度任务信息
+ *
+ * @param jobId 调度ID
+ *
+ * @return 角色对象信息
+ */
+ public SysJob selectJobById (Long jobId);
+
+ /**
+ * 通过调度ID删除调度任务信息
+ *
+ * @param jobId 调度ID
+ *
+ * @return 结果
+ */
+ public int deleteJobById (Long jobId);
+
+ /**
+ * 批量删除调度任务信息
+ *
+ * @param ids 需要删除的数据ID
+ *
+ * @return 结果
+ */
+ public int deleteJobByIds (Long[] ids);
+
+ /**
+ * 修改调度任务信息
+ *
+ * @param job 调度任务信息
+ *
+ * @return 结果
+ */
+ public int updateJob (SysJob job);
+
+ /**
+ * 新增调度任务信息
+ *
+ * @param job 调度任务信息
+ *
+ * @return 结果
+ */
+ public int insertJob (SysJob job);
+}
diff --git a/src/main/java/com/fate/job/service/ISysJobLogService.java b/src/main/java/com/fate/job/service/ISysJobLogService.java
new file mode 100644
index 0000000..8a238d8
--- /dev/null
+++ b/src/main/java/com/fate/job/service/ISysJobLogService.java
@@ -0,0 +1,60 @@
+package com.fate.job.service;
+
+import com.fate.job.domain.SysJobLog;
+
+import java.util.List;
+
+/**
+ * 定时任务调度日志信息信息 服务层
+ *
+ * @author fate
+ */
+public interface ISysJobLogService {
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ *
+ * @return 调度任务日志集合
+ */
+ public List selectJobLogList (SysJobLog jobLog);
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ *
+ * @return 调度任务日志对象信息
+ */
+ public SysJobLog selectJobLogById (Long jobLogId);
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ */
+ public void addJobLog (SysJobLog jobLog);
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的日志ID
+ *
+ * @return 结果
+ */
+ public int deleteJobLogByIds (Long[] logIds);
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ *
+ * @return 结果
+ */
+ public int deleteJobLogById (Long jobId);
+
+ /**
+ * 清空任务日志
+ */
+ public void cleanJobLog ();
+}
diff --git a/src/main/java/com/fate/job/service/ISysJobService.java b/src/main/java/com/fate/job/service/ISysJobService.java
new file mode 100644
index 0000000..edc0dbb
--- /dev/null
+++ b/src/main/java/com/fate/job/service/ISysJobService.java
@@ -0,0 +1,113 @@
+package com.fate.job.service;
+
+import com.fate.common.core.exception.job.TaskException;
+import com.fate.job.domain.SysJob;
+import org.quartz.SchedulerException;
+
+import java.util.List;
+
+/**
+ * 定时任务调度信息信息 服务层
+ *
+ * @author fate
+ */
+public interface ISysJobService {
+ /**
+ * 获取quartz调度器的计划任务
+ *
+ * @param job 调度信息
+ *
+ * @return 调度任务集合
+ */
+ public List selectJobList (SysJob job);
+
+ /**
+ * 通过调度任务ID查询调度信息
+ *
+ * @param jobId 调度任务ID
+ *
+ * @return 调度任务对象信息
+ */
+ public SysJob selectJobById (Long jobId);
+
+ /**
+ * 暂停任务
+ *
+ * @param job 调度信息
+ *
+ * @return 结果
+ */
+ public int pauseJob (SysJob job) throws SchedulerException;
+
+ /**
+ * 恢复任务
+ *
+ * @param job 调度信息
+ *
+ * @return 结果
+ */
+ public int resumeJob (SysJob job) throws SchedulerException;
+
+ /**
+ * 删除任务后,所对应的trigger也将被删除
+ *
+ * @param job 调度信息
+ *
+ * @return 结果
+ */
+ public int deleteJob (SysJob job) throws SchedulerException;
+
+ /**
+ * 批量删除调度信息
+ *
+ * @param jobIds 需要删除的任务ID
+ *
+ * @return 结果
+ */
+ public void deleteJobByIds (Long[] jobIds) throws SchedulerException;
+
+ /**
+ * 任务调度状态修改
+ *
+ * @param job 调度信息
+ *
+ * @return 结果
+ */
+ public int changeStatus (SysJob job) throws SchedulerException;
+
+ /**
+ * 立即运行任务
+ *
+ * @param job 调度信息
+ *
+ * @return 结果
+ */
+ public boolean run (SysJob job) throws SchedulerException;
+
+ /**
+ * 新增任务
+ *
+ * @param job 调度信息
+ *
+ * @return 结果
+ */
+ public int insertJob (SysJob job) throws SchedulerException, TaskException;
+
+ /**
+ * 更新任务
+ *
+ * @param job 调度信息
+ *
+ * @return 结果
+ */
+ public int updateJob (SysJob job) throws SchedulerException, TaskException;
+
+ /**
+ * 校验cron表达式是否有效
+ *
+ * @param cronExpression 表达式
+ *
+ * @return 结果
+ */
+ public boolean checkCronExpressionIsValid (String cronExpression);
+}
diff --git a/src/main/java/com/fate/job/service/SysJobLogServiceImpl.java b/src/main/java/com/fate/job/service/SysJobLogServiceImpl.java
new file mode 100644
index 0000000..db61df8
--- /dev/null
+++ b/src/main/java/com/fate/job/service/SysJobLogServiceImpl.java
@@ -0,0 +1,83 @@
+package com.fate.job.service;
+
+import com.fate.job.domain.SysJobLog;
+import com.fate.job.mapper.SysJobLogMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 定时任务调度日志信息 服务层
+ *
+ * @author fate
+ */
+@Service
+public class SysJobLogServiceImpl implements ISysJobLogService {
+ @Autowired
+ private SysJobLogMapper jobLogMapper;
+
+ /**
+ * 获取quartz调度器日志的计划任务
+ *
+ * @param jobLog 调度日志信息
+ *
+ * @return 调度任务日志集合
+ */
+ @Override
+ public List selectJobLogList (SysJobLog jobLog) {
+ return jobLogMapper.selectJobLogList(jobLog);
+ }
+
+ /**
+ * 通过调度任务日志ID查询调度信息
+ *
+ * @param jobLogId 调度任务日志ID
+ *
+ * @return 调度任务日志对象信息
+ */
+ @Override
+ public SysJobLog selectJobLogById (Long jobLogId) {
+ return jobLogMapper.selectJobLogById(jobLogId);
+ }
+
+ /**
+ * 新增任务日志
+ *
+ * @param jobLog 调度日志信息
+ */
+ @Override
+ public void addJobLog (SysJobLog jobLog) {
+ jobLogMapper.insertJobLog(jobLog);
+ }
+
+ /**
+ * 批量删除调度日志信息
+ *
+ * @param logIds 需要删除的数据ID
+ *
+ * @return 结果
+ */
+ @Override
+ public int deleteJobLogByIds (Long[] logIds) {
+ return jobLogMapper.deleteJobLogByIds(logIds);
+ }
+
+ /**
+ * 删除任务日志
+ *
+ * @param jobId 调度日志ID
+ */
+ @Override
+ public int deleteJobLogById (Long jobId) {
+ return jobLogMapper.deleteJobLogById(jobId);
+ }
+
+ /**
+ * 清空任务日志
+ */
+ @Override
+ public void cleanJobLog () {
+ jobLogMapper.cleanJobLog();
+ }
+}
diff --git a/src/main/java/com/fate/job/service/SysJobServiceImpl.java b/src/main/java/com/fate/job/service/SysJobServiceImpl.java
new file mode 100644
index 0000000..c307aba
--- /dev/null
+++ b/src/main/java/com/fate/job/service/SysJobServiceImpl.java
@@ -0,0 +1,239 @@
+package com.fate.job.service;
+
+import com.fate.common.core.constant.ScheduleConstants;
+import com.fate.common.core.exception.job.TaskException;
+import com.fate.job.domain.SysJob;
+import com.fate.job.mapper.SysJobMapper;
+import com.fate.job.util.CronUtils;
+import com.fate.job.util.ScheduleUtils;
+import org.quartz.JobDataMap;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.PostConstruct;
+import java.util.List;
+
+/**
+ * 定时任务调度信息 服务层
+ *
+ * @author fate
+ */
+@Service
+public class SysJobServiceImpl implements ISysJobService {
+ @Autowired
+ private Scheduler scheduler;
+
+ @Autowired
+ private SysJobMapper jobMapper;
+
+ /**
+ * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
+ */
+ @PostConstruct
+ public void init () throws SchedulerException, TaskException {
+ scheduler.clear();
+ List jobList = jobMapper.selectJobAll();
+ for (SysJob job : jobList) {
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ }
+
+ /**
+ * 获取quartz调度器的计划任务列表
+ *
+ * @param job 调度信息
+ *
+ * @return
+ */
+ @Override
+ public List selectJobList (SysJob job) {
+ return jobMapper.selectJobList(job);
+ }
+
+ /**
+ * 通过调度任务ID查询调度信息
+ *
+ * @param jobId 调度任务ID
+ *
+ * @return 调度任务对象信息
+ */
+ @Override
+ public SysJob selectJobById (Long jobId) {
+ return jobMapper.selectJobById(jobId);
+ }
+
+ /**
+ * 暂停任务
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int pauseJob (SysJob job) throws SchedulerException {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0) {
+ scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 恢复任务
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int resumeJob (SysJob job) throws SchedulerException {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0) {
+ scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 删除任务后,所对应的trigger也将被删除
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int deleteJob (SysJob job) throws SchedulerException {
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ int rows = jobMapper.deleteJobById(jobId);
+ if (rows > 0) {
+ scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+ }
+ return rows;
+ }
+
+ /**
+ * 批量删除调度信息
+ *
+ * @param jobIds 需要删除的任务ID
+ *
+ * @return 结果
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteJobByIds (Long[] jobIds) throws SchedulerException {
+ for (Long jobId : jobIds) {
+ SysJob job = jobMapper.selectJobById(jobId);
+ deleteJob(job);
+ }
+ }
+
+ /**
+ * 任务调度状态修改
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int changeStatus (SysJob job) throws SchedulerException {
+ int rows = 0;
+ String status = job.getStatus();
+ if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {
+ rows = resumeJob(job);
+ } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {
+ rows = pauseJob(job);
+ }
+ return rows;
+ }
+
+ /**
+ * 立即运行任务
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public boolean run (SysJob job) throws SchedulerException {
+ boolean result = false;
+ Long jobId = job.getJobId();
+ String jobGroup = job.getJobGroup();
+ SysJob properties = selectJobById(job.getJobId());
+ // 参数
+ JobDataMap dataMap = new JobDataMap();
+ dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
+ JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
+ if (scheduler.checkExists(jobKey)) {
+ result = true;
+ scheduler.triggerJob(jobKey, dataMap);
+ }
+ return result;
+ }
+
+ /**
+ * 新增任务
+ *
+ * @param job 调度信息 调度信息
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int insertJob (SysJob job) throws SchedulerException, TaskException {
+ job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
+ int rows = jobMapper.insertJob(job);
+ if (rows > 0) {
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+ return rows;
+ }
+
+ /**
+ * 更新任务的时间表达式
+ *
+ * @param job 调度信息
+ */
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public int updateJob (SysJob job) throws SchedulerException, TaskException {
+ SysJob properties = selectJobById(job.getJobId());
+ int rows = jobMapper.updateJob(job);
+ if (rows > 0) {
+ updateSchedulerJob(job, properties.getJobGroup());
+ }
+ return rows;
+ }
+
+ /**
+ * 更新任务
+ *
+ * @param job 任务对象
+ * @param jobGroup 任务组名
+ */
+ public void updateSchedulerJob (SysJob job, String jobGroup) throws SchedulerException, TaskException {
+ Long jobId = job.getJobId();
+ // 判断是否存在
+ JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
+ if (scheduler.checkExists(jobKey)) {
+ // 防止创建时存在数据问题 先移除,然后在执行创建操作
+ scheduler.deleteJob(jobKey);
+ }
+ ScheduleUtils.createScheduleJob(scheduler, job);
+ }
+
+ /**
+ * 校验cron表达式是否有效
+ *
+ * @param cronExpression 表达式
+ *
+ * @return 结果
+ */
+ @Override
+ public boolean checkCronExpressionIsValid (String cronExpression) {
+ return CronUtils.isValid(cronExpression);
+ }
+}
diff --git a/src/main/java/com/fate/job/task/MyTask.java b/src/main/java/com/fate/job/task/MyTask.java
new file mode 100644
index 0000000..03fd215
--- /dev/null
+++ b/src/main/java/com/fate/job/task/MyTask.java
@@ -0,0 +1,24 @@
+package com.fate.job.task;
+
+import com.fate.common.core.utils.StringUtils;
+import org.springframework.stereotype.Component;
+
+/**
+ * 定时任务调度测试
+ *
+ * @author fate
+ */
+@Component("myTask")
+public class MyTask {
+ public void ryMultipleParams (String s, Boolean b, Long l, Double d, Integer i) {
+ System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
+ }
+
+ public void ryParams (String params) {
+ System.out.println("执行有参方法:" + params);
+ }
+
+ public void ryNoParams () {
+ System.out.println("执行无参方法");
+ }
+}
diff --git a/src/main/java/com/fate/job/util/AbstractQuartzJob.java b/src/main/java/com/fate/job/util/AbstractQuartzJob.java
new file mode 100644
index 0000000..9e569a1
--- /dev/null
+++ b/src/main/java/com/fate/job/util/AbstractQuartzJob.java
@@ -0,0 +1,97 @@
+package com.fate.job.util;
+
+import com.fate.common.core.constant.ScheduleConstants;
+import com.fate.common.core.utils.ExceptionUtil;
+import com.fate.common.core.utils.SpringUtils;
+import com.fate.common.core.utils.StringUtils;
+import com.fate.common.core.utils.bean.BeanUtils;
+import com.fate.job.domain.SysJob;
+import com.fate.job.domain.SysJobLog;
+import com.fate.job.service.ISysJobLogService;
+import org.quartz.Job;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+
+/**
+ * 抽象quartz调用
+ *
+ * @author fate
+ */
+public abstract class AbstractQuartzJob implements Job {
+ private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
+
+ /**
+ * 线程本地变量
+ */
+ private static ThreadLocal threadLocal = new ThreadLocal<>();
+
+ @Override
+ public void execute (JobExecutionContext context) throws JobExecutionException {
+ SysJob sysJob = new SysJob();
+ BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES));
+ try {
+ before(context, sysJob);
+ if (sysJob != null) {
+ doExecute(context, sysJob);
+ }
+ after(context, sysJob, null);
+ } catch (Exception e) {
+ log.error("任务执行异常 - :", e);
+ after(context, sysJob, e);
+ }
+ }
+
+ /**
+ * 执行前
+ *
+ * @param context 工作执行上下文对象
+ * @param sysJob 系统计划任务
+ */
+ protected void before (JobExecutionContext context, SysJob sysJob) {
+ threadLocal.set(new Date());
+ }
+
+ /**
+ * 执行后
+ *
+ * @param context 工作执行上下文对象
+ * @param sysJob 系统计划任务
+ */
+ protected void after (JobExecutionContext context, SysJob sysJob, Exception e) {
+ Date startTime = threadLocal.get();
+ threadLocal.remove();
+
+ final SysJobLog sysJobLog = new SysJobLog();
+ sysJobLog.setJobName(sysJob.getJobName());
+ sysJobLog.setJobGroup(sysJob.getJobGroup());
+ sysJobLog.setInvokeTarget(sysJob.getInvokeTarget());
+ sysJobLog.setStartTime(startTime);
+ sysJobLog.setStopTime(new Date());
+ long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime();
+ sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒");
+ if (e != null) {
+ sysJobLog.setStatus("1");
+ String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
+ sysJobLog.setExceptionInfo(errorMsg);
+ } else {
+ sysJobLog.setStatus("0");
+ }
+
+ // 写入数据库当中
+ SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog);
+ }
+
+ /**
+ * 执行方法,由子类重载
+ *
+ * @param context 工作执行上下文对象
+ * @param sysJob 系统计划任务
+ *
+ * @throws Exception 执行过程中的异常
+ */
+ protected abstract void doExecute (JobExecutionContext context, SysJob sysJob) throws Exception;
+}
diff --git a/src/main/java/com/fate/job/util/CronUtils.java b/src/main/java/com/fate/job/util/CronUtils.java
new file mode 100644
index 0000000..ec31bc4
--- /dev/null
+++ b/src/main/java/com/fate/job/util/CronUtils.java
@@ -0,0 +1,56 @@
+package com.fate.job.util;
+
+import org.quartz.CronExpression;
+
+import java.text.ParseException;
+import java.util.Date;
+
+/**
+ * cron表达式工具类
+ *
+ * @author fate
+ */
+public class CronUtils {
+ /**
+ * 返回一个布尔值代表一个给定的Cron表达式的有效性
+ *
+ * @param cronExpression Cron表达式
+ *
+ * @return boolean 表达式是否有效
+ */
+ public static boolean isValid (String cronExpression) {
+ return CronExpression.isValidExpression(cronExpression);
+ }
+
+ /**
+ * 返回一个字符串值,表示该消息无效Cron表达式给出有效性
+ *
+ * @param cronExpression Cron表达式
+ *
+ * @return String 无效时返回表达式错误描述,如果有效返回null
+ */
+ public static String getInvalidMessage (String cronExpression) {
+ try {
+ new CronExpression(cronExpression);
+ return null;
+ } catch (ParseException pe) {
+ return pe.getMessage();
+ }
+ }
+
+ /**
+ * 返回下一个执行时间根据给定的Cron表达式
+ *
+ * @param cronExpression Cron表达式
+ *
+ * @return Date 下次Cron表达式执行时间
+ */
+ public static Date getNextExecution (String cronExpression) {
+ try {
+ CronExpression cron = new CronExpression(cronExpression);
+ return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
+ } catch (ParseException e) {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/fate/job/util/JobInvokeUtil.java b/src/main/java/com/fate/job/util/JobInvokeUtil.java
new file mode 100644
index 0000000..832b18e
--- /dev/null
+++ b/src/main/java/com/fate/job/util/JobInvokeUtil.java
@@ -0,0 +1,165 @@
+package com.fate.job.util;
+
+import com.fate.common.core.utils.SpringUtils;
+import com.fate.common.core.utils.StringUtils;
+import com.fate.job.domain.SysJob;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 任务执行工具
+ *
+ * @author fate
+ */
+public class JobInvokeUtil {
+ /**
+ * 执行方法
+ *
+ * @param sysJob 系统任务
+ */
+ public static void invokeMethod (SysJob sysJob) throws Exception {
+ String invokeTarget = sysJob.getInvokeTarget();
+ String beanName = getBeanName(invokeTarget);
+ String methodName = getMethodName(invokeTarget);
+ List