From e7613a0fe931b6065cca50e4013e3c655970cdd0 Mon Sep 17 00:00:00 2001
From: xiangpei <xiangpei@timesnew.cn>
Date: 星期二, 02 四月 2024 10:33:06 +0800
Subject: [PATCH] 定时任务集成

---
 ycl-server/src/main/java/com/ycl/utils/quartz/CronUtils.java                         |   64 +
 ycl-server/src/main/java/com/ycl/system/controller/SysJobController.java             |  178 +++++
 ycl-server/src/main/java/com/ycl/system/service/ISysJobService.java                  |  103 ++
 ycl-server/src/main/java/com/ycl/utils/quartz/AbstractQuartzJob.java                 |  108 +++
 ycl-pojo/src/main/java/com/ycl/system/domain/SysJob.java                             |  171 ++++
 ycl-server/pom.xml                                                                   |    3 
 ycl-server/src/main/java/com/ycl/system/service/ISysJobLogService.java               |   57 +
 ycl-server/src/main/java/com/ycl/system/service/impl/SysJobLogServiceImpl.java       |   89 ++
 ycl-server/src/main/java/com/ycl/system/mapper/SysJobLogMapper.java                  |   65 +
 ycl-server/src/main/java/com/ycl/utils/quartz/JobInvokeUtil.java                     |  183 +++++
 ycl-pojo/src/main/java/com/ycl/system/domain/SysJobLog.java                          |  156 ++++
 ycl-server/src/main/java/com/ycl/utils/quartz/QuartzJobExecution.java                |   19 
 ycl-server/src/main/java/com/ycl/system/mapper/SysJobMapper.java                     |   68 +
 ycl-server/src/main/java/com/ycl/utils/quartz/ScheduleUtils.java                     |  131 +++
 ycl-server/src/main/resources/mapper/system/SysJobMapper.xml                         |  111 +++
 ycl-common/pom.xml                                                                   |   13 
 ycl-server/src/main/resources/mapper/system/SysJobLogMapper.xml                      |   94 ++
 ycl-server/src/main/java/com/ycl/task/RyTask.java                                    |   28 
 ycl-server/src/main/java/com/ycl/utils/quartz/QuartzDisallowConcurrentExecution.java |   21 
 ycl-common/src/main/java/utils/CronUtils.java                                        |   64 +
 ycl-server/src/main/java/com/ycl/system/service/impl/SysJobServiceImpl.java          |  261 +++++++
 ycl-server/src/main/java/com/ycl/system/controller/SysJobLogController.java          |   87 ++
 22 files changed, 2,073 insertions(+), 1 deletions(-)

diff --git a/ycl-common/pom.xml b/ycl-common/pom.xml
index d5b3290..4f8f328 100644
--- a/ycl-common/pom.xml
+++ b/ycl-common/pom.xml
@@ -16,6 +16,19 @@
         <maven.compiler.target>17</maven.compiler.target>
     </properties>
     <dependencies>
+
+        <!-- 瀹氭椂浠诲姟 -->
+        <dependency>
+            <groupId>org.quartz-scheduler</groupId>
+            <artifactId>quartz</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.mchange</groupId>
+                    <artifactId>c3p0</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
         <!-- 鑷畾涔夐獙璇佹敞瑙� -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
