第09章 - 定时任务模块

第09章 - 定时任务模块

9.1 定时任务概述

9.1.1 功能介绍

RuoYi-Cloud的定时任务模块(ruoyi-job)基于Quartz框架实现,提供了在线管理定时任务的功能:

  • 在线添加、修改、删除定时任务
  • 支持立即执行任务
  • 任务执行日志记录
  • 支持Cron表达式
  • 支持简单触发器配置
  • 任务暂停和恢复

9.1.2 项目结构

ruoyi-modules/ruoyi-job/
├── src/main/java/com/ruoyi/job/
│   ├── RuoYiJobApplication.java      # 启动类
│   ├── config/
│   │   └── ScheduleConfig.java       # Quartz配置
│   ├── controller/
│   │   ├── SysJobController.java     # 任务管理控制器
│   │   └── SysJobLogController.java  # 任务日志控制器
│   ├── domain/
│   │   ├── SysJob.java               # 定时任务实体
│   │   └── SysJobLog.java            # 任务日志实体
│   ├── mapper/
│   │   ├── SysJobMapper.java
│   │   └── SysJobLogMapper.java
│   ├── service/
│   │   ├── ISysJobService.java
│   │   ├── ISysJobLogService.java
│   │   └── impl/
│   ├── task/
│   │   └── RyTask.java               # 任务示例
│   └── util/
│       ├── AbstractQuartzJob.java    # 抽象任务类
│       ├── CronUtils.java            # Cron工具类
│       ├── JobInvokeUtil.java        # 任务执行工具
│       ├── QuartzDisallowConcurrentExecution.java
│       ├── QuartzJobExecution.java
│       └── ScheduleUtils.java        # 调度工具类
└── src/main/resources/
    ├── bootstrap.yml
    └── mapper/job/

9.2 Quartz配置

9.2.1 依赖配置

<!-- quartz依赖 -->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <exclusions>
        <exclusion>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
        </exclusion>
    </exclusions>
</dependency>

9.2.2 Quartz配置类

@Configuration
public class ScheduleConfig {
    
    @Bean
    public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setDataSource(dataSource);
        
        // quartz参数
        Properties prop = new Properties();
        prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler");
        prop.put("org.quartz.scheduler.instanceId", "AUTO");
        // 线程池配置
        prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
        prop.put("org.quartz.threadPool.threadCount", "20");
        prop.put("org.quartz.threadPool.threadPriority", "5");
        // JobStore配置
        prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
        // 集群配置
        prop.put("org.quartz.jobStore.isClustered", "true");
        prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
        prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10");
        prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true");
        // sqlserver 启用
        // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
        prop.put("org.quartz.jobStore.misfireThreshold", "12000");
        prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
        factory.setQuartzProperties(prop);
        
        factory.setSchedulerName("RuoyiScheduler");
        // 延时启动
        factory.setStartupDelay(1);
        factory.setApplicationContextSchedulerContextKey("applicationContextKey");
        // 可选,QuartzScheduler 启动时更新己存在的Job
        factory.setOverwriteExistingJobs(true);
        // 设置自动启动,默认为true
        factory.setAutoStartup(true);
        
        return factory;
    }
}

9.2.3 数据库表结构

Quartz需要以下数据库表:

