Explorar el Código

Job 代码的初始化

YunaiV hace 4 años
padre
commit
efbd8cad49
Se han modificado 28 ficheros con 578 adiciones y 462 borrados
  1. 2 10
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java
  2. 0 56
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java
  3. 0 102
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java
  4. 0 12
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java
  5. 0 24
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
  6. 0 159
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java
  7. 0 18
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java
  8. 0 16
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java
  9. 0 59
      ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java
  10. 99 0
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/InfJobController.java
  11. 57 0
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobBaseVO.java
  12. 14 0
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobCreateReqVO.java
  13. 57 0
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobExcelVO.java
  14. 20 0
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobExportReqVO.java
  15. 25 0
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobPageReqVO.java
  16. 23 0
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobRespVO.java
  17. 21 0
      src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobUpdateReqVO.java
  18. 36 0
      src/main/java/cn/iocoder/dashboard/modules/infra/convert/job/InfJobConvert.java
  19. 6 4
      src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/job/InfJobDO.java
  20. 37 0
      src/main/java/cn/iocoder/dashboard/modules/infra/dal/mysql/job/InfJobMapper.java
  21. 0 1
      src/main/java/cn/iocoder/dashboard/modules/infra/dal/mysql/package-info.java
  22. 3 0
      src/main/java/cn/iocoder/dashboard/modules/infra/enums/InfErrorCodeConstants.java
  23. 75 0
      src/main/java/cn/iocoder/dashboard/modules/infra/service/job/InfJobService.java
  24. 86 0
      src/main/java/cn/iocoder/dashboard/modules/infra/service/job/impl/InfJobServiceImpl.java
  25. 0 1
      src/main/java/cn/iocoder/dashboard/modules/infra/service/package-info.java
  26. 1 0
      src/main/java/cn/iocoder/dashboard/modules/system/enums/dict/SysDictTypeEnum.java
  27. 10 0
      src/main/resources/codegen/java/controller/vo/baseVO.vm
  28. 6 0
      src/test/java/cn/iocoder/dashboard/framework/quartz/core/scheduler/SchedulerManagerTest.java

+ 2 - 10
ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java

