自动取消失效订单(监听器 + quartz)

转载:http://www.cnblogs.com/qingxinshujuan/p/5403878.html
CronTrigger最详细的Cron表达式范例参考:http://blog.csdn.net/zixiao217/article/details/53075009
任务需求:

  关闭超时未支付的订单,将订单信息置为失效状态

相关技术:

  quartz框架定时调度

实现思路:

1.在服务启动时,查询数据库中的已下单未支付的订单数据,按下单时间先后存入队列中,先下单的存到头不,后下单存入队列尾部,取队列的头元素
2.检测与现在的时间,如果超过40分钟,则执行数据操作,即关闭订单,但是只关闭未支付的订单,之后在将头元素从队列移出,并取出下一个元素进行检测,以此类推
3.如果检测出时间未到40分钟,则线程等待相应的时间差,之后在执行订单操作
相关问题:

1.在执行时要防止轮询任务追尾,即在上一个job未执行完毕时就开始下一次轮询,解决方法是在job上加@DisallowConcurrentExecution注解,该注解的作用是让下一次job要等待当前job执行完毕
2.设置的轮询间隔是35分钟一次,订单超时是40分钟,中间有5分钟的时间差,为了防止订单被多次加入队列中,在加入订单队列时要注意去重
相关代码

方式1:监听器 + quartz实现;

package com.taotao.order.scheduler;

import org.apache.log4j.Logger;
import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

import com.taotao.order.controller.OrderController;
/**
 * 
* @ClassName: CancelOrderTask 
* @Description: TODO(取消订单执行类) 
* @author 
* @date 2017年9月1日 上午10:58:26 
*
 */
public class CancelOrderTask {

    static Logger logger = Logger.getLogger(OrderController.class);
    /*
     * 1个job配置两个触发器
     */
    public void cancelOrderTask() throws SchedulerException { 
        // 获得一个scheduler
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        Scheduler scheduler = schedulerFactory.getScheduler();
        // 创建一个job 任务名,任务组,任务执行类
        JobDetail jobDetail = JobBuilder.newJob(CancelOrderJob.class)
                .withIdentity("cancelOrderJob", "orderJob") //设定job的name和group
                .storeDurably() //当job没有绑定触发器时放入scheduler不会抛出异常
                .build();
        scheduler.addJob(jobDetail, false);
        //创建一个简单触发器,当启动项目时立即执行,CronTrigger的startNow()方法在设定定时任务后不起作用
        Trigger simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity("firstCancelOrderTrigger", "firstOrderTrigger") //设定触发器name和group
                .startNow() //设定立即执行
                .forJob("cancelOrderJob", "orderJob") //触发器绑定job
                .build();
        //触发器加入到scheduler
        scheduler.scheduleJob(simpleTrigger);

        /*
         *  创建一个CronTrigger触发器,设定触发时间
         *  CronTrigger触发器startNow没有任何作用,因为有自己的触发时间
         */
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity("cancelOrderTrigger", "orderTrigger")
                .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 * * * ?")) //触发时间(轮询时间)
                .forJob("cancelOrderJob", "orderJob").build(); //触发器绑定job
        // 将job和触发器绑定
        scheduler.scheduleJob(cronTrigger);
        String logInfo = jobDetail.getKey() + "取消订单任务定时器启动";
        logger.info(logInfo);
        System.out.println(logInfo);
        scheduler.start();
    }