-- 任务详细信息表
CREATE TABLE QRTZ_JOB_DETAILS (
    sched_name varchar(120) NOT NULL COMMENT '调度名称',
    job_name varchar(200) NOT NULL COMMENT '任务名称',
    job_group varchar(200) NOT NULL COMMENT '任务组名',
    description varchar(250) DEFAULT NULL COMMENT '相关介绍',
    job_class_name varchar(250) NOT NULL COMMENT '执行任务类名称',
    is_durable varchar(1) NOT NULL COMMENT '是否持久化',
    is_nonconcurrent varchar(1) NOT NULL COMMENT '是否并发',
    is_update_data varchar(1) NOT NULL COMMENT '是否更新数据',
    requests_recovery varchar(1) NOT NULL COMMENT '是否接受恢复执行',
    job_data blob DEFAULT NULL COMMENT '存放持久化job对象',
    PRIMARY KEY (sched_name,job_name,job_group)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 触发器详细信息表
CREATE TABLE QRTZ_TRIGGERS (
    sched_name varchar(120) NOT NULL COMMENT '调度名称',
    trigger_name varchar(200) NOT NULL COMMENT '触发器名称',
    trigger_group varchar(200) NOT NULL COMMENT '触发器组名',
    job_name varchar(200) NOT NULL COMMENT '任务名称',
    job_group varchar(200) NOT NULL COMMENT '任务组名',
    description varchar(250) DEFAULT NULL COMMENT '相关介绍',
    next_fire_time bigint(13) DEFAULT NULL COMMENT '上一次触发时间',
    prev_fire_time bigint(13) DEFAULT NULL COMMENT '下一次触发时间',
    priority int(11) DEFAULT NULL COMMENT '优先级',
    trigger_state varchar(16) NOT NULL COMMENT '触发器状态',
    trigger_type varchar(8) NOT NULL COMMENT '触发器的类型',
    start_time bigint(13) NOT NULL COMMENT '开始时间',
    end_time bigint(13) DEFAULT NULL COMMENT '结束时间',
    calendar_name varchar(200) DEFAULT NULL COMMENT '日程表名称',
    misfire_instr smallint(2) DEFAULT NULL COMMENT '补偿执行的策略',
    job_data blob DEFAULT NULL COMMENT '存放持久化job对象',
    PRIMARY KEY (sched_name,trigger_name,trigger_group),
    KEY sched_name (sched_name,job_name,job_group),
    CONSTRAINT QRTZ_TRIGGERS_ibfk_1 FOREIGN KEY (sched_name,job_name,job_group) REFERENCES QRTZ_JOB_DETAILS (sched_name,job_name,job_group)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 更多Quartz表结构请参考sql/quartz.sql

9.3 定时任务实体

9.3.1 任务实体类

public class SysJob extends BaseEntity {
    
    /** 任务ID */
    @Excel(name = "任务序号", cellType = ColumnType.NUMERIC)
    private Long jobId;
    
    /** 任务名称 */
    @Excel(name = "任务名称")
    @NotBlank(message = "任务名称不能为空")
    @Size(min = 0, max = 64, message = "任务名称不能超过64个字符")
    private String jobName;
    
    /** 任务组名 */
    @Excel(name = "任务组名")
    private String jobGroup;
    
    /** 调用目标字符串 */
    @Excel(name = "调用目标字符串")
    @NotBlank(message = "调用目标字符串不能为空")
    @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符")
    private String invokeTarget;
    
    /** cron执行表达式 */
    @Excel(name = "执行表达式")
    @NotBlank(message = "Cron执行表达式不能为空")
    @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符")
    private String cronExpression;
    
    /** cron计划策略 0=默认 1=立即触发执行 2=触发一次执行 3=不触发立即执行 */
    @Excel(name = "计划策略", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行")
    private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT;
    
    /** 是否并发执行(0允许 1禁止) */
    @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止")
    private String concurrent;
    
    /** 任务状态(0正常 1暂停) */
    @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停")
    private String status;
    
    // getter/setter...
}

9.3.2 任务日志实体

public class SysJobLog extends BaseEntity {
    
    /** ID */
    @Excel(name = "日志序号")
    private Long jobLogId;
    
    /** 任务名称 */
    @Excel(name = "任务名称")
    private String jobName;
    
    /** 任务组名 */
    @Excel(name = "任务组名")
    private String jobGroup;
    
    /** 调用目标字符串 */
    @Excel(name = "调用目标字符串")
    private String invokeTarget;
    
    /** 日志信息 */
    @Excel(name = "日志信息")
    private String jobMessage;
    
    /** 执行状态(0正常 1失败) */
    @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败")
    private String status;
    
    /** 异常信息 */
    @Excel(name = "异常信息")
    private String exceptionInfo;
    
    /** 开始时间 */
    private Date startTime;
    
    /** 停止时间 */
    private Date stopTime;
    
    // getter/setter...
}

9.4 任务调度工具

9.4.1 调度工具类

public class ScheduleUtils {
    
    /**
     * 得到quartz任务类
     */
    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 Exception {
        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));
        }
        
        // 判断任务是否过期
        if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) {
            // 执行调度任务
            scheduler.scheduleJob(jobDetail, trigger);
        }
        
        // 暂停任务
        if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) {
            scheduler.pauseJob(getJobKey(jobId, jobGroup));
        }
    }
    
    /**
     * 更新定时任务
     */
    public static void updateScheduleJob(Scheduler scheduler, SysJob job, String jobGroup) throws Exception {
        JobKey jobKey = getJobKey(job.getJobId(), jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除,然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        createScheduleJob(scheduler, job);
    }
    
    /**
     * 立即执行任务
     */
    public static void run(Scheduler scheduler, SysJob job) throws Exception {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, job);
        JobKey jobKey = getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            scheduler.triggerJob(jobKey, dataMap);
        } else {
            throw new TaskException("任务不存在或已删除", Code.NOT_FOUND);
        }
    }
    
    /**
     * 暂停任务
     */
    public static void pauseJob(Scheduler scheduler, Long jobId, String jobGroup) throws Exception {
        scheduler.pauseJob(getJobKey(jobId, jobGroup));
    }
    
    /**
     * 恢复任务
     */
    public static void resumeJob(Scheduler scheduler, Long jobId, String jobGroup) throws Exception {
        scheduler.resumeJob(getJobKey(jobId, jobGroup));
    }
    
    /**
     * 删除定时任务
     */
    public static void deleteScheduleJob(Scheduler scheduler, Long jobId, String jobGroup) throws Exception {
        scheduler.deleteJob(getJobKey(jobId, jobGroup));
    }
    
    /**
     * 设置定时任务策略
     */
    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) {
        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", Code.CONFIG_ERROR);
        }
    }
}