@@ -14,6 +14,7 @@ import java.util.Properties;
  */
 @Configuration
 public class ScheduleConfig {
+
     @Bean
     public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
         SchedulerFactoryBean factory = new SchedulerFactoryBean();
@@ -23,14 +24,7 @@ public class ScheduleConfig {
         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.quartz.impl.jdbcjobstore.JobStoreTX");
         // 集群配置
-        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");
@@ -38,7 +32,6 @@ public class ScheduleConfig {
         // 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");
@@ -48,8 +41,7 @@ public class ScheduleConfig {
         // 可选,QuartzScheduler
         // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
         factory.setOverwriteExistingJobs(true);
-        // 设置自动启动,默认为true
-        factory.setAutoStartup(true);
+
 
         return factory;
     }

+ 0 - 56
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java

@@ -1,56 +0,0 @@
-package com.ruoyi.quartz.service;
-
-import java.util.List;
-
-import com.ruoyi.quartz.domain.SysJobLog;
-
-/**
- * 定时任务调度日志信息信息 服务层
- *
- * @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();
-}

+ 0 - 102
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java

@@ -1,102 +0,0 @@
-package com.ruoyi.quartz.service;
-
-import java.util.List;
-
-import org.quartz.SchedulerException;
-import com.ruoyi.common.exception.job.TaskException;
-import com.ruoyi.quartz.domain.SysJob;
-
-/**
- * 定时任务调度信息信息 服务层
- *
- * @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;
-
-    /**
-     * 删除任务后,所对应的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 void 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);
-}

+ 0 - 12
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java

@@ -31,18 +31,6 @@ public class SysJobServiceImpl implements ISysJobService {
     @Autowired
     private 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调度器的计划任务列表
      *

+ 0 - 24
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java

@@ -1,24 +0,0 @@
-package com.ruoyi.quartz.task;
-
-import org.springframework.stereotype.Component;
-import com.ruoyi.common.utils.StringUtils;
-
-/**
- * 定时任务调度测试
- *
- * @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("执行无参方法");
-    }
-}

+ 0 - 159
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java

@@ -1,159 +0,0 @@
-package com.ruoyi.quartz.util;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.LinkedList;
-import java.util.List;
-
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.common.utils.spring.SpringUtils;
-import com.ruoyi.quartz.domain.SysJob;
-
-/**
- * 任务执行工具
- *
- * @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).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().getDeclaredMethod(methodName, getMethodParamsType(methodParams));
-            method.invoke(bean, getMethodParamsValue(methodParams));
-        } else {
-            Method method = bean.getClass().getDeclaredMethod(methodName);
-            method.invoke(bean);
-        }
-    }
-
-    /**
-     * 校验是否为为class包名
-     *
-     * @param str 名称
-     * @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.contains(str, "'")) {
-                classs.add(new Object[]{StringUtils.replace(str, "'", ""), String.class});
-            }
-            // boolean布尔类型,等于true或者false
-            else if (StringUtils.equals(str, "true") || StringUtils.equalsIgnoreCase(str, "false")) {
-                classs.add(new Object[]{Boolean.valueOf(str), Boolean.class});
-            }
-            // long长整形,包含L
-            else if (StringUtils.containsIgnoreCase(str, "L")) {
-                classs.add(new Object[]{Long.valueOf(StringUtils.replaceIgnoreCase(str, "L", "")), Long.class});
-            }
-            // double浮点类型,包含D
-            else if (StringUtils.containsIgnoreCase(str, "D")) {
-                classs.add(new Object[]{Double.valueOf(StringUtils.replaceIgnoreCase(str, "D", "")), 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;
-    }
-}

+ 0 - 18
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java

@@ -1,18 +0,0 @@
-package com.ruoyi.quartz.util;
-
-import org.quartz.DisallowConcurrentExecution;
-import org.quartz.JobExecutionContext;
-import com.ruoyi.quartz.domain.SysJob;
-
-/**
- * 定时任务处理(禁止并发执行)
- *
- * @author ruoyi
- */
-@DisallowConcurrentExecution
-public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
-    @Override
-    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
-        JobInvokeUtil.invokeMethod(sysJob);
-    }
-}

+ 0 - 16
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java

@@ -1,16 +0,0 @@
-package com.ruoyi.quartz.util;
-
-import org.quartz.JobExecutionContext;
-import com.ruoyi.quartz.domain.SysJob;
-
-/**
- * 定时任务处理(允许并发执行)
- *
- * @author ruoyi
- */
-public class QuartzJobExecution extends AbstractQuartzJob {
-    @Override
-    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
-        JobInvokeUtil.invokeMethod(sysJob);
-    }
-}

+ 0 - 59
ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java

@@ -21,65 +21,6 @@ import com.ruoyi.quartz.domain.SysJob;
  * @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);