diff --git a/ycl-common/src/main/java/utils/CronUtils.java b/ycl-common/src/main/java/utils/CronUtils.java
new file mode 100644
index 0000000..0077c82
--- /dev/null
+++ b/ycl-common/src/main/java/utils/CronUtils.java
@@ -0,0 +1,64 @@
+package utils;
+
+import org.quartz.CronExpression;
+
+import java.text.ParseException;
+import java.util.Date;
+
+/**
+ * cron琛ㄨ揪寮忓伐鍏风被
+ *
+ * @author ruoyi
+ *
+ */
+public class CronUtils
+{
+    /**
+     * 杩斿洖涓�涓竷灏斿�间唬琛ㄤ竴涓粰瀹氱殑Cron琛ㄨ揪寮忕殑鏈夋晥鎬�
+     *
+     * @param cronExpression Cron琛ㄨ揪寮�
+     * @return boolean 琛ㄨ揪寮忔槸鍚︽湁鏁�
+     */
+    public static boolean isValid(String cronExpression)
+    {
+        return CronExpression.isValidExpression(cronExpression);
+    }
+
+    /**
+     * 杩斿洖涓�涓瓧绗︿覆鍊�,琛ㄧず璇ユ秷鎭棤鏁圕ron琛ㄨ揪寮忕粰鍑烘湁鏁堟��
+     *
+     * @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/ycl-pojo/src/main/java/com/ycl/system/domain/SysJob.java b/ycl-pojo/src/main/java/com/ycl/system/domain/SysJob.java
new file mode 100644
index 0000000..8d5d476
--- /dev/null
+++ b/ycl-pojo/src/main/java/com/ycl/system/domain/SysJob.java
@@ -0,0 +1,171 @@
+package com.ycl.system.domain;
+
+import annotation.Excel;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import constant.ScheduleConstants;
+import jakarta.validation.constraints.NotBlank;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import pojo.BaseEntity;
+import utils.CronUtils;
+import utils.StringUtils;
+
+import javax.validation.constraints.Size;
+import java.util.Date;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害琛� sys_job
+ *
+ * @author ruoyi
+ */
+public class SysJob extends BaseEntity
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 浠诲姟ID */
+    @Excel(name = "浠诲姟搴忓彿", cellType = Excel.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姝e父 1鏆傚仠锛� */
+    @Excel(name = "浠诲姟鐘舵��", readConverterExp = "0=姝e父,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/ycl-pojo/src/main/java/com/ycl/system/domain/SysJobLog.java b/ycl-pojo/src/main/java/com/ycl/system/domain/SysJobLog.java
new file mode 100644
index 0000000..ec97001
--- /dev/null
+++ b/ycl-pojo/src/main/java/com/ycl/system/domain/SysJobLog.java
@@ -0,0 +1,156 @@
+package com.ycl.system.domain;
+
+import annotation.Excel;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import pojo.BaseEntity;
+
+import java.util.Date;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害鏃ュ織琛� sys_job_log
+ *
+ * @author ruoyi
+ */
+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姝e父 1澶辫触锛� */
+    @Excel(name = "鎵ц鐘舵��", readConverterExp = "0=姝e父,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/ycl-server/pom.xml b/ycl-server/pom.xml
index b30767c..931bb3f 100644
--- a/ycl-server/pom.xml
+++ b/ycl-server/pom.xml
@@ -44,6 +44,7 @@
             <artifactId>spring-boot-starter-test</artifactId>
         </dependency>
 
+
         <!-- 楠岃瘉鐮� -->
         <dependency>
             <groupId>pro.fessional</groupId>
@@ -119,4 +120,4 @@
         </plugins>
         <finalName>${project.artifactId}</finalName>
     </build>
-</project>
\ No newline at end of file
+</project>
diff --git a/ycl-server/src/main/java/com/ycl/system/controller/SysJobController.java b/ycl-server/src/main/java/com/ycl/system/controller/SysJobController.java
new file mode 100644
index 0000000..e59a20d
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/system/controller/SysJobController.java
@@ -0,0 +1,178 @@
+package com.ycl.system.controller;
+
+import annotation.Log;
+import com.ycl.exception.job.TaskException;
+import com.ycl.system.AjaxResult;
+import com.ycl.system.domain.SysJob;
+import com.ycl.system.page.TableDataInfo;
+import com.ycl.system.service.ISysJobService;
+import com.ycl.utils.StringUtils;
+import com.ycl.utils.poi.ExcelUtil;
+import com.ycl.utils.quartz.CronUtils;
+import com.ycl.utils.quartz.ScheduleUtils;
+import constant.Constants;
+import enumeration.BusinessType;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 璋冨害浠诲姟淇℃伅鎿嶄綔澶勭悊
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/monitor/job")
+public class SysJobController extends BaseController
+{
+    @Autowired
+    private ISysJobService jobService;
+
+    /**
+     * 鏌ヨ瀹氭椂浠诲姟鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysJob sysJob)
+    {
+        startPage();
+        List<SysJob> list = jobService.selectJobList(sysJob);
+        return getDataTable(list);
+    }
+
+    /**
+     * 瀵煎嚭瀹氭椂浠诲姟鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+    @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, SysJob sysJob)
+    {
+        List<SysJob> list = jobService.selectJobList(sysJob);
+        ExcelUtil<SysJob> util = new ExcelUtil<SysJob>(SysJob.class);
+        util.exportExcel(response, list, "瀹氭椂浠诲姟");
+    }
+
+    /**
+     * 鑾峰彇瀹氭椂浠诲姟璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+    @GetMapping(value = "/{jobId}")
+    public AjaxResult getInfo(@PathVariable("jobId") Long jobId)
+    {
+        return success(jobService.selectJobById(jobId));
+    }
+
+    /**
+     * 鏂板瀹氭椂浠诲姟
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:add')")
+    @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException
+    {
+        if (!CronUtils.isValid(job.getCronExpression()))
+        {
+            return error("鏂板浠诲姟'" + job.getJobName() + "'澶辫触锛孋ron琛ㄨ揪寮忎笉姝g‘");
+        }
+        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(getUsername());
+        return toAjax(jobService.insertJob(job));
+    }
+
+    /**
+     * 淇敼瀹氭椂浠诲姟
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:edit')")
+    @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException
+    {
+        if (!CronUtils.isValid(job.getCronExpression()))
+        {
+            return error("淇敼浠诲姟'" + job.getJobName() + "'澶辫触锛孋ron琛ㄨ揪寮忎笉姝g‘");
+        }
+        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(getUsername());
+        return toAjax(jobService.updateJob(job));
+    }
+
+    /**
+     * 瀹氭椂浠诲姟鐘舵�佷慨鏀�
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+    @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.UPDATE)
+    @PutMapping("/changeStatus")
+    public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException
+    {
+        SysJob newJob = jobService.selectJobById(job.getJobId());
+        newJob.setStatus(job.getStatus());
+        return toAjax(jobService.changeStatus(newJob));
+    }
+
+    /**
+     * 瀹氭椂浠诲姟绔嬪嵆鎵ц涓�娆�
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')")
+    @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.UPDATE)
+    @PutMapping("/run")
+    public AjaxResult run(@RequestBody SysJob job) throws SchedulerException
+    {
+        boolean result = jobService.run(job);
+        return result ? success() : error("浠诲姟涓嶅瓨鍦ㄦ垨宸茶繃鏈燂紒");
+    }
+
+    /**
+     * 鍒犻櫎瀹氭椂浠诲姟
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+    @Log(title = "瀹氭椂浠诲姟", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{jobIds}")
+    public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException, TaskException
+    {
+        jobService.deleteJobByIds(jobIds);
+        return success();
+    }
+}
diff --git a/ycl-server/src/main/java/com/ycl/system/controller/SysJobLogController.java b/ycl-server/src/main/java/com/ycl/system/controller/SysJobLogController.java
new file mode 100644
index 0000000..75b5398
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/system/controller/SysJobLogController.java
@@ -0,0 +1,87 @@
+package com.ycl.system.controller;
+
+import annotation.Log;
+import com.ycl.system.AjaxResult;
+import com.ycl.system.domain.SysJobLog;
+import com.ycl.system.page.TableDataInfo;
+import com.ycl.system.service.ISysJobLogService;
+import enumeration.BusinessType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import utils.poi.ExcelUtil;
+
+import jakarta.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 璋冨害鏃ュ織鎿嶄綔澶勭悊
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/monitor/jobLog")
+public class SysJobLogController extends BaseController
+{
+    @Autowired
+    private ISysJobLogService jobLogService;
+
+    /**
+     * 鏌ヨ瀹氭椂浠诲姟璋冨害鏃ュ織鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(SysJobLog sysJobLog)
+    {
+        startPage();
+        List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+        return getDataTable(list);
+    }
+
+    /**
+     * 瀵煎嚭瀹氭椂浠诲姟璋冨害鏃ュ織鍒楄〃
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:export')")
+    @Log(title = "浠诲姟璋冨害鏃ュ織", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, SysJobLog sysJobLog)
+    {
+        List<SysJobLog> list = jobLogService.selectJobLogList(sysJobLog);
+        ExcelUtil<SysJobLog> util = new ExcelUtil<SysJobLog>(SysJobLog.class);
+        util.exportExcel(response, list, "璋冨害鏃ュ織");
+    }
+
+    /**
+     * 鏍规嵁璋冨害缂栧彿鑾峰彇璇︾粏淇℃伅
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:query')")
+    @GetMapping(value = "/{jobLogId}")
+    public AjaxResult getInfo(@PathVariable Long jobLogId)
+    {
+        return success(jobLogService.selectJobLogById(jobLogId));
+    }
+
+
+    /**
+     * 鍒犻櫎瀹氭椂浠诲姟璋冨害鏃ュ織
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+    @Log(title = "瀹氭椂浠诲姟璋冨害鏃ュ織", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{jobLogIds}")
+    public AjaxResult remove(@PathVariable Long[] jobLogIds)
+    {
+        return toAjax(jobLogService.deleteJobLogByIds(jobLogIds));
+    }
+
+    /**
+     * 娓呯┖瀹氭椂浠诲姟璋冨害鏃ュ織
+     */
+    @PreAuthorize("@ss.hasPermi('monitor:job:remove')")
+    @Log(title = "璋冨害鏃ュ織", businessType = BusinessType.CLEAN)
+    @DeleteMapping("/clean")
+    public AjaxResult clean()
+    {
+        jobLogService.cleanJobLog();
+        return success();
+    }
+}
diff --git a/ycl-server/src/main/java/com/ycl/system/mapper/SysJobLogMapper.java b/ycl-server/src/main/java/com/ycl/system/mapper/SysJobLogMapper.java
new file mode 100644
index 0000000..cce1c57
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/system/mapper/SysJobLogMapper.java
@@ -0,0 +1,65 @@
+package com.ycl.system.mapper;
+
+import com.ycl.system.domain.SysJobLog;
+
+import java.util.List;
+
+/**
+ * 璋冨害浠诲姟鏃ュ織淇℃伅 鏁版嵁灞�
+ *
+ * @author ruoyi
+ */
+public interface SysJobLogMapper
+{
+    /**
+     * 鑾峰彇quartz璋冨害鍣ㄦ棩蹇楃殑璁″垝浠诲姟
+     *
+     * @param jobLog 璋冨害鏃ュ織淇℃伅
+     * @return 璋冨害浠诲姟鏃ュ織闆嗗悎
+     */
+    public List<SysJobLog> selectJobLogList(SysJobLog jobLog);
+
+    /**
+     * 鏌ヨ鎵�鏈夎皟搴︿换鍔℃棩蹇�
+     *
+     * @return 璋冨害浠诲姟鏃ュ織鍒楄〃
+     */
+    public List<SysJobLog> 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/ycl-server/src/main/java/com/ycl/system/mapper/SysJobMapper.java b/ycl-server/src/main/java/com/ycl/system/mapper/SysJobMapper.java
new file mode 100644
index 0000000..96be5db
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/system/mapper/SysJobMapper.java
@@ -0,0 +1,68 @@
+package com.ycl.system.mapper;
+
+import com.ycl.system.domain.SysJob;
+
+import java.util.List;
+
+/**
+ * 璋冨害浠诲姟淇℃伅 鏁版嵁灞�
+ *
+ * @author ruoyi
+ */
+public interface SysJobMapper
+{
+    /**
+     * 鏌ヨ璋冨害浠诲姟鏃ュ織闆嗗悎
+     *
+     * @param job 璋冨害淇℃伅
+     * @return 鎿嶄綔鏃ュ織闆嗗悎
+     */
+    public List<SysJob> selectJobList(SysJob job);
+
+    /**
+     * 鏌ヨ鎵�鏈夎皟搴︿换鍔�
+     *
+     * @return 璋冨害浠诲姟鍒楄〃
+     */
+    public List<SysJob> 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/ycl-server/src/main/java/com/ycl/system/service/ISysJobLogService.java b/ycl-server/src/main/java/com/ycl/system/service/ISysJobLogService.java
new file mode 100644
index 0000000..9925c41
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/system/service/ISysJobLogService.java
@@ -0,0 +1,57 @@
+package com.ycl.system.service;
+
+import com.ycl.system.domain.SysJobLog;
+
+import java.util.List;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害鏃ュ織淇℃伅淇℃伅 鏈嶅姟灞�
+ *
+ * @author ruoyi
+ */
+public interface ISysJobLogService
+{
+    /**
+     * 鑾峰彇quartz璋冨害鍣ㄦ棩蹇楃殑璁″垝浠诲姟
+     *
+     * @param jobLog 璋冨害鏃ュ織淇℃伅
+     * @return 璋冨害浠诲姟鏃ュ織闆嗗悎
+     */
+    public List<SysJobLog> 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/ycl-server/src/main/java/com/ycl/system/service/ISysJobService.java b/ycl-server/src/main/java/com/ycl/system/service/ISysJobService.java
new file mode 100644
index 0000000..6dbf13a
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/system/service/ISysJobService.java
@@ -0,0 +1,103 @@
+package com.ycl.system.service;
+
+import com.ycl.exception.job.TaskException;
+import com.ycl.system.domain.SysJob;
+import org.quartz.SchedulerException;
+
+import java.util.List;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害淇℃伅淇℃伅 鏈嶅姟灞�
+ *
+ * @author ruoyi
+ */
+public interface ISysJobService
+{
+    /**
+     * 鑾峰彇quartz璋冨害鍣ㄧ殑璁″垝浠诲姟
+     *
+     * @param job 璋冨害淇℃伅
+     * @return 璋冨害浠诲姟闆嗗悎
+     */
+    public List<SysJob> 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;
+
+    /**
+     * 鍒犻櫎浠诲姟鍚庯紝鎵�瀵瑰簲鐨則rigger涔熷皢琚垹闄�
+     *
+     * @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/ycl-server/src/main/java/com/ycl/system/service/impl/SysJobLogServiceImpl.java b/ycl-server/src/main/java/com/ycl/system/service/impl/SysJobLogServiceImpl.java
new file mode 100644
index 0000000..d61c53a
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/system/service/impl/SysJobLogServiceImpl.java
@@ -0,0 +1,89 @@
+package com.ycl.system.service.impl;
+
+import com.ycl.system.domain.SysJobLog;
+import com.ycl.system.mapper.SysJobLogMapper;
+import com.ycl.system.service.ISysJobLogService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害鏃ュ織淇℃伅 鏈嶅姟灞�
+ *
+ * @author ruoyi
+ */
+@Service
+@RequiredArgsConstructor
+public class SysJobLogServiceImpl implements ISysJobLogService
+{
+    private final SysJobLogMapper jobLogMapper;
+
+    /**
+     * 鑾峰彇quartz璋冨害鍣ㄦ棩蹇楃殑璁″垝浠诲姟
+     *
+     * @param jobLog 璋冨害鏃ュ織淇℃伅
+     * @return 璋冨害浠诲姟鏃ュ織闆嗗悎
+     */
+    @Override
+    public List<SysJobLog> 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/ycl-server/src/main/java/com/ycl/system/service/impl/SysJobServiceImpl.java b/ycl-server/src/main/java/com/ycl/system/service/impl/SysJobServiceImpl.java
new file mode 100644
index 0000000..07d28a5
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/system/service/impl/SysJobServiceImpl.java
@@ -0,0 +1,261 @@
+package com.ycl.system.service.impl;
+
+import com.ycl.exception.job.TaskException;
+import com.ycl.system.domain.SysJob;
+import com.ycl.system.mapper.SysJobMapper;
+import com.ycl.system.service.ISysJobService;
+import com.ycl.utils.quartz.CronUtils;
+import com.ycl.utils.quartz.ScheduleUtils;
+import constant.ScheduleConstants;
+import jakarta.annotation.PostConstruct;
+import lombok.RequiredArgsConstructor;
+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 java.util.List;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害淇℃伅 鏈嶅姟灞�
+ *
+ * @author ruoyi
+ */
+@Service
+@RequiredArgsConstructor
+public class SysJobServiceImpl implements ISysJobService
+{
+    private final Scheduler scheduler;
+    private final SysJobMapper jobMapper;
+
+    /**
+     * 椤圭洰鍚姩鏃讹紝鍒濆鍖栧畾鏃跺櫒 涓昏鏄槻姝㈡墜鍔ㄤ慨鏀规暟鎹簱瀵艰嚧鏈悓姝ュ埌瀹氭椂浠诲姟澶勭悊锛堟敞锛氫笉鑳芥墜鍔ㄤ慨鏀规暟鎹簱ID鍜屼换鍔$粍鍚嶏紝鍚﹀垯浼氬鑷磋剰鏁版嵁锛�
+     */
+    @PostConstruct
+    public void init() throws SchedulerException, TaskException
+    {
+        scheduler.clear();
+        List<SysJob> jobList = jobMapper.selectJobAll();
+        for (SysJob job : jobList)
+        {
+            ScheduleUtils.createScheduleJob(scheduler, job);
+        }
+    }
+
+    /**
+     * 鑾峰彇quartz璋冨害鍣ㄧ殑璁″垝浠诲姟鍒楄〃
+     *
+     * @param job 璋冨害淇℃伅
+     * @return
+     */
+    @Override
+    public List<SysJob> 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;
+    }
+
+    /**
+     * 鍒犻櫎浠诲姟鍚庯紝鎵�瀵瑰簲鐨則rigger涔熷皢琚垹闄�
+     *
+     * @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/ycl-server/src/main/java/com/ycl/task/RyTask.java b/ycl-server/src/main/java/com/ycl/task/RyTask.java
new file mode 100644
index 0000000..7a4f295
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/task/RyTask.java
@@ -0,0 +1,28 @@
+package com.ycl.task;
+
+import com.ycl.utils.StringUtils;
+import org.springframework.stereotype.Component;
+
+/**
+ * 瀹氭椂浠诲姟璋冨害娴嬭瘯
+ *
+ * @author ruoyi
+ */
+@Component("ryTask")
+public class RyTask
+{
+    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/ycl-server/src/main/java/com/ycl/utils/quartz/AbstractQuartzJob.java b/ycl-server/src/main/java/com/ycl/utils/quartz/AbstractQuartzJob.java
new file mode 100644
index 0000000..b46e906
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/utils/quartz/AbstractQuartzJob.java
@@ -0,0 +1,108 @@
+package com.ycl.utils.quartz;
+
+import com.ycl.system.domain.SysJob;
+import com.ycl.system.domain.SysJobLog;
+import com.ycl.system.service.ISysJobLogService;
+import com.ycl.utils.ExceptionUtil;
+import com.ycl.utils.StringUtils;
+import com.ycl.utils.bean.BeanUtils;
+import com.ycl.utils.spring.SpringUtils;
+import constant.Constants;
+import constant.ScheduleConstants;
+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 ruoyi
+ */
+public abstract class AbstractQuartzJob implements Job
+{
+    private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class);
+
+    /**
+     * 绾跨▼鏈湴鍙橀噺
+     */
+    private static ThreadLocal<Date> 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(Constants.FAIL);
+            String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000);
+            sysJobLog.setExceptionInfo(errorMsg);
+        }
+        else
+        {
+            sysJobLog.setStatus(Constants.SUCCESS);
+        }
+
+        // 鍐欏叆鏁版嵁搴撳綋涓�
+        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/ycl-server/src/main/java/com/ycl/utils/quartz/CronUtils.java b/ycl-server/src/main/java/com/ycl/utils/quartz/CronUtils.java
new file mode 100644
index 0000000..468d682
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/utils/quartz/CronUtils.java
@@ -0,0 +1,64 @@
+package com.ycl.utils.quartz;
+
+import org.quartz.CronExpression;
+
+import java.text.ParseException;
+import java.util.Date;
+
+/**
+ * cron琛ㄨ揪寮忓伐鍏风被
+ *
+ * @author ruoyi
+ *
+ */
+public class CronUtils
+{
+    /**
+     * 杩斿洖涓�涓竷灏斿�间唬琛ㄤ竴涓粰瀹氱殑Cron琛ㄨ揪寮忕殑鏈夋晥鎬�
+     *
+     * @param cronExpression Cron琛ㄨ揪寮�
+     * @return boolean 琛ㄨ揪寮忔槸鍚︽湁鏁�
+     */
+    public static boolean isValid(String cronExpression)
+    {
+        return CronExpression.isValidExpression(cronExpression);
+    }
+
+    /**
+     * 杩斿洖涓�涓瓧绗︿覆鍊�,琛ㄧず璇ユ秷鎭棤鏁圕ron琛ㄨ揪寮忕粰鍑烘湁鏁堟��
+     *
+     * @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/ycl-server/src/main/java/com/ycl/utils/quartz/JobInvokeUtil.java b/ycl-server/src/main/java/com/ycl/utils/quartz/JobInvokeUtil.java
new file mode 100644
index 0000000..64e47d1
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/utils/quartz/JobInvokeUtil.java
@@ -0,0 +1,183 @@
+package com.ycl.utils.quartz;
+
+import com.ycl.system.domain.SysJob;
+import com.ycl.utils.StringUtils;
+import com.ycl.utils.spring.SpringUtils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 浠诲姟鎵ц宸ュ叿
+ *
+ * @author ruoyi
+ */
+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<Object[]> methodParams = getMethodParams(invokeTarget);
+
+        if (!isValidClassName(beanName))
+        {
+            Object bean = SpringUtils.getBean(beanName);
+            invokeMethod(bean, methodName, methodParams);
+        }
+        else
+        {
+            Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance();
+            invokeMethod(bean, methodName, methodParams);
+        }
+    }
+
+    /**
+     * 璋冪敤浠诲姟鏂规硶
+     *
+     * @param bean 鐩爣瀵硅薄
+     * @param methodName 鏂规硶鍚嶇О
+     * @param methodParams 鏂规硶鍙傛暟
+     */
+    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
+            throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException,
+            InvocationTargetException
+    {
+        if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0)
+        {
+            Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams));
+            method.invoke(bean, getMethodParamsValue(methodParams));
+        }
+        else
+        {
+            Method method = bean.getClass().getMethod(methodName);
+            method.invoke(bean);
+        }
+    }
+
+    /**
+     * 鏍¢獙鏄惁涓轰负class鍖呭悕
+     *
+     * @param invokeTarget 鍚嶇О
+     * @return true鏄� false鍚�
+     */
+    public static boolean isValidClassName(String invokeTarget)
+    {
+        return StringUtils.countMatches(invokeTarget, ".") > 1;
+    }
+
+    /**
+     * 鑾峰彇bean鍚嶇О
+     *
+     * @param invokeTarget 鐩爣瀛楃涓�
+     * @return bean鍚嶇О
+     */
+    public static String getBeanName(String invokeTarget)
+    {
+        String beanName = StringUtils.substringBefore(invokeTarget, "(");
+        return StringUtils.substringBeforeLast(beanName, ".");
+    }
+
+    /**
+     * 鑾峰彇bean鏂规硶
+     *
+     * @param invokeTarget 鐩爣瀛楃涓�
+     * @return method鏂规硶
+     */
+    public static String getMethodName(String invokeTarget)
+    {
+        String methodName = StringUtils.substringBefore(invokeTarget, "(");
+        return StringUtils.substringAfterLast(methodName, ".");
+    }
+
+    /**
+     * 鑾峰彇method鏂规硶鍙傛暟鐩稿叧鍒楄〃
+     *
+     * @param invokeTarget 鐩爣瀛楃涓�
+     * @return method鏂规硶鐩稿叧鍙傛暟鍒楄〃
+     */
+    public static List<Object[]> getMethodParams(String invokeTarget)
+    {
+        String methodStr = StringUtils.substringBetween(invokeTarget, "(", ")");
+        if (StringUtils.isEmpty(methodStr))
+        {
+            return null;
+        }
+        String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)");
+        List<Object[]> classs = new LinkedList<>();
+        for (int i = 0; i < methodParams.length; i++)
+        {
+            String str = StringUtils.trimToEmpty(methodParams[i]);
+            // String瀛楃涓茬被鍨嬶紝浠�'鎴�"寮�澶�
+            if (StringUtils.startsWithAny(str, "'", "\""))
+            {
+                classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class });
+            }
+            // boolean甯冨皵绫诲瀷锛岀瓑浜巘rue鎴栬�協alse
+            else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str))
+            {
+                classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
+            }
+            // long闀挎暣褰紝浠缁撳熬
+            else if (StringUtils.endsWith(str, "L"))
+            {
+                classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class });
+            }
+            // double娴偣绫诲瀷锛屼互D缁撳熬
+            else if (StringUtils.endsWith(str, "D"))
+            {
+                classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class });
+            }
+            // 鍏朵粬绫诲瀷褰掔被涓烘暣褰�
+            else
+            {
+                classs.add(new Object[] { Integer.valueOf(str), Integer.class });
+            }
+        }
+        return classs;
+    }
+
+    /**
+     * 鑾峰彇鍙傛暟绫诲瀷
+     *
+     * @param methodParams 鍙傛暟鐩稿叧鍒楄〃
+     * @return 鍙傛暟绫诲瀷鍒楄〃
+     */
+    public static Class<?>[] getMethodParamsType(List<Object[]> methodParams)
+    {
+        Class<?>[] classs = new Class<?>[methodParams.size()];
+        int index = 0;
+        for (Object[] os : methodParams)
+        {
+            classs[index] = (Class<?>) os[1];
+            index++;
+        }
+        return classs;
+    }
+
+    /**
+     * 鑾峰彇鍙傛暟鍊�
+     *
+     * @param methodParams 鍙傛暟鐩稿叧鍒楄〃
+     * @return 鍙傛暟鍊煎垪琛�
+     */
+    public static Object[] getMethodParamsValue(List<Object[]> methodParams)
+    {
+        Object[] classs = new Object[methodParams.size()];
+        int index = 0;
+        for (Object[] os : methodParams)
+        {
+            classs[index] = (Object) os[0];
+            index++;
+        }
+        return classs;
+    }
+}
diff --git a/ycl-server/src/main/java/com/ycl/utils/quartz/QuartzDisallowConcurrentExecution.java b/ycl-server/src/main/java/com/ycl/utils/quartz/QuartzDisallowConcurrentExecution.java
new file mode 100644
index 0000000..7287cce
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/utils/quartz/QuartzDisallowConcurrentExecution.java
@@ -0,0 +1,21 @@
+package com.ycl.utils.quartz;
+
+import com.ycl.system.domain.SysJob;
+import org.quartz.DisallowConcurrentExecution;
+import org.quartz.JobExecutionContext;
+
+/**
+ * 瀹氭椂浠诲姟澶勭悊锛堢姝㈠苟鍙戞墽琛岋級
+ *
+ * @author ruoyi
+ *
+ */
+@DisallowConcurrentExecution
+public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob
+{
+    @Override
+    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
+    {
+        JobInvokeUtil.invokeMethod(sysJob);
+    }
+}
diff --git a/ycl-server/src/main/java/com/ycl/utils/quartz/QuartzJobExecution.java b/ycl-server/src/main/java/com/ycl/utils/quartz/QuartzJobExecution.java
new file mode 100644
index 0000000..d91a1bc
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/utils/quartz/QuartzJobExecution.java
@@ -0,0 +1,19 @@
+package com.ycl.utils.quartz;
+
+import com.ycl.system.domain.SysJob;
+import org.quartz.JobExecutionContext;
+
+/**
+ * 瀹氭椂浠诲姟澶勭悊锛堝厑璁稿苟鍙戞墽琛岋級
+ *
+ * @author ruoyi
+ *
+ */
+public class QuartzJobExecution extends AbstractQuartzJob
+{
+    @Override
+    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception
+    {
+        JobInvokeUtil.invokeMethod(sysJob);
+    }
+}
diff --git a/ycl-server/src/main/java/com/ycl/utils/quartz/ScheduleUtils.java b/ycl-server/src/main/java/com/ycl/utils/quartz/ScheduleUtils.java
new file mode 100644
index 0000000..2ac0da7
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/utils/quartz/ScheduleUtils.java
@@ -0,0 +1,131 @@
+package com.ycl.utils.quartz;
+
+import com.ycl.exception.job.TaskException;
+import com.ycl.system.domain.SysJob;
+import com.ycl.utils.StringUtils;
+import com.ycl.utils.spring.SpringUtils;
+import constant.Constants;
+import constant.ScheduleConstants;
+import org.quartz.*;
+
+/**
+ * 瀹氭椂浠诲姟宸ュ叿绫�
+ *
+ * @author ruoyi
+ *
+ */
+public class ScheduleUtils
+{
+    /**
+     * 寰楀埌quartz浠诲姟绫�
+     *
+     * @param sysJob 鎵ц璁″垝
+     * @return 鍏蜂綋鎵ц浠诲姟绫�
+     */
+    private static Class<? extends Job> getQuartzJobClass(SysJob sysJob)
+    {
+        boolean isConcurrent = "0".equals(sysJob.getConcurrent());
+        return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class;
+    }
+
+    /**
+     * 鏋勫缓浠诲姟瑙﹀彂瀵硅薄
+     */
+    public static TriggerKey getTriggerKey(Long jobId, String jobGroup)
+    {
+        return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
+    }
+
+    /**
+     * 鏋勫缓浠诲姟閿璞�
+     */
+    public static JobKey getJobKey(Long jobId, String jobGroup)
+    {
+        return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup);
+    }
+
+    /**
+     * 鍒涘缓瀹氭椂浠诲姟
+     */
+    public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException
+    {
+        Class<? extends Job> jobClass = getQuartzJobClass(job);
+        // 鏋勫缓job淇℃伅
+        Long jobId = job.getJobId();
+        String jobGroup = job.getJobGroup();
+        JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build();
+
+        // 琛ㄨ揪寮忚皟搴︽瀯寤哄櫒
+        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
+        cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder);
+
+        // 鎸夋柊鐨刢ronExpression琛ㄨ揪寮忔瀯寤轰竴涓柊鐨則rigger
+        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup))
+                .withSchedule(cronScheduleBuilder).build();
+
+        // 鏀惧叆鍙傛暟锛岃繍琛屾椂鐨勬柟娉曞彲浠ヨ幏鍙�
+        jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job);
+
+        // 鍒ゆ柇鏄惁瀛樺湪
+        if (scheduler.checkExists(getJobKey(jobId, jobGroup)))
+        {
+            // 闃叉鍒涘缓鏃跺瓨鍦ㄦ暟鎹棶棰� 鍏堢Щ闄わ紝鐒跺悗鍦ㄦ墽琛屽垱寤烘搷浣�
+            scheduler.deleteJob(getJobKey(jobId, jobGroup));
+        }
+
+        // 鍒ゆ柇浠诲姟鏄惁杩囨湡
+        if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression())))
+        {
+            // 鎵ц璋冨害浠诲姟
+            scheduler.scheduleJob(jobDetail, trigger);
+        }
+
+        // 鏆傚仠浠诲姟
+        if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue()))
+        {
+            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
+        }
+    }
+
+    /**
+     * 璁剧疆瀹氭椂浠诲姟绛栫暐
+     */
+    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb)
+            throws TaskException
+    {
+        switch (job.getMisfirePolicy())
+        {
+            case ScheduleConstants.MISFIRE_DEFAULT:
+                return cb;
+            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
+                return cb.withMisfireHandlingInstructionIgnoreMisfires();
+            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
+                return cb.withMisfireHandlingInstructionFireAndProceed();
+            case ScheduleConstants.MISFIRE_DO_NOTHING:
+                return cb.withMisfireHandlingInstructionDoNothing();
+            default:
+                throw new TaskException("The task misfire policy '" + job.getMisfirePolicy()
+                        + "' cannot be used in cron schedule tasks", TaskException.Code.CONFIG_ERROR);
+        }
+    }
+
+    /**
+     * 妫�鏌ュ寘鍚嶆槸鍚︿负鐧藉悕鍗曢厤缃�
+     *
+     * @param invokeTarget 鐩爣瀛楃涓�
+     * @return 缁撴灉
+     */
+    public static boolean whiteList(String invokeTarget)
+    {
+        String packageName = StringUtils.substringBefore(invokeTarget, "(");
+        int count = StringUtils.countMatches(packageName, ".");
+        if (count > 1)
+        {
+            return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR);
+        }
+        Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]);
+        String beanPackageName = obj.getClass().getPackage().getName();
+        return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR)
+                && !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR);
+    }
+}
diff --git a/ycl-server/src/main/resources/mapper/system/SysJobLogMapper.xml b/ycl-server/src/main/resources/mapper/system/SysJobLogMapper.xml
new file mode 100644
index 0000000..2f753b2
--- /dev/null
+++ b/ycl-server/src/main/resources/mapper/system/SysJobLogMapper.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ycl.system.mapper.SysJobLogMapper">
+
+	<resultMap type="SysJobLog" id="SysJobLogResult">
+		<id     property="jobLogId"       column="job_log_id"      />
+		<result property="jobName"        column="job_name"        />
+		<result property="jobGroup"       column="job_group"       />
+		<result property="invokeTarget"   column="invoke_target"   />
+		<result property="jobMessage"     column="job_message"     />
+		<result property="status"         column="status"          />
+		<result property="exceptionInfo"  column="exception_info"  />
+		<result property="createTime"     column="create_time"     />
+	</resultMap>
+
+	<sql id="selectJobLogVo">
+        select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time
+		from sys_job_log
+    </sql>
+
+	<select id="selectJobLogList" parameterType="SysJobLog" resultMap="SysJobLogResult">
+		<include refid="selectJobLogVo"/>
+		<where>
+			<if test="jobName != null and jobName != ''">
+				AND job_name like concat('%', #{jobName}, '%')
+			</if>
+			<if test="jobGroup != null and jobGroup != ''">
+				AND job_group = #{jobGroup}
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="invokeTarget != null and invokeTarget != ''">
+				AND invoke_target like concat('%', #{invokeTarget}, '%')
+			</if>
+			<if test="params.beginTime != null and params.beginTime != ''"><!-- 寮�濮嬫椂闂存绱� -->
+				and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
+			</if>
+			<if test="params.endTime != null and params.endTime != ''"><!-- 缁撴潫鏃堕棿妫�绱� -->
+				and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
+			</if>
+		</where>
+		order by create_time desc
+	</select>
+
+	<select id="selectJobLogAll" resultMap="SysJobLogResult">
+		<include refid="selectJobLogVo"/>
+	</select>
+
+	<select id="selectJobLogById" parameterType="Long" resultMap="SysJobLogResult">
+		<include refid="selectJobLogVo"/>
+		where job_log_id = #{jobLogId}
+	</select>
+
+	<delete id="deleteJobLogById" parameterType="Long">
+ 		delete from sys_job_log where job_log_id = #{jobLogId}
+ 	</delete>
+
+ 	<delete id="deleteJobLogByIds" parameterType="Long">
+ 		delete from sys_job_log where job_log_id in
+ 		<foreach collection="array" item="jobLogId" open="(" separator="," close=")">
+ 			#{jobLogId}
+        </foreach>
+ 	</delete>
+
+ 	<update id="cleanJobLog">
+        truncate table sys_job_log
+    </update>
+
+ 	<insert id="insertJobLog" parameterType="SysJobLog">
+ 		insert into sys_job_log(
+ 			<if test="jobLogId != null and jobLogId != 0">job_log_id,</if>
+ 			<if test="jobName != null and jobName != ''">job_name,</if>
+ 			<if test="jobGroup != null and jobGroup != ''">job_group,</if>
+ 			<if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
+ 			<if test="jobMessage != null and jobMessage != ''">job_message,</if>
+ 			<if test="status != null and status != ''">status,</if>
+ 			<if test="exceptionInfo != null and exceptionInfo != ''">exception_info,</if>
+ 			create_time
+ 		)values(
+ 			<if test="jobLogId != null and jobLogId != 0">#{jobLogId},</if>
+ 			<if test="jobName != null and jobName != ''">#{jobName},</if>
+ 			<if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
+ 			<if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
+ 			<if test="jobMessage != null and jobMessage != ''">#{jobMessage},</if>
+ 			<if test="status != null and status != ''">#{status},</if>
+ 			<if test="exceptionInfo != null and exceptionInfo != ''">#{exceptionInfo},</if>
+ 			sysdate()
+ 		)
+	</insert>
+
+</mapper>
diff --git a/ycl-server/src/main/resources/mapper/system/SysJobMapper.xml b/ycl-server/src/main/resources/mapper/system/SysJobMapper.xml
new file mode 100644
index 0000000..685ec2f
--- /dev/null
+++ b/ycl-server/src/main/resources/mapper/system/SysJobMapper.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ycl.system.mapper.SysJobMapper">
+
+	<resultMap type="SysJob" id="SysJobResult">
+		<id     property="jobId"          column="job_id"          />
+		<result property="jobName"        column="job_name"        />
+		<result property="jobGroup"       column="job_group"       />
+		<result property="invokeTarget"   column="invoke_target"   />
+		<result property="cronExpression" column="cron_expression" />
+		<result property="misfirePolicy"  column="misfire_policy"  />
+		<result property="concurrent"     column="concurrent"      />
+		<result property="status"         column="status"          />
+		<result property="createBy"       column="create_by"       />
+		<result property="createTime"     column="create_time"     />
+		<result property="updateBy"       column="update_by"       />
+		<result property="updateTime"     column="update_time"     />
+		<result property="remark"         column="remark"          />
+	</resultMap>
+
+	<sql id="selectJobVo">
+        select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark
+		from sys_job
+    </sql>
+
+	<select id="selectJobList" parameterType="SysJob" resultMap="SysJobResult">
+		<include refid="selectJobVo"/>
+		<where>
+			<if test="jobName != null and jobName != ''">
+				AND job_name like concat('%', #{jobName}, '%')
+			</if>
+			<if test="jobGroup != null and jobGroup != ''">
+				AND job_group = #{jobGroup}
+			</if>
+			<if test="status != null and status != ''">
+				AND status = #{status}
+			</if>
+			<if test="invokeTarget != null and invokeTarget != ''">
+				AND invoke_target like concat('%', #{invokeTarget}, '%')
+			</if>
+		</where>
+	</select>
+
+	<select id="selectJobAll" resultMap="SysJobResult">
+		<include refid="selectJobVo"/>
+	</select>
+
+	<select id="selectJobById" parameterType="Long" resultMap="SysJobResult">
+		<include refid="selectJobVo"/>
+		where job_id = #{jobId}
+	</select>
+
+	<delete id="deleteJobById" parameterType="Long">
+ 		delete from sys_job where job_id = #{jobId}
+ 	</delete>
+
+ 	<delete id="deleteJobByIds" parameterType="Long">
+ 		delete from sys_job where job_id in
+ 		<foreach collection="array" item="jobId" open="(" separator="," close=")">
+ 			#{jobId}
+        </foreach>
+ 	</delete>
+
+ 	<update id="updateJob" parameterType="SysJob">
+ 		update sys_job
+ 		<set>
+ 			<if test="jobName != null and jobName != ''">job_name = #{jobName},</if>
+ 			<if test="jobGroup != null and jobGroup != ''">job_group = #{jobGroup},</if>
+ 			<if test="invokeTarget != null and invokeTarget != ''">invoke_target = #{invokeTarget},</if>
+ 			<if test="cronExpression != null and cronExpression != ''">cron_expression = #{cronExpression},</if>
+ 			<if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy = #{misfirePolicy},</if>
+ 			<if test="concurrent != null and concurrent != ''">concurrent = #{concurrent},</if>
+ 			<if test="status !=null">status = #{status},</if>
+ 			<if test="remark != null and remark != ''">remark = #{remark},</if>
+ 			<if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+ 			update_time = sysdate()
+ 		</set>
+ 		where job_id = #{jobId}
+	</update>
+
+ 	<insert id="insertJob" parameterType="SysJob" useGeneratedKeys="true" keyProperty="jobId">
+ 		insert into sys_job(
+ 			<if test="jobId != null and jobId != 0">job_id,</if>
+ 			<if test="jobName != null and jobName != ''">job_name,</if>
+ 			<if test="jobGroup != null and jobGroup != ''">job_group,</if>
+ 			<if test="invokeTarget != null and invokeTarget != ''">invoke_target,</if>
+ 			<if test="cronExpression != null and cronExpression != ''">cron_expression,</if>
+ 			<if test="misfirePolicy != null and misfirePolicy != ''">misfire_policy,</if>
+ 			<if test="concurrent != null and concurrent != ''">concurrent,</if>
+ 			<if test="status != null and status != ''">status,</if>
+ 			<if test="remark != null and remark != ''">remark,</if>
+ 			<if test="createBy != null and createBy != ''">create_by,</if>
+ 			create_time
+ 		)values(
+ 			<if test="jobId != null and jobId != 0">#{jobId},</if>
+ 			<if test="jobName != null and jobName != ''">#{jobName},</if>
+ 			<if test="jobGroup != null and jobGroup != ''">#{jobGroup},</if>
+ 			<if test="invokeTarget != null and invokeTarget != ''">#{invokeTarget},</if>
+ 			<if test="cronExpression != null and cronExpression != ''">#{cronExpression},</if>
+ 			<if test="misfirePolicy != null and misfirePolicy != ''">#{misfirePolicy},</if>
+ 			<if test="concurrent != null and concurrent != ''">#{concurrent},</if>
+ 			<if test="status != null and status != ''">#{status},</if>
+ 			<if test="remark != null and remark != ''">#{remark},</if>
+ 			<if test="createBy != null and createBy != ''">#{createBy},</if>
+ 			sysdate()
+ 		)
+	</insert>
+
+</mapper>

--
Gitblit v1.8.0