9.4.2 任务执行工具

public class JobInvokeUtil {
    
    /**
     * 执行方法
     */
    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);
        }
    }
    
    /**
     * 调用任务方法
     */
    private static void invokeMethod(Object bean, String methodName, List<Object[]> methodParams)
            throws Exception {
        if (methodParams != null && 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);
        }
    }
    
    /**
     * 校验是否为白名单类
     */
    public static boolean isValidClassName(String invokeTarget) {
        return StringUtils.startsWithAny(invokeTarget, Constants.JOB_WHITELIST_STR);
    }
    
    /**
     * 获取bean名称
     */
    public static String getBeanName(String invokeTarget) {
        String beanName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringBeforeLast(beanName, ".");
    }
    
    /**
     * 获取bean方法
     */
    public static String getMethodName(String invokeTarget) {
        String methodName = StringUtils.substringBefore(invokeTarget, "(");
        return StringUtils.substringAfterLast(methodName, ".");
    }
    
    /**
     * 获取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]);
            // 字符串类型
            if (StringUtils.startsWithAny(str, "'", "\"")) {
                classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class });
            }
            // 布尔类型
            else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) {
                classs.add(new Object[] { Boolean.valueOf(str), Boolean.class });
            }
            // 长整型
            else if (StringUtils.endsWith(str, "L")) {
                classs.add(new Object[] { Long.valueOf(StringUtils.substringBefore(str, "L")), Long.class });
            }
            // 浮点型
            else if (StringUtils.endsWith(str, "D")) {
                classs.add(new Object[] { Double.valueOf(StringUtils.substringBefore(str, "D")), Double.class });
            }
            // 整型
            else {
                classs.add(new Object[] { Integer.valueOf(str), Integer.class });
            }
        }
        return classs;
    }
}

9.5 任务执行类

9.5.1 抽象任务类

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);
        }
    }
    
    /**
     * 执行前
     */
    protected void before(JobExecutionContext context, SysJob sysJob) {
        threadLocal.set(new Date());
    }
    
    /**
     * 执行后
     */
    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);
    }
    
    /**
     * 执行方法,由子类重载
     */
    protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception;
}

9.5.2 允许并发执行

public class QuartzJobExecution extends AbstractQuartzJob {
    
    @Override
    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
        JobInvokeUtil.invokeMethod(sysJob);
    }
}

9.5.3 禁止并发执行

@DisallowConcurrentExecution
public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob {
    
    @Override
    protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception {
        JobInvokeUtil.invokeMethod(sysJob);
    }
}

9.6 自定义任务

9.6.1 任务示例

@Component("ryTask")
public class RyTask {
    
    /**
     * 无参方法
     */
    public void ryNoParams() {
        System.out.println("执行无参方法");
    }
    