    public static void main(String[] args) {
        CancelOrderTask cancelOrderTask = new CancelOrderTask();
        try {
            cancelOrderTask.cancelOrderTask();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

}

quartz任务的job,用于检测数据库失效订单并将其关闭,代码如下:

package com.taotao.order.scheduler;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import org.apache.log4j.Logger;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import com.taotao.order.pojo.Order;

/**
 * quartz任务的job,用于检测数据库失效订单并将其关闭
 * @DisallowConcurrentExecution 这个注解标明上个任务没有执行完毕不会执行下个任务
 */
@DisallowConcurrentExecution
public class CancelOrderJob implements Job {
    //订单有效时间40分钟
    public static final long EFFTIVE_TIME = 40 * 60 * 1000;
    private Logger logger = Logger.getLogger(CancelOrderJob.class);

    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        System.out.println("失效订单检测任务开始执行!");
        Queue<Order> queue = new LinkedList<>();

        // 在每次启动Job时去数据库查找失效订单,并加入到队列中(从数据库中查询,此处使用假数据)
        List<Order> list = getInvalidOrder();
        if (!list.isEmpty()) {
            for (Order o : list) {
                queue.offer(o);
            }
        }
        // 获取队列的头元素,开始检测头订单是否失效
        Order element = queue.peek();
        while (element != null) {
            //时间差值
            Long diff = this.checkOrder(element);
            if (diff != null && diff >= EFFTIVE_TIME) {
                System.out.println("开始关闭订单" + element.getOrderId() + "下单时间" + element.getCreateTime());
                // 弹出队列
                queue.poll();
                // 取下一个元素
                element = queue.peek();
            } else if (diff < EFFTIVE_TIME) {
                try {
                    System.out.println("等待检测订单" + element.getOrderId() + "下单时间" + element.getCreateTime() + "已下单"
                            + diff / 1000 + "秒");
                    //线程等待
                    Thread.sleep(EFFTIVE_TIME - diff);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    logger.info("CancelOrderJob.checkOrder定时任务出现问题");
                }
            }
        }
    }

    /**
     * 获取订单的下单时间和现在的时间差
     * 
     * @author wangpeiqing 2016年4月16日
     * @param order
     * @return
     *
     */
    public Long checkOrder(Order order) {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Long diff = null;
        if (order != null) {
            Date createTime = order.getCreateTime();
            try {
                diff = sdf.parse(sdf.format(date)).getTime() - sdf.parse(sdf.format(createTime)).getTime();
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        // 返回值为毫秒
        return diff;
    }

    /**
     * 
     * @Title: getInvalidOrder
     * @Description: TODO(生产订单假数据用于测试)
     * @return List<Order> 返回类型
     * @return
     */
    private List<Order> getInvalidOrder() {
        List<Order> invalidOrders = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            Order order = new Order();
            order.setOrderId(String.valueOf(i));
            order.setCreateTime(new Date());
            invalidOrders.add(order);
        }
        return invalidOrders;
    }

}

监听器类

package com.taotao.order.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.quartz.SchedulerException;

import com.taotao.order.scheduler.CancelOrderTask;

public class CancelOrderListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        CancelOrderTask task = new CancelOrderTask();
        try {
            task.cancelOrderTask();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }

}

配置监听器:web.xml文件中添加监听器,启动程序时执行执行任务

<!--取消订单监听器,启动程序时执行执行任务  -->
    <listener>
        <listener-class>com.taotao.order.listener.CancelOrderListener</listener-class>
    </listener>

订单实体类:

package com.taotao.pojo;

import java.util.Date;

public class TbOrder {
    private String orderId;

    private String payment;

    private Integer paymentType;

    private String postFee;

    private Integer status;

    private Date createTime;

    private Date updateTime;

    private Date paymentTime;

    private Date consignTime;

    private Date endTime;

    private Date closeTime;

    private String shippingName;

    private String shippingCode;

    private Long userId;

    private String buyerMessage;

    private String buyerNick;

    private Integer buyerRate;

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId == null ? null : orderId.trim();
    }

    public String getPayment() {
        return payment;
    }

    public void setPayment(String payment) {
        this.payment = payment == null ? null : payment.trim();
    }

    public Integer getPaymentType() {
        return paymentType;
    }

    public void setPaymentType(Integer paymentType) {
        this.paymentType = paymentType;
    }

    public String getPostFee() {
        return postFee;
    }

    public void setPostFee(String postFee) {
        this.postFee = postFee == null ? null : postFee.trim();
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    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;
    }

    public Date getPaymentTime() {
        return paymentTime;
    }

    public void setPaymentTime(Date paymentTime) {
        this.paymentTime = paymentTime;
    }

    public Date getConsignTime() {
        return consignTime;
    }

    public void setConsignTime(Date consignTime) {
        this.consignTime = consignTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public Date getCloseTime() {
        return closeTime;
    }

    public void setCloseTime(Date closeTime) {
        this.closeTime = closeTime;
    }

    public String getShippingName() {
        return shippingName;
    }

    public void setShippingName(String shippingName) {
        this.shippingName = shippingName == null ? null : shippingName.trim();
    }

    public String getShippingCode() {
        return shippingCode;
    }

    public void setShippingCode(String shippingCode) {
        this.shippingCode = shippingCode == null ? null : shippingCode.trim();
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getBuyerMessage() {
        return buyerMessage;
    }

    public void setBuyerMessage(String buyerMessage) {
        this.buyerMessage = buyerMessage == null ? null : buyerMessage.trim();
    }

    public String getBuyerNick() {
        return buyerNick;
    }

    public void setBuyerNick(String buyerNick) {
        this.buyerNick = buyerNick == null ? null : buyerNick.trim();
    }

    public Integer getBuyerRate() {
        return buyerRate;
    }

    public void setBuyerRate(Integer buyerRate) {
        this.buyerRate = buyerRate;
    }
}
package com.taotao.order.pojo;

import java.util.List;

import com.taotao.pojo.TbOrder;
import com.taotao.pojo.TbOrderItem;
import com.taotao.pojo.TbOrderShipping;

public class Order extends TbOrder{

    private List<TbOrderItem> orderItems;
    private TbOrderShipping orderShipping;
    public List<TbOrderItem> getOrderItems() {
        return orderItems;
    }
    public void setOrderItems(List<TbOrderItem> orderItems) {
        this.orderItems = orderItems;
    }
    public TbOrderShipping getOrderShipping() {
        return orderShipping;
    }
    public void setOrderShipping(TbOrderShipping orderShipping) {
        this.orderShipping = orderShipping;
    }

}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值