SpringBoot + MyBatis + Quartz 实现持久化定时发送邮件功能

一、pom.xml 配置

                <!-- 定时任务 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.3</version>
        </dependency>
                <!-- 添加邮件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>

二、目录结构 和数据库表


image.png

image.png

image.png

系统表

所有表

创建表sql

CREATE TABLE `sys_quartz_sys_data` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `key_code` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `job_class_Name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `job_group_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `execution_time` datetime DEFAULT NULL,
  `job_data` text CHARACTER SET utf8,
  `del_flag` char(255) CHARACTER SET utf8 DEFAULT NULL,
  `edit_flag` char(255) CHARACTER SET utf8 DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `id` (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=888 DEFAULT CHARSET=latin1;



CREATE TABLE `QRTZ_BLOB_TRIGGERS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `TRIGGER_NAME` varchar(200) NOT NULL,
  `TRIGGER_GROUP` varchar(200) NOT NULL,
  `BLOB_DATA` blob,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`) USING BTREE,
  CONSTRAINT `QRTZ_BLOB_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE `QRTZ_CALENDARS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `CALENDAR_NAME` varchar(200) NOT NULL,
  `CALENDAR` blob NOT NULL,
  PRIMARY KEY (`SCHED_NAME`,`CALENDAR_NAME`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;


CREATE TABLE `QRTZ_CRON_TRIGGERS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `TRIGGER_NAME` varchar(200) NOT NULL,
  `TRIGGER_GROUP` varchar(200) NOT NULL,
  `CRON_EXPRESSION` varchar(200) NOT NULL,
  `TIME_ZONE_ID` varchar(80) DEFAULT NULL,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`) USING BTREE,
  CONSTRAINT `QRTZ_CRON_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE `QRTZ_FIRED_TRIGGERS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `ENTRY_ID` varchar(95) NOT NULL,
  `TRIGGER_NAME` varchar(200) NOT NULL,
  `TRIGGER_GROUP` varchar(200) NOT NULL,
  `INSTANCE_NAME` varchar(200) NOT NULL,
  `FIRED_TIME` bigint(13) NOT NULL,
  `SCHED_TIME` bigint(13) NOT NULL,
  `PRIORITY` int(11) NOT NULL,
  `STATE` varchar(16) NOT NULL,
  `JOB_NAME` varchar(200) DEFAULT NULL,
  `JOB_GROUP` varchar(200) DEFAULT NULL,
  `IS_NONCONCURRENT` varchar(1) DEFAULT NULL,
  `REQUESTS_RECOVERY` varchar(1) DEFAULT NULL,
  PRIMARY KEY (`SCHED_NAME`,`ENTRY_ID`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE `QRTZ_JOB_DETAILS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `JOB_NAME` varchar(200) NOT NULL,
  `JOB_GROUP` varchar(200) NOT NULL,
  `DESCRIPTION` varchar(250) DEFAULT NULL,
  `JOB_CLASS_NAME` varchar(250) NOT NULL,
  `IS_DURABLE` varchar(1) NOT NULL,
  `IS_NONCONCURRENT` varchar(1) NOT NULL,
  `IS_UPDATE_DATA` varchar(1) NOT NULL,
  `REQUESTS_RECOVERY` varchar(1) NOT NULL,
  `JOB_DATA` blob,
  PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE `QRTZ_LOCKS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `LOCK_NAME` varchar(40) NOT NULL,
  PRIMARY KEY (`SCHED_NAME`,`LOCK_NAME`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE `QRTZ_PAUSED_TRIGGER_GRPS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `TRIGGER_GROUP` varchar(200) NOT NULL,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_GROUP`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;


CREATE TABLE `QRTZ_SCHEDULER_STATE` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `INSTANCE_NAME` varchar(200) NOT NULL,
  `LAST_CHECKIN_TIME` bigint(13) NOT NULL,
  `CHECKIN_INTERVAL` bigint(13) NOT NULL,
  PRIMARY KEY (`SCHED_NAME`,`INSTANCE_NAME`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE `QRTZ_SIMPLE_TRIGGERS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `TRIGGER_NAME` varchar(200) NOT NULL,
  `TRIGGER_GROUP` varchar(200) NOT NULL,
  `REPEAT_COUNT` bigint(7) NOT NULL,
  `REPEAT_INTERVAL` bigint(12) NOT NULL,
  `TIMES_TRIGGERED` bigint(10) NOT NULL,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`) USING BTREE,
  CONSTRAINT `QRTZ_SIMPLE_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE `QRTZ_SIMPROP_TRIGGERS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `TRIGGER_NAME` varchar(200) NOT NULL,
  `TRIGGER_GROUP` varchar(200) NOT NULL,
  `STR_PROP_1` varchar(512) DEFAULT NULL,
  `STR_PROP_2` varchar(512) DEFAULT NULL,
  `STR_PROP_3` varchar(512) DEFAULT NULL,
  `INT_PROP_1` int(11) DEFAULT NULL,
  `INT_PROP_2` int(11) DEFAULT NULL,
  `LONG_PROP_1` bigint(20) DEFAULT NULL,
  `LONG_PROP_2` bigint(20) DEFAULT NULL,
  `DEC_PROP_1` decimal(13,4) DEFAULT NULL,
  `DEC_PROP_2` decimal(13,4) DEFAULT NULL,
  `BOOL_PROP_1` varchar(1) DEFAULT NULL,
  `BOOL_PROP_2` varchar(1) DEFAULT NULL,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`) USING BTREE,
  CONSTRAINT `QRTZ_SIMPROP_TRIGGERS_ibfk_1` FOREIGN KEY (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`) REFERENCES `QRTZ_TRIGGERS` (`SCHED_NAME`, `TRIGGER_NAME`, `TRIGGER_GROUP`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;


CREATE TABLE `QRTZ_TRIGGERS` (
  `SCHED_NAME` varchar(120) NOT NULL,
  `TRIGGER_NAME` varchar(200) NOT NULL,
  `TRIGGER_GROUP` varchar(200) NOT NULL,
  `JOB_NAME` varchar(200) NOT NULL,
  `JOB_GROUP` varchar(200) NOT NULL,
  `DESCRIPTION` varchar(250) DEFAULT NULL,
  `NEXT_FIRE_TIME` bigint(13) DEFAULT NULL,
  `PREV_FIRE_TIME` bigint(13) DEFAULT NULL,
  `PRIORITY` int(11) DEFAULT NULL,
  `TRIGGER_STATE` varchar(16) NOT NULL,
  `TRIGGER_TYPE` varchar(8) NOT NULL,
  `START_TIME` bigint(13) NOT NULL,
  `END_TIME` bigint(13) DEFAULT NULL,
  `CALENDAR_NAME` varchar(200) DEFAULT NULL,
  `MISFIRE_INSTR` smallint(2) DEFAULT NULL,
  `JOB_DATA` blob,
  PRIMARY KEY (`SCHED_NAME`,`TRIGGER_NAME`,`TRIGGER_GROUP`) USING BTREE,
  KEY `SCHED_NAME` (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`) USING BTREE,
  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=utf8 ROW_FORMAT=DYNAMIC;



三、property文件

# 固定前缀org.quartz
# 主要分为scheduler、threadPool、jobStore、plugin等部分
#
#
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false

# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数
org.quartz.threadPool.threadCount = 5
# 优先级
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true

org.quartz.jobStore.misfireThreshold = 5000

# 默认存储在内存中
#org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

#持久化
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX

org.quartz.jobStore.tablePrefix = QRTZ_

org.quartz.jobStore.dataSource = qzDS

org.quartz.dataSource.qzDS.driver = com.mysql.jdbc.Driver

org.quartz.dataSource.qzDS.maxConnections = 10

#221环境
#org.quartz.dataSource.qzDS.URL = jdbc:mysql://127.0.0.1:3306/xxxx?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&autoReconnect=true
#org.quartz.dataSource.qzDS.user = root
#org.quartz.dataSource.qzDS.password = xxxxxx

#58环境
org.quartz.dataSource.qzDS.URL = jdbc:mysql://192.168.1.00:3306/xxxxx?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
org.quartz.dataSource.qzDS.user = root
org.quartz.dataSource.qzDS.password = xxxxxx

四、ScheduleConfig.java配置文件

package com.superkind.common.config.Schedule;

import com.superkind.common.utils.schedule.ScheduleUtil;
import com.superkind.modules.task.web.DynamicTask;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

@Configuration
@EnableAsync
@EnableScheduling
public class ScheduleConfig {

    @Bean
    public SchedulerFactoryBean scheduler() {
        return new SchedulerFactoryBean();
    }

    @Bean
    public ScheduleUtil scheduleUtil() {
        return new ScheduleUtil();
    }

    @Bean
    public DynamicTask dynamicTask() {
        return new DynamicTask();
    }
}



五、定时管理的三层结构 dao、service、controller
1、实体类

package com.superkind.modules.job.model.entity;

import com.baomidou.mybatisplus.activerecord.Model;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;

import java.io.Serializable;
import java.util.Date;

/**
 * <p>
 * 
 * </p>
 *
 * @author superkind
 * @since 2018-11-19
 */
@TableName("sys_quartz_sys_data")
public class QuartzSysData extends Model<QuartzSysData> {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    
    @TableField("key_code")
    private String keyCode;
    
    @TableField("job_class_Name")
    private String jobClassName;
    
    @TableField("job_group_name")
    private String jobGroupName;
    
    @TableField("execution_time")
    private Date executionTime;
    
    @TableField("job_data")
    private String jobData;
    
    @TableField("del_flag")
    private String delFlag;
    
    @TableField("edit_flag")
    private String editFlag;
    
    @TableField("create_time")
    private Date createTime;
    
    @TableField("update_time")
    private Date updateTime;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getKeyCode() {
        return keyCode;
    }

    public void setKeyCode(String keyCode) {
        this.keyCode = keyCode;
    }

    public String getJobClassName() {
        return jobClassName;
    }

    public void setJobClassName(String jobClassName) {
        this.jobClassName = jobClassName;
    }

    public Date getExecutionTime() {
        return executionTime;
    }

    public void setExecutionTime(Date executionTime) {
        this.executionTime = executionTime;
    }

    public String getJobData() {
        return jobData;
    }

    public void setJobData(String jobData) {
        this.jobData = jobData;
    }

    public String getDelFlag() {
        return delFlag;
    }

    public void setDelFlag(String delFlag) {
        this.delFlag = delFlag;
    }

    public String getEditFlag() {
        return editFlag;
    }

    public void setEditFlag(String editFlag) {
        this.editFlag = editFlag;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    @Override
    protected Serializable pkVal() {
        return this.id;
    }

    public String getJobGroupName() {
        return jobGroupName;
    }

    public void setJobGroupName(String jobGroupName) {
        this.jobGroupName = jobGroupName;
    }
 
}




2、任务保留类 QuartzInsertJob.java

package com.superkind.modules.job.task;


import com.superkind.common.tools.DateCalcTool;
import com.superkind.modules.job.model.entity.QuartzSysData;
import com.superkind.modules.job.service.IQuartzSysDataService;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.UUID;


/**
 * 这个类是程序quartz任务保留类
 * 每晚固定时间执行
 * 目的在于将cy_quartz_sys_data表中今天需要执行的任务
 * 放到持久化调度系统中
 * Created by Dell on 2017/9/12.
 */
@Component
public class QuartzInsertJob implements BaseJob
{ 
    
    @Autowired @Qualifier("Scheduler")
    private Scheduler scheduler;
    
    @Autowired
    private IQuartzSysDataService quartzSysDataService;
    
    private static Logger _log = LoggerFactory.getLogger(QuartzInsertJob.class);

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {  
            _log.info("创建定时:");
            List<QuartzSysData> quartzList = this.quartzSysDataService.selectQuartzList();
            try {   
                // 启动调度器  
                scheduler.start(); 
                
                for (int i = 0 ; i < quartzList.size() ; i++) {
                    String uuid = UUID.randomUUID().toString();
                    JobDetail jobDetail  = JobBuilder.newJob(getClass(quartzList.get(i).getJobClassName()).getClass()).withIdentity(quartzList.get(i).getJobClassName(), uuid).build();
                    CronScheduleBuilder scheduleBuilder  = CronScheduleBuilder.cronSchedule(DateCalcTool.getCronExpression(quartzList.get(i).getExecutionTime()));
                    CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(quartzList.get(i).getJobClassName(), uuid).withSchedule(scheduleBuilder).build();
                    jobDetail.getJobDataMap().put("JobData",quartzList.get(i).getJobData());
                    scheduler.scheduleJob(jobDetail, trigger);
                    
                    quartzList.get(i).setJobGroupName(uuid);
                    quartzList.get(i).setUpdateTime(new Date());
                    quartzList.get(i).setDelFlag("1");
                }
                boolean result = false;
                if (quartzList != null && quartzList.size() > 0) {
                    result = this.quartzSysDataService.updateBatchById(quartzList);
                }
                _log.info("修改结果:" + result);
            } catch (SchedulerException e) {
                _log.error("Err Task,创建定时任务失败:" + e);
               
            } catch(Exception e2) {
                e2.printStackTrace();
            }
    }

    
    public static BaseJob getClass(String classname) throws Exception
    {
        Class<?> class1 = Class.forName(classname);
        return (BaseJob)class1.newInstance();
    }
    

}



3、顶层接口 BaseJob

package com.superkind.modules.job.task;


import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public interface BaseJob extends Job{
    public void execute(JobExecutionContext context) throws JobExecutionException;
}


4、service 及 impl

package com.superkind.modules.job.service;

import com.baomidou.mybatisplus.service.IService;
import com.superkind.modules.job.model.entity.QuartzSysData;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author 
 * @since 2018-11-19
 */
public interface IQuartzSysDataService extends IService<QuartzSysData> {
    
    
    /**
     * 每次清除所有的定时任务后,执行该方法添加必要的执行方法
     */
    public Integer addNecessaryTask(String jobClassName, String jobGroupName, String cronExpression);
    
    /**
     * 查询明天凌晨之前需要执行的任务
     * 如果返回-1则为出错
     * @return
     */
    public List<QuartzSysData> selectQuartzList();
    
    
    /**
     * 更改为删除状态
     * 如果返回-1则为出错
     * @return
     */
    public Integer updateQuartzListStatus();
    
    /**
     * 增加定时任务
     * @param jobClassName
     * @param executionTime
     * @param params
     * @param keyCode
     * 如果返回-1则为出错
     * @return
     */
    public Integer addQuartzTask(String jobClassName, String executionTime, Map<String, Object> params, String keyCode);


    /**
     * 更新任务
     * 如果返回-1则为出错
     * @param quartzTaskId
     * @param executionTime
     * @param jobClassName
     * @param params
     * @return
     */
    public Integer updagteQuartzLongerTime(Integer quartzTaskId, String executionTime, String jobClassName, Map<String, Object> params);
    
    /**
     * 删除定时任务
     * 如果返回-1则为出错
     * @param quartzTaskId quartz任务记录id
     * @return
     */
    public Integer deleteQuartzTask(Integer quartzTaskId);

}


package com.superkind.modules.job.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.superkind.common.constant.CommonConstant;
import com.superkind.common.tools.DateCalcTool;
import com.superkind.modules.job.mapper.QuartzSysDataMapper;
import com.superkind.modules.job.model.entity.QuartzSysData;
import com.superkind.modules.job.service.IQuartzSysDataService;
import com.superkind.modules.job.task.BaseJob;
import com.superkind.modules.message.mapper.SysMessageMapper;
import com.superkind.modules.message.model.entity.SysMessage;
import com.superkind.modules.user.mapper.UserMapper;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 
 * @since 2018-11-19
 */
@Service
public class QuartzSysDataServiceImpl extends ServiceImpl<QuartzSysDataMapper, QuartzSysData> implements IQuartzSysDataService {

    @Autowired @Qualifier("Scheduler")
    private Scheduler scheduler;

    @Resource
    private SysMessageMapper sysMessageMapper;

    @Resource
    private UserMapper userMapper;


    @Override
    public List<QuartzSysData> selectQuartzList() {
        Map<String,Object> params = new HashMap<String,Object>();
        params.put("time", DateCalcTool.getTommorrow());
        List<QuartzSysData> list = this.baseMapper.selectQuartzList(params);
        return list;
    }

    @Override
    public Integer updateQuartzListStatus() {
        Map<String,Object> params = new HashMap<String,Object>();
        params.put("time", DateCalcTool.getTommorrow());
        return this.baseMapper.updateQuartzListStatus(params);
    }

    @Override
    public Integer addQuartzTask(String jobClassName, String executionTime, Map<String, Object> params,String keyCode) {
        SimpleDateFormat dateFormate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Integer quartzId = -1;
        try {
            Date exeTime = null;

            exeTime = dateFormate.parse(executionTime);

            // Map转json
            String jsonString = JSON.toJSONString(params);

            //定时类
            QuartzSysData quartzTask = new QuartzSysData();

            //消息类
            SysMessage message = new SysMessage();

            if (checkToday(exeTime)) {
                //消息已发送
                //message.setIsSend(CommonConstant.IS_SEND_SUCCESS);

                String jobGroupName = UUID.randomUUID().toString();
                quartzTask.setJobClassName(jobClassName);
                quartzTask.setJobGroupName(jobGroupName);
                quartzTask.setJobData(jsonString);
                quartzTask.setKeyCode(keyCode);
                quartzTask.setExecutionTime(exeTime);
                quartzTask.setCreateTime(new Date());
                quartzTask.setUpdateTime(new Date());
                quartzTask.setDelFlag(CommonConstant.STATUS_DEL.toString());
                quartzTask.setEditFlag(CommonConstant.EDIT_ALLOW);
                this.addJob(jobClassName, jobGroupName, DateCalcTool.getCronExpression(exeTime), jsonString);

            } else {
                //消息未发送
                //message.setIsSend(CommonConstant.NOT_SEND);

                quartzTask.setJobClassName(jobClassName);
                quartzTask.setJobData(jsonString);
                quartzTask.setKeyCode(keyCode);
                quartzTask.setExecutionTime(exeTime);
                quartzTask.setCreateTime(new Date());
                quartzTask.setDelFlag(CommonConstant.STATUS_NORMAL.toString());
                quartzTask.setEditFlag(CommonConstant.EDIT_ALLOW);

            }
            message.setCreateUserId(Integer.valueOf(params.get("createUserId").toString()));
            message.setMessageContext(params.get("messageContext").toString());
            message.setMessageTitle(params.get("messageTitle").toString());
            message.setReciveUserId(params.get("reciveUserId").toString());
            message.setImage(params.get("image").toString());
            message.setFormId(Integer.valueOf(params.get("formId").toString()));
            message.setUrl(params.get("url").toString());
            message.setSendTime(exeTime);
            message.setCreateTime(new Date());
            message.setType(params.get("type").toString());
            message.setEditFlag(CommonConstant.EDIT_ALLOW);
            sysMessageMapper.addMessage(message);


            this.baseMapper.insert(quartzTask);
            quartzId = quartzTask.getId();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return quartzId;
    }
    

    @Override
    public Integer updagteQuartzLongerTime(Integer quartzTaskId, String executionTime, String jobClassName,
            Map<String, Object> jobData) {
        try {

            QuartzSysData quartzTask = this.baseMapper.selectById(quartzTaskId);
            // 查找任务执行日期是否为今天,如果是今天则任务已进入持久化调度系统.
            if (checkToday(quartzTask.getExecutionTime())) {
                //如果定时任务已进入持久化调度系统,需在持久化系统里面删除后,再添加到cy_quartz_sys_data表
                jobdelete(quartzTask.getJobClassName(), quartzTask.getJobGroupName());
                Integer newQuartzTaskId = this.addQuartzTask(jobClassName, executionTime, jobData, quartzTask.getKeyCode());
                return newQuartzTaskId;
            } else {
                //如果定时任务还记录在cy_quartz_sys_data表中未执行,修改执行时间执行类即可
                // Map转json
                String jsonString = JSON.toJSONString(jobData);
                SimpleDateFormat dateFormate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date exeTime = dateFormate.parse(executionTime);
                quartzTask.setJobClassName(jobClassName);
                quartzTask.setJobData(jsonString);
                quartzTask.setExecutionTime(exeTime);
                quartzTask.setUpdateTime(new Date());
                this.baseMapper.updateById(quartzTask);
                return quartzTask.getId();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }
    
    @Override
    public Integer deleteQuartzTask(Integer quartzTaskId) {
        QuartzSysData quartzTask = this.baseMapper.selectById(quartzTaskId);
        try {
            //查找任务执行日期是否为今天,如果是今天则任务已进入持久化调度系统.
            if (checkToday(quartzTask.getExecutionTime())) {
                // 如果定时任务已记录在持久化quartz表里面,需在持久化quartz里面删除
                jobdelete(quartzTask.getJobClassName(), quartzTask.getJobGroupName());
            }else {
                quartzTask.setDelFlag("1");
                quartzTask.setUpdateTime(new Date());
                this.baseMapper.updateById(quartzTask);
            }
            return quartzTask.getId();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return -1;
    }
    
    /**
     * 查找是否是今天凌晨之后的时间
     * @param exeTime
     * @return
     */
    public static boolean checkToday(Date exeTime) {
        boolean result = false;
        try {
            SimpleDateFormat todayFormate = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
            Date today = todayFormate.parse(todayFormate.format(new Date()));
            Date tommorrow = DateCalcTool.getTommorrowDate();
            //result = exeTime.before(DateCalcTool.getTommorrowDate())&&exeTime.after(today);
            result = exeTime.getTime() <= tommorrow.getTime() && exeTime.getTime() > today.getTime();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    
    public void jobdelete(String jobClassName, String jobGroupName) throws Exception
    {       
        scheduler.pauseTrigger(TriggerKey.triggerKey(jobClassName, jobGroupName));
        scheduler.unscheduleJob(TriggerKey.triggerKey(jobClassName, jobGroupName));
        scheduler.deleteJob(JobKey.jobKey(jobClassName, jobGroupName));             
    }
    
    public void addJob(String jobClassName, String jobGroupName, String cronExpression,String jobData)throws Exception{
        // 启动调度器
        scheduler.start();

        // 构建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(jobClassName).getClass())
                .withIdentity(jobClassName, jobGroupName).build();

        // 表达式调度构建器(即任务执行的时间)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);

        // 按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobClassName, jobGroupName)
                .withSchedule(scheduleBuilder).build();
        if(jobData!=null){
            jobDetail.getJobDataMap().put("JobData", jobData);
        }

        try {
            scheduler.scheduleJob(jobDetail, trigger);

        } catch (SchedulerException e) {
            System.out.println("创建定时任务失败" + e);
            throw new Exception("创建定时任务失败");
        }
    }
    
    public static BaseJob getClass(String classname) throws Exception 
    {
        Class<?> class1 = Class.forName(classname);
        return (BaseJob)class1.newInstance();
    }

    @Override
    public Integer addNecessaryTask(String jobClassName, String jobGroupName, String cronExpression) {
        try {
            this.addJob(jobClassName, jobGroupName, cronExpression,null);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

}



4、mapper 及 xml

package com.superkind.modules.job.mapper;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.superkind.modules.job.model.entity.QuartzSysData;

import java.util.List;
import java.util.Map;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author 
 * @since 2018-11-19
 */
public interface QuartzSysDataMapper extends BaseMapper<QuartzSysData> {
    
     List<QuartzSysData> selectQuartzList(Map<String, Object> params);

     Integer updateQuartzListStatus(Map<String, Object> params);
    
}


<?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.superkind.modules.job.mapper.QuartzSysDataMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.superkind.modules.job.model.entity.QuartzSysData">
        <id column="id" property="id" />
        <result column="key_code" property="keyCode" />
        <result column="job_class_Name" property="jobClassName" />
        <result column="job_group_name" property="jobGroupName" />
        <result column="execution_time" property="executionTime" />
        <result column="job_data" property="jobData" />
        <result column="del_flag" property="delFlag" />
        <result column="edit_flag" property="editFlag" />
        <result column="create_time" property="createTime" />
        <result column="update_time" property="updateTime" />
    </resultMap>


    <select id="selectQuartzList" resultMap="BaseResultMap" parameterType="java.util.Map">
        select * from sys_quartz_sys_data q
        where 
        q.execution_time &lt;= #{time}
        and del_flag = 0
                
    </select>
    
    <update id="updateQuartzListStatus" parameterType="java.util.Map">
        update
        sys_quartz_sys_data  set del_flag = 1 
        where 
        execution_time &lt;= #{time}
        and del_flag = 0
    </update>
</mapper>

5、任务执行类

package com.superkind.modules.job.task;

import com.alibaba.fastjson.JSONObject;
import com.superkind.common.constant.CommonConstant;
import com.superkind.common.utils.email.EmailUtil;
import com.superkind.modules.message.mapper.SysMessageMapper;
import com.superkind.modules.user.mapper.UserMapper;
import io.swagger.annotations.ApiModel;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @Author: yokipang
 * @Date: 2020/7/15
 */
@ApiModel(value = "定时发送消息")
@Component
public class MessageJob implements BaseJob {

    @Resource
    private UserMapper userMapper;


    @Autowired
    EmailUtil emailUtil;

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        try{
            System.out.println("------------- MessageJob -------------");
            JobDataMap dataMap = context.getJobDetail().getJobDataMap();
            String strData = dataMap.getString("JobData");
            JSONObject jsonObject = JSONObject.parseObject(strData);
            //json对象转Map
            Map<String,Object> map = (Map<String,Object>)jsonObject;

            String type = map.get("type").toString();
            String reciveUserId = map.get("reciveUserId").toString();
            String messageTitle = map.get("messageTitle").toString();
            String messageContext = map.get("messageContext").toString();


            //用户IDList
            List<String> userIdList = new ArrayList<>();

            //用户邮箱
            List<String> emailList = null;

            if(type.equals("0")){//发送邮件
                if(reciveUserId==null && reciveUserId.equals("")){
                    throw new RuntimeException("接收人ID为空!");
                }else if(reciveUserId.contains(",")){//发送给部分人
                    userIdList = Arrays.asList(reciveUserId.split(","));
                }else if(!reciveUserId.equals("0") && reciveUserId!="0"){ //发送单个用户
                    userIdList.add(reciveUserId);
                }

                //根据用户ID查询出邮箱List 为null则查出所有
                emailList = userMapper.selectUserEmailByIdList(userIdList);
                if(emailList==null || emailList.size()==0){
                    throw new RuntimeException("没有要发送的邮箱号!");
                }

                String[] emailArray = emailList.toArray(new String[0]);

                emailUtil.sendHtmlMail(emailArray,messageTitle,messageContext);
                //message.setIsSend(CommonConstant.IS_SEND_SUCCESS);

            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}


所有代码先上着,流程下次搞

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350