    /**
     * 有参方法
     */
    public void ryParams(String s) {
        System.out.println("执行有参方法:" + s);
    }
    
    /**
     * 多参方法
     */
    public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) {
        System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i));
    }
}

9.6.2 调用目标字符串格式

示例 说明
ryTask.ryNoParams 调用无参方法
ryTask.ryParams('ry') 调用字符串参数方法
ryTask.ryMultipleParams('ry', true, 2000L, 316.50D, 100) 调用多参方法

9.6.3 Cron表达式说明

Cron表达式由6或7个字段组成,用空格分隔:

秒 分 时 日 月 周 [年]
字段 允许值 特殊字符
0-59 , - * /
0-59 , - * /
0-23 , - * /
1-31 , - * / ? L W
1-12或JAN-DEC , - * /
1-7或SUN-SAT , - * / ? L #
可选,1970-2099 , - * /

常用表达式:

表达式 说明
0 0 12 * * ? 每天12点执行
0 15 10 ? * * 每天10:15执行
0 0/5 14 * * ? 每天14点到14:55,每5分钟执行
0 0 10,14,16 * * ? 每天10点、14点、16点执行
0 0 12 ? * WED 每周三12点执行
0 0 12 1 * ? 每月1日12点执行

9.7 任务服务实现

9.7.1 任务服务

@Service
public class SysJobServiceImpl implements ISysJobService {
    
    @Autowired
    private Scheduler scheduler;
    
    @Autowired
    private SysJobMapper jobMapper;
    
    /**
     * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理
     */
    @PostConstruct
    public void init() throws Exception {
        scheduler.clear();
        List<SysJob> jobList = jobMapper.selectJobAll();
        for (SysJob job : jobList) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
    }
    
    /**
     * 新增任务
     */
    @Override
    @Transactional
    public int insertJob(SysJob job) throws Exception {
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = jobMapper.insertJob(job);
        if (rows > 0) {
            ScheduleUtils.createScheduleJob(scheduler, job);
        }
        return rows;
    }
    
    /**
     * 更新任务
     */
    @Override
    @Transactional
    public int updateJob(SysJob job) throws Exception {
        SysJob properties = selectJobById(job.getJobId());
        int rows = jobMapper.updateJob(job);
        if (rows > 0) {
            updateSchedulerJob(job, properties.getJobGroup());
        }
        return rows;
    }
    
    /**
     * 更新任务的时间表达式
     */
    public void updateSchedulerJob(SysJob job, String jobGroup) throws Exception {
        Long jobId = job.getJobId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除,然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }
    
    /**
     * 暂停任务
     */
    @Override
    @Transactional
    public int pauseJob(SysJob job) throws Exception {
        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;
    }
    
    /**
     * 恢复任务
     */
    @Override
    @Transactional
    public int resumeJob(SysJob job) throws Exception {
        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;
    }
    
    /**
     * 立即运行任务
     */
    @Override
    @Transactional
    public boolean run(SysJob job) throws Exception {
        boolean result = false;
        Long jobId = job.getJobId();
        SysJob tmpObj = selectJobById(jobId);
        if (tmpObj != null) {
            ScheduleUtils.run(scheduler, tmpObj);
            result = true;
        }
        return result;
    }
    
    /**
     * 删除任务
     */
    @Override
    @Transactional
    public int deleteJob(SysJob job) throws Exception {
        Long jobId = job.getJobId();
        String jobGroup = job.getJobGroup();
        int rows = jobMapper.deleteJobById(jobId);
        if (rows > 0) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows;
    }
}

9.8 小结

本章详细介绍了RuoYi-Cloud的定时任务模块,包括:

  1. 模块概述:基于Quartz的在线任务管理
  2. Quartz配置:调度器、触发器配置
  3. 任务实体:任务和日志实体类
  4. 调度工具:任务创建、更新、删除、执行
  5. 任务执行:并发与非并发执行
  6. 自定义任务:任务编写方法
  7. Cron表达式:表达式语法和常用示例

掌握定时任务模块可以轻松实现系统中的各种定时业务需求。


上一章:代码生成器使用 | 返回目录 | 下一章:文件服务与存储

posted @ 2026-01-08 14:05  我才是银古  阅读(25)  评论(0)    收藏  举报