-
-        // 按新的cronExpression表达式构建一个新的trigger
-        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));
-        }
-
-        scheduler.scheduleJob(jobDetail, trigger);
-
-        // 暂停任务
-        if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
-            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
-        }
-    }
 
     /**
      * 设置定时任务策略

+ 99 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/InfJobController.java

@@ -0,0 +1,99 @@
+package cn.iocoder.dashboard.modules.infra.controller.job;
+
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.*;
+import cn.iocoder.dashboard.modules.infra.convert.job.InfJobConvert;
+import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO;
+import cn.iocoder.dashboard.modules.infra.service.job.InfJobService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT;
+
+@Api(tags = "定时任务")
+@RestController
+@RequestMapping("/infra/job")
+@Validated
+public class InfJobController {
+
+    @Resource
+    private InfJobService jobService;
+
+    @PostMapping("/create")
+    @ApiOperation("创建定时任务")
+    @PreAuthorize("@ss.hasPermission('infra:job:create')")
+    public CommonResult<Long> createJob(@Valid @RequestBody InfJobCreateReqVO createReqVO) {
+        return success(jobService.createJob(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @ApiOperation("更新定时任务")
+    @PreAuthorize("@ss.hasPermission('infra:job:update')")
+    public CommonResult<Boolean> updateJob(@Valid @RequestBody InfJobUpdateReqVO updateReqVO) {
+        jobService.updateJob(updateReqVO);
+        return success(true);
+    }
+
+	@DeleteMapping("/delete")
+    @ApiOperation("删除定时任务")
+    @ApiImplicitParam(name = "id", value = "编号", required = true)
+	@PreAuthorize("@ss.hasPermission('infra:job:delete')")
+    public CommonResult<Boolean> deleteJob(@RequestParam("id") Long id) {
+        jobService.deleteJob(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @ApiOperation("获得定时任务")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
+    @PreAuthorize("@ss.hasPermission('infra:job:query')")
+    public CommonResult<InfJobRespVO> getJob(@RequestParam("id") Long id) {
+        InfJobDO job = jobService.getJob(id);
+        return success(InfJobConvert.INSTANCE.convert(job));
+    }
+
+    @GetMapping("/list")
+    @ApiOperation("获得定时任务列表")
+    @ApiImplicitParam(name = "ids", value = "编号列表", required = true, dataTypeClass = List.class)
+    @PreAuthorize("@ss.hasPermission('infra:job:query')")
+    public CommonResult<List<InfJobRespVO>> getJobList(@RequestParam("ids") Collection<Long> ids) {
+        List<InfJobDO> list = jobService.getJobList(ids);
+        return success(InfJobConvert.INSTANCE.convertList(list));
+    }
+
+    @GetMapping("/page")
+    @ApiOperation("获得定时任务分页")
+    @PreAuthorize("@ss.hasPermission('infra:job:query')")
+    public CommonResult<PageResult<InfJobRespVO>> getJobPage(@Valid InfJobPageReqVO pageVO) {
+        PageResult<InfJobDO> pageResult = jobService.getJobPage(pageVO);
+        return success(InfJobConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @ApiOperation("导出定时任务 Excel")
+    @PreAuthorize("@ss.hasPermission('infra:job:export')")
+    @OperateLog(type = EXPORT)
+    public void exportJobExcel(@Valid InfJobExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<InfJobDO> list = jobService.getJobList(exportReqVO);
+        // 导出 Excel
+        List<InfJobExcelVO> datas = InfJobConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "定时任务.xls", "数据", InfJobExcelVO.class, datas);
+    }
+
+}

+ 57 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobBaseVO.java

@@ -0,0 +1,57 @@
+package cn.iocoder.dashboard.modules.infra.controller.job.vo.job;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+
+import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+/**
+* 定时任务 Base VO,提供给添加、修改、详细的子 VO 使用
+* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+*/
+@Data
+public class InfJobBaseVO {
+
+    @ApiModelProperty(value = "任务名称", required = true, example = "测试任务")
+    @NotNull(message = "任务名称不能为空")
+    private String name;
+
+    @ApiModelProperty(value = "任务状态", required = true, example = "1", notes = "参见 InfJobStatusEnum 枚举")
+    @NotNull(message = "任务状态不能为空")
+    private Integer status;
+
+    @ApiModelProperty(value = "处理器的名字", required = true, example = "sysUserSessionTimeoutJob")
+    @NotNull(message = "处理器的名字不能为空")
+    private String handlerName;
+
+    @ApiModelProperty(value = "处理器的参数", example = "yudao")
+    private String handlerParam;
+
+    @ApiModelProperty(value = "CRON 表达式", required = true, example = "0/10 * * * * ? *")
+    @NotNull(message = "CRON 表达式不能为空")
+    private String cronExpression;
+
+    @ApiModelProperty(value = "最后一次执行的开始时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date executeBeginTime;
+
+    @ApiModelProperty(value = "最后一次执行的结束时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date executeEndTime;
+
+    @ApiModelProperty(value = "上一次触发时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date firePrevTime;
+
+    @ApiModelProperty(value = "下一次触发时间")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    private Date fireNextTime;
+
+    @ApiModelProperty(value = "监控超时时间", example = "1000")
+    private Integer monitorTimeout;
+
+}

+ 14 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.dashboard.modules.infra.controller.job.vo.job;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@ApiModel("定时任务创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfJobCreateReqVO extends InfJobBaseVO {
+
+}

+ 57 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobExcelVO.java

@@ -0,0 +1,57 @@
+package cn.iocoder.dashboard.modules.infra.controller.job.vo.job;
+
+import cn.iocoder.dashboard.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.dashboard.framework.excel.core.convert.DictConvert;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.modules.system.enums.dict.SysDictTypeEnum.INF_JOB_STATUS;
+
+/**
+ * 定时任务 Excel VO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class InfJobExcelVO {
+
+    @ExcelProperty("任务编号")
+    private Long id;
+
+    @ExcelProperty("任务名称")
+    private String name;
+
+    @ExcelProperty(value = "任务状态", converter = DictConvert.class)
+    @DictFormat(INF_JOB_STATUS)
+    private Integer status;
+
+    @ExcelProperty("处理器的名字")
+    private String handlerName;
+
+    @ExcelProperty("处理器的参数")
+    private String handlerParam;
+
+    @ExcelProperty("CRON 表达式")
+    private String cronExpression;
+
+    @ExcelProperty("最后一次执行的开始时间")
+    private Date executeBeginTime;
+
+    @ExcelProperty("最后一次执行的结束时间")
+    private Date executeEndTime;
+
+    @ExcelProperty("上一次触发时间")
+    private Date firePrevTime;
+
+    @ExcelProperty("下一次触发时间")
+    private Date fireNextTime;
+
+    @ExcelProperty("监控超时时间")
+    private Integer monitorTimeout;
+
+    @ExcelProperty("创建时间")
+    private Date createTime;
+
+}

+ 20 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobExportReqVO.java

@@ -0,0 +1,20 @@
+package cn.iocoder.dashboard.modules.infra.controller.job.vo.job;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@ApiModel(value = "定时任务 Excel 导出 Request VO", description = "参数和 InfJobPageReqVO 是一致的")
+@Data
+public class InfJobExportReqVO {
+
+    @ApiModelProperty(value = "任务名称", example = "测试任务", notes = "模糊匹配")
+    private String name;
+
+    @ApiModelProperty(value = "任务状态", example = "1", notes = "参见 InfJobStatusEnum 枚举")
+    private Integer status;
+
+    @ApiModelProperty(value = "处理器的名字", example = "sysUserSessionTimeoutJob", notes = "模糊匹配")
+    private String handlerName;
+
+}

+ 25 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobPageReqVO.java

@@ -0,0 +1,25 @@
+package cn.iocoder.dashboard.modules.infra.controller.job.vo.job;
+
+import cn.iocoder.dashboard.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@ApiModel("定时任务分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfJobPageReqVO extends PageParam {
+
+    @ApiModelProperty(value = "任务名称", example = "测试任务", notes = "模糊匹配")
+    private String name;
+
+    @ApiModelProperty(value = "任务状态", example = "1", notes = "参见 InfJobStatusEnum 枚举")
+    private Integer status;
+
+    @ApiModelProperty(value = "处理器的名字", example = "sysUserSessionTimeoutJob", notes = "模糊匹配")
+    private String handlerName;
+
+}

+ 23 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobRespVO.java

@@ -0,0 +1,23 @@
+package cn.iocoder.dashboard.modules.infra.controller.job.vo.job;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.Date;
+
+@ApiModel("定时任务 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfJobRespVO extends InfJobBaseVO {
+
+    @ApiModelProperty(value = "任务编号", required = true, example = "1024")
+    private Long id;
+
+    @ApiModelProperty(value = "创建时间", required = true)
+    private Date createTime;
+
+}

+ 21 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobUpdateReqVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.dashboard.modules.infra.controller.job.vo.job;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@ApiModel("定时任务更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class InfJobUpdateReqVO extends InfJobBaseVO {
+
+    @ApiModelProperty(value = "任务编号", required = true, example = "1024")
+    @NotNull(message = "任务编号不能为空")
+    private Long id;
+
+}

+ 36 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/convert/job/InfJobConvert.java

@@ -0,0 +1,36 @@
+package cn.iocoder.dashboard.modules.infra.convert.job;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobCreateReqVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobExcelVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobRespVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobUpdateReqVO;
+import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 定时任务 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfJobConvert {
+
+    InfJobConvert INSTANCE = Mappers.getMapper(InfJobConvert.class);
+
+    InfJobDO convert(InfJobCreateReqVO bean);
+
+    InfJobDO convert(InfJobUpdateReqVO bean);
+
+    InfJobRespVO convert(InfJobDO bean);
+
+    List<InfJobRespVO> convertList(List<InfJobDO> list);
+
+    PageResult<InfJobRespVO> convertPage(PageResult<InfJobDO> page);
+
+    List<InfJobExcelVO> convertList02(List<InfJobDO> list);
+
+}

+ 6 - 4
src/main/java/cn/iocoder/dashboard/modules/infra/dal/dataobject/job/InfJobDO.java

@@ -2,6 +2,8 @@ package cn.iocoder.dashboard.modules.infra.dal.dataobject.job;
 
 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
 import cn.iocoder.dashboard.modules.infra.enums.job.InfJobStatusEnum;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
@@ -43,6 +45,7 @@ public class InfJobDO extends BaseDO {
     /**
      * 处理器的参数
      */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
     private String handlerParam;
 
     // ========== 时间相关字段 ==========
@@ -75,13 +78,12 @@ public class InfJobDO extends BaseDO {
     private Date fireNextTime;
 
     // ========== 监控相关字段 ==========
-    /**
-     * 监控开关
-     */
-    private Boolean monitorSwitch;
     /**
      * 监控超时时间,单位:毫秒
+     *
+     * 注意,这里的超时的目的,不是进行任务的取消,而是告警任务的执行时间过长
      */
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
     private Integer monitorTimeout;
 
     // TODO misfirePolicy

+ 37 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/dal/mysql/job/InfJobMapper.java

@@ -0,0 +1,37 @@
+package cn.iocoder.dashboard.modules.infra.dal.mysql.job;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobExportReqVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobPageReqVO;
+import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 定时任务 Mapper
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface InfJobMapper extends BaseMapperX<InfJobDO> {
+
+    default PageResult<InfJobDO> selectPage(InfJobPageReqVO reqVO) {
+        return selectPage(reqVO, new QueryWrapperX<InfJobDO>()
+                .likeIfPresent("name", reqVO.getName())
+                .eqIfPresent("status", reqVO.getStatus())
+                .likeIfPresent("handler_name", reqVO.getHandlerName())
+        );
+    }
+
+    default List<InfJobDO> selectList(InfJobExportReqVO reqVO) {
+        return selectList(new QueryWrapperX<InfJobDO>()
+                .likeIfPresent("name", reqVO.getName())
+                .eqIfPresent("status", reqVO.getStatus())
+                .likeIfPresent("handler_name", reqVO.getHandlerName())
+        );
+    }
+
+}

+ 0 - 1
src/main/java/cn/iocoder/dashboard/modules/infra/dal/mysql/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.dashboard.modules.infra.dal.mysql;

+ 3 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/enums/InfErrorCodeConstants.java

@@ -15,4 +15,7 @@ public interface InfErrorCodeConstants {
     ErrorCode CONFIG_CAN_NOT_DELETE_SYSTEM_TYPE = new ErrorCode(1001000003, "不能删除类型为系统内置的参数配置");
     ErrorCode CONFIG_GET_VALUE_ERROR_IF_SENSITIVE = new ErrorCode(1001000004, "不允许获取敏感配置到前端");
 
+    // ========== 定时任务 1001001000 ==========
+    ErrorCode JOB_NOT_EXISTS = new ErrorCode(1001001000, "定时任务不存在");
+
 }

+ 75 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/service/job/InfJobService.java

@@ -0,0 +1,75 @@
+package cn.iocoder.dashboard.modules.infra.service.job;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobCreateReqVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobExportReqVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobPageReqVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobUpdateReqVO;
+import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO;
+
+import javax.validation.Valid;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 定时任务 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface InfJobService {
+
+    /**
+     * 创建定时任务
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createJob(@Valid InfJobCreateReqVO createReqVO);
+
+    /**
+     * 更新定时任务
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateJob(@Valid InfJobUpdateReqVO updateReqVO);
+
+    /**
+     * 删除定时任务
+     *
+     * @param id 编号
+     */
+    void deleteJob(Long id);
+
+    /**
+     * 获得定时任务
+     *
+     * @param id 编号
+     * @return 定时任务
+     */
+    InfJobDO getJob(Long id);
+
+    /**
+     * 获得定时任务列表
+     *
+     * @param ids 编号
+     * @return 定时任务列表
+     */
+    List<InfJobDO> getJobList(Collection<Long> ids);
+
+    /**
+     * 获得定时任务分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 定时任务分页
+     */
+    PageResult<InfJobDO> getJobPage(InfJobPageReqVO pageReqVO);
+
+    /**
+     * 获得定时任务列表, 用于 Excel 导出
+     *
+     * @param exportReqVO 查询条件
+     * @return 定时任务分页
+     */
+    List<InfJobDO> getJobList(InfJobExportReqVO exportReqVO);
+
+}

+ 86 - 0
src/main/java/cn/iocoder/dashboard/modules/infra/service/job/impl/InfJobServiceImpl.java

@@ -0,0 +1,86 @@
+package cn.iocoder.dashboard.modules.infra.service.job.impl;
+
+import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobCreateReqVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobExportReqVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobPageReqVO;
+import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobUpdateReqVO;
+import cn.iocoder.dashboard.modules.infra.convert.job.InfJobConvert;
+import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO;
+import cn.iocoder.dashboard.modules.infra.dal.mysql.job.InfJobMapper;
+import cn.iocoder.dashboard.modules.infra.service.job.InfJobService;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.Collection;
+import java.util.List;
+
+import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_NOT_EXISTS;
+
+/**
+ * 定时任务 Service 实现类
+ *
+ * @author 芋道源码
+ */
+@Service
+@Validated
+public class InfJobServiceImpl implements InfJobService {
+
+    @Resource
+    private InfJobMapper jobMapper;
+
+    @Override
+    public Long createJob(InfJobCreateReqVO createReqVO) {
+        // 插入
+        InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
+        jobMapper.insert(job);
+        // 返回
+        return job.getId();
+    }
+
+    @Override
+    public void updateJob(InfJobUpdateReqVO updateReqVO) {
+        // 校验存在
+        this.validateJobExists(updateReqVO.getId());
+        // 更新
+        InfJobDO updateObj = InfJobConvert.INSTANCE.convert(updateReqVO);
+        jobMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteJob(Long id) {
+        // 校验存在
+        this.validateJobExists(id);
+        // 更新
+        jobMapper.deleteById(id);
+    }
+
+    private void validateJobExists(Long id) {
+        if (jobMapper.selectById(id) == null) {
+            throw ServiceExceptionUtil.exception(JOB_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public InfJobDO getJob(Long id) {
+        return jobMapper.selectById(id);
+    }
+
+    @Override
+    public List<InfJobDO> getJobList(Collection<Long> ids) {
+        return jobMapper.selectBatchIds(ids);
+    }
+
+    @Override
+    public PageResult<InfJobDO> getJobPage(InfJobPageReqVO pageReqVO) {
+		return jobMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<InfJobDO> getJobList(InfJobExportReqVO exportReqVO) {
+		return jobMapper.selectList(exportReqVO);
+    }
+
+}

+ 0 - 1
src/main/java/cn/iocoder/dashboard/modules/infra/service/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.dashboard.modules.infra.service;

+ 1 - 0
src/main/java/cn/iocoder/dashboard/modules/system/enums/dict/SysDictTypeEnum.java

@@ -18,6 +18,7 @@ public enum SysDictTypeEnum {
     SYS_BOOLEAN_STRING("sys_boolean_string"), // Boolean 是否类型
 
     INF_REDIS_TIMEOUT_TYPE("inf_redis_timeout_type"),  // Redis 超时类型
+    INF_JOB_STATUS("inf_job_status") // 任务状态的枚举
     ;
 
 

+ 10 - 0
src/main/resources/codegen/java/controller/vo/baseVO.vm

@@ -4,6 +4,16 @@ import lombok.*;
 import java.util.*;
 import io.swagger.annotations.*;
 import javax.validation.constraints.*;
+## 处理 Date 字段的引入
+#foreach ($column in $columns)
+#if (${column.createOperation} && ${column.updateOperation} && ${column.listOperationResult})##通用操作
+    && ${column.javaType} == "Date")## 时间类型
+import org.springframework.format.annotation.DateTimeFormat;
+
+import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+#break
+#end
+#end
 
 /**
 * ${table.classComment} Base VO,提供给添加、修改、详细的子 VO 使用

+ 6 - 0
src/test/java/cn/iocoder/dashboard/framework/quartz/core/scheduler/SchedulerManagerTest.java

@@ -29,6 +29,12 @@ class SchedulerManagerTest {
         schedulerManager.updateJob(jobHandlerName, "hahaha", "0/20 * * * * ? *");
     }
 
+    @Test
+    public void testDeleteJob() throws SchedulerException {
+        String jobHandlerName = StrUtil.lowerFirst(SysUserSessionTimeoutJob.class.getSimpleName());
+        schedulerManager.deleteJob(jobHandlerName);
+    }
+
     @Test
     public void testPauseJob() throws SchedulerException {
         String jobHandlerName = StrUtil.lowerFirst(SysUserSessionTimeoutJob.class.getSimpleName());