Quartz进阶-工具类封装及代码抽取(订单倒计时案列准备工作)

前言

经过上篇Quartz进阶-工具类封装及代码抽取之后我们可以添加定时任务了,但删除定时任务还做不到
所以需要工具类来实现–提供动态添加/删除定时任务的方法
另一个问题就是不支持业务的动态扩展
实现方式一:针对每个业务写一个job(太繁琐)
实现方式二:抽取公共的job

1.准备QuartzUtils工具类【quartz.util】

add:添加方法
modify:修改方法
remove:移除方法

/**
 * Quartz调度管理器
 * 
 */
public class QuartzUtils {
   private static String JOB_GROUP_NAME = "JOB_GROUP_SYSTEM";
   private static String TRIGGER_GROUP_NAME = "TRIGGER_GROUP_SYSTEM";

   /**
    * @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
    * 
    * @param sched
    *            调度器
    * 
    * @param jobName
    *            任务名
    * @param cls
    *            任务
    * @param params
    *            任务参数
    * @param time
    *            时间设置,参考quartz说明文档
    * 
    * @Title: QuartzManager.java
    */
   public static void addJob(Scheduler sched, String jobName, @SuppressWarnings("rawtypes") Class cls, Object params,
         String time) {
      try {
         JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);// 任务名,任务组,任务执行类
         @SuppressWarnings("unchecked")

         JobDataMap jobDataMap = new JobDataMap();
         jobDataMap.put("params", params);
         JobDetail jobDetail = newJob(cls).withIdentity(jobKey).setJobData(jobDataMap).build();
         TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);// 触发器
         System.out.println(time);
         Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();// 触发器时间设定
         sched.scheduleJob(jobDetail, trigger);
         if (!sched.isShutdown()) {
            sched.start();// 启动
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 添加一个定时任务
    * 
    * @param sched
    *            调度器
    * 
    * @param jobName
    *            任务名
    * @param jobGroupName
    *            任务组名
    * @param triggerName
    *            触发器名
    * @param triggerGroupName
    *            触发器组名
    * @param jobClass
    *            任务
    * @param params
    *            任务参数
    * @param time
    *            时间设置,参考quartz说明文档
    * 
    * @Title: QuartzManager.java
    */
   public static void addJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
         String triggerGroupName, @SuppressWarnings("rawtypes") Class jobClass,  Object params, String time) {
      try {
         JobKey jobKey = new JobKey(jobName, jobGroupName);
         JobDataMap jobDataMap = new JobDataMap();
         jobDataMap.put("params", params);
         @SuppressWarnings("unchecked")
         JobDetail jobDetail = newJob(jobClass).withIdentity(jobKey).setJobData(jobDataMap).build();
         // 触发器
         TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
         Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();
         sched.scheduleJob(jobDetail, trigger);
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
    * 
    * @param sched
    *            调度器
    * @param jobName
    * @param time
    * 
    * @Title: QuartzManager.java
    */
   @SuppressWarnings("rawtypes")
   public static void modifyJobTime(Scheduler sched, String jobName, String time) {
      try {
         TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
         CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
         if (trigger == null) {
            return;
         }
         String oldTime = trigger.getCronExpression();
         if (!oldTime.equalsIgnoreCase(time)) {
            JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
            JobDetail jobDetail = sched.getJobDetail(jobKey);
            Class objJobClass = jobDetail.getJobClass();
            Object params = jobDetail.getJobDataMap().get("params");
            removeJob(sched, jobName);
            System.out.println("修改任务:" + jobName);
            addJob(sched, jobName, objJobClass, params,time);
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 修改一个任务的触发时间
    * 
    * @param sched
    *            调度器 *
    * @param sched
    *            调度器
    * @param triggerName
    * @param triggerGroupName
    * @param time
    * 
    * @Title: QuartzManager.java
    */
   public static void modifyJobTime(Scheduler sched, String triggerName, String triggerGroupName, String time) {
      try {
         TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
         CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
         if (trigger == null) {
            return;
         }
         String oldTime = trigger.getCronExpression();
         if (!oldTime.equalsIgnoreCase(time)) {
            // 修改时间
            trigger.getTriggerBuilder().withSchedule(cronSchedule(time));
            // 重启触发器
            sched.resumeTrigger(triggerKey);
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
    * 
    * @param sched
    *            调度器
    * @param jobName
    * 
    * @Title: QuartzManager.java
    */
   public static void removeJob(Scheduler sched, String jobName) {
      try {
         TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
         sched.pauseTrigger(triggerKey);// 停止触发器
         sched.unscheduleJob(triggerKey);// 移除触发器
         JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
         sched.deleteJob(jobKey);// 删除任务
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description: 移除一个任务
    * 
    * @param sched
    *            调度器
    * @param jobName
    * @param jobGroupName
    * @param triggerName
    * @param triggerGroupName
    * 
    * @Title: QuartzManager.java
    */
   public static void removeJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
         String triggerGroupName) {
      try {
         TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
         sched.pauseTrigger(triggerKey);// 停止触发器
         sched.unscheduleJob(triggerKey);// 移除触发器
         JobKey jobKey = new JobKey(jobName, jobGroupName);
         sched.deleteJob(jobKey);// 删除任务
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description:启动所有定时任务
    * 
    * @param sched  调度器
    * 
    * @Title: QuartzManager.java
    */
   public static void startJobs(Scheduler sched) {
      try {
         sched.start();
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }

   /**
    * @Description:关闭所有定时任务
    * 
    * @param sched
    *            调度器
    * 
    */
   public static void shutdownJobs(Scheduler sched) {
      try {
         if (!sched.isShutdown()) {
            sched.shutdown();
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }
}

测试工具类是否正常

//springboot做了自动配置 - 直接注入进来使用即可
    @Autowired
    private SchedulerFactoryBean factoryBean;
    @Test
	public void test1 ()throws Exception{
        System.err.println(factoryBean);

        //1 创建定时任务
        Scheduler sched = factoryBean.getScheduler();   //调度器
        String jobName = "myJob";                       //作业名称
        Class cls = PrintTimeJob.class;                 //作业
//        Object params = null;                           //传null
        Object params = "zs";                           //传字符串
//        Object params = new User(1L,"zs");            //传对象 - 对象需要序列化
        String time = "0/1 * * * * ?";                  //触发时间 - Cron表达式

        QuartzUtils.addJob(sched,jobName,cls,params,time);
        System.out.println("作业已经创建...");
        //2 睡眠一下
        Thread.sleep(10000);

        //3 删除定时任务
        QuartzUtils.removeJob(sched,jobName);
        System.out.println("作业已经删除...");
        //让程序不结束 - 删除就不会执行定时任务了
        Thread.sleep(10000);
    }

业务分析

虽然有了QuartzUtil
但是所有订单中都要写这样一堆代码

Scheduler sched = factoryBean.getScheduler();   //调度器
	        String jobName = "myJob";                       //作业名称
	        Class cls = PrintTimeJob.class;                 //作业
	//        Object params = null;                           //传null
	        Object params = "zs";                           //传字符串 //传对象
	//        Object params = new User(1L,"zs");            //传字符串 //传对象
	        String time = "0/1 * * * * ?";                  //触发时间 - Cron表达式        QuartzUtils.addJob(sched,jobName,cls,params,time);

解决:写一个service将代码封装到里面,然后其他地方注入之后直接调用即可
而且添加的时候都要定义一个类似于PrintTimeJob类这会导致类会越来越多扩展性很差
解决:写一个MainJob,使用类型type区分

2.准备QuartzJobInfo对象【quartz.dto】

将工具类的参数封装,方便进行传递,参数就是测试工具类时必须要的参数
作用:
将QuartzUtils工具类中的add方法的参数进行封装
将Date类型的时间转换成cron表达式
type

@Data
public class QuartzJobInfo implements Serializable {
    //业务类型:超时未支付,超时未确认
    private String type;
    //作业名
    private String jobName;
    //参数
    private Map<String, Object> params;
    //cron表达式
    private String cronj;
    private Date fireDate;
    //将一个Date时间类型转换成一个cron表达式并赋值给当前对象的cronj
    public void setFireDate(Date fireDate) {
        this.fireDate = fireDate;
        String[] cronArr = new String[7];
        for (int i = 0; i < cronArr.length; i++) {
            cronArr[i] = "";
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(fireDate);
        int second = calendar.get(Calendar.SECOND);
        int minute = calendar.get(Calendar.MINUTE);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int month = calendar.get(Calendar.MONTH) + 1;
        int year = calendar.get(Calendar.YEAR);

        cronArr[0] = second + "";
        cronArr[1] = minute + "";
        cronArr[2] = hour + "";

        cronArr[3] = day + "";
        cronArr[4] = month + "";
        cronArr[5] = "?";
        cronArr[6] = year + "";

        String cron = String.join(" ",cronArr).trim();
        this.setCronj(cron);
    }
    //测试
    public static void main(String[] args) {
        QuartzJobInfo quartzJobInfo = new QuartzJobInfo();
        quartzJobInfo.setFireDate(new Date(System.currentTimeMillis()+60*60*1000));
        System.out.println(quartzJobInfo.getCronj());
    }
}

3.准备JobConstants常量类【quartz.constant】 - 拷贝

作用:用来区分不同的业务类型【列:8个 = 4个取消 + 4个自动确认(4个订单)】
超时未支付 - 取消订单【改状态】
超时未确认 - 自动确认【改状态】
/**
 * 作业相关常量
 */
public class JobConstants {
    //超时未支付
    //领养订单超时未支付
    public static final String OVER_TIME_NO_PAY_ADOPT = "over_time_no_pay_adopt";
    //服务订单超时未支付
    public static final String OVER_TIME_NO_PAY_PRODUCT = "over_time_no_pay_product";
    //商品订单超时未支付
    public static final String OVER_TIME_NO_PAY_GOODS = "over_time_no_pay_goods";
    //充值订单超时未支付
    public static final String OVER_TIME_NO_PAY_RECHARGE = "over_time_no_pay_recharge";

    //超时未确认
    public static final String OVER_TIME_NO_CONFIRM_ADOPT = "over_time_no_confirm_adopt";
    public static final String OVER_TIME_NO_CONFIRM_PRODUCT = "over_time_no_confirm_product";
    public static final String OVER_TIME_NO_CONFIRM_GOODS = "over_time_no_confirm_goods";
    public static final String OVER_TIME_NO_CONFIRM_RECHARGE = "over_time_no_confirm_recharge";
}

4.写一个MainJob实现Job接口【quartz.job】

public class MainJob implements Job {
	    @Override
	    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
	        //根据不同的类型 type 区分不同的业务
	    }
	}

至此准备工作完毕

代码结构分析图解

5.编写QuartzServiceImpl

目的:为了简写调用添加和删除定时器的方法

public interface IQuartzUtils {
    /**
     * 添加定时任务-对工具类的add定时任务的方法再次封装
     *
     * @param info
     */
    void add(QuartzJobInfo info);
    /**
     * 移除定时任务
     * @param jobName
     */
    void remove(String jobName);
}
//目的:为了简写调用添加和删除定时器的方法
@Service
public class QuartzServiceImpl implements IQuartzService {
    //springboot做了自动配置 - 直接注入进来使用即可
    @Autowired
    private SchedulerFactoryBean factoryBean;
    @Override
    public void add(QuartzJobInfo info) {
        QuartzUtils.addJob(
                factoryBean.getScheduler(),
                info.getJobName(),
                MainJob.class,
                info,
                info.getCronj());
    }
    @Override
    public void remove(String jobName) {//常量类 + unionPaySn
        QuartzUtils.removeJob(factoryBean.getScheduler(),jobName);
    }
}

下列为项目功能代码块(忽略)

添加定时任务:

1.在生成订单,跳转到支付页面的时候就要添加定时任务【AdoptOrderServiceImpl】

//4.创建订单倒计时 - 15分钟未支付就取消订单 @TODO
    QuartzJobInfo info = new QuartzJobInfo();
    info.setType(JobConstants.OVER_TIME_NO_PAY_ADOPT);
    info.setFireDate(payBill.getLastPayTime());
    info.setJobName(JobConstants.OVER_TIME_NO_PAY_ADOPT + payBill.getUnionPaySn());
    Map<String,Object> map = new HashMap<>();
    map.put("orderId",adoptOrder.getId());
    info.setParams(map);

    quartzService.add(info);
    System.out.println("订单倒计时取消任务【" + info.getJobName() + "】创建成功...");
    return payData;

2.MainJob中使用type区分不同的取消任务【MainJob】

@Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        // jobDataMap.put("params", params);
        // 获取传递的参数:info 就可以强转成info  //添加定时任务最底层就是调用工具类,工具类中添加参数put("params", params);
        QuartzJobInfo info = (QuartzJobInfo)jobExecutionContext.getJobDetail().getJobDataMap().get("params");
        if(info.getType().equals(JobConstants.OVER_TIME_NO_PAY_ADOPT)){ //领养订单超时未支付
            //获取参数 - 修改订单或支付单的状态  - 找到支付单或订单【id,paySn】
            Map<String, Object> params = info.getParams();
            Long orderId = Long.parseLong(params.get("orderId").toString());
            //修改订单状态
            AdoptOrder adoptOrder = adoptOrderMapper.loadById(orderId);
            adoptOrder.setState(-1); //取消订单
            adoptOrderMapper.update(adoptOrder);
            //修改支付单状态
            PayBill payBill = payBillMapper.loadByUnionPaySn(adoptOrder.getPaySn());
            payBill.setState(-1);
            payBillMapper.update(payBill);
        }
    }

3.支付成功之后,删除定时任务【PayController】

//修改订单状态    if(payBill.getBusinessType().equals(PayConstants.BUSINESS_TYPE_ADOPT)){ //修改的是领养订单 - 找到是哪一种订单
        AdoptOrder adoptOrder = adoptOrderService.getById(payBill.getBusinessKey());
        adoptOrder.setState(1);
        adoptOrderService.update(adoptOrder);

        //支付成功之后  取消定时任务
        quartzService.remove(JobConstants.OVER_TIME_NO_PAY_ADOPT + out_trade_no);
        System.out.println("定时任务移除成功...");

    }else if(payBill.getBusinessType().equals(PayConstants.BUSINESS_TYPE_PRODUCT)){//修改服务订单  @TODO
    }

2.MainJob中使用type区分不同的取消任务【MainJob】

public class MainJob implements Job {
	@Autowired
	private AdoptOrderMapper adoptOrderMapper;
	@Autowired
	private PayBillMapper payBillMapper;
	@Override
	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
		//根据不同的类型 type 区分不同的业务
		// jobDataMap.put("params", params);
		// 获取传递的参数:info 就可以强转成info  //添加定时任务最底层就是调用工具类,工具类中添加参数put("params", params);
		QuartzJobInfo info = (QuartzJobInfo)jobExecutionContext.getJobDetail().getJobDataMap().get("params");
		if(info.getType().equals(JobConstants.OVER_TIME_NO_PAY_ADOPT)){ //领养订单超时未支付
			//获取参数 - 修改订单或支付单的状态  - 找到支付单或订单【id,paySn】
			Map<String, Object> params = info.getParams();
			Long orderId = Long.parseLong(params.get("orderId").toString());
			//修改订单状态
			AdoptOrder adoptOrder = adoptOrderMapper.findById(orderId);
			adoptOrder.setState(-1); //取消订单
			adoptOrderMapper.update(adoptOrder);
			//修改支付单状态
			PayBill payBill = payBillMapper.loadByUnionPaySn(adoptOrder.getPaySn());
			payBill.setState(-1);
			payBillMapper.update(payBill);
		}
	}
}

3.支付成功之后,删除定时任务【AlipayController】

//修改订单状态
if(payBill.getBusinessType().equals(PayConstants.BUSINESS_TYPE_ADOPT)){//确认是领养订单,因为其他订单有可能有一样的id值-找到是哪一种订单
	Long adoptOrderId = payBill.getBusinessKey();
    AdoptOrder adoptOrder = adoptOrderService.findById(adoptOrderId);
    	adoptOrder.setState(1);
    //修改订单的状态
	adoptOrderService.update(adoptOrder);
     //支付成功之后 取消定时任务
	quartzService.remove(JobConstants.OVER_TIME_NO_PAY_ADOPT + out_trade_no);
     System.out.println("定时任务移除成功...");
	}else if (payBill.getBusinessType().equals(PayConstants.BUSINESS_TYPE_RECHARGE)){//修改服务订单的状态 @todo
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值