1. 程式人生 > >訂單超時自動取消,延時任務

訂單超時自動取消,延時任務

應用場景,電商專案使用者下單後超過指定時間未支付,訂單自動失效。

使用延時佇列會使用到定時任務,需要先把定時任務做好。

在使用者下單成功後。定時任務定時掃描出下單成功且未支付的訂單,將訂單加入到延時執行佇列中。同時也加入到快取中。

延時執行類在執行訂單失效時,先到快取內查詢一次,如果沒有查詢到,說明該訂單已支付或者已取消(支付成功或取消訂單清除對應快取)

一;執行實體

package io.jboot.admin.job;

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

import com.hnzh.wmall.service.entity.IndentCommodity;


public class TaskEntity implements Delayed{
    
    private int id;  
    private String body;  //訊息內容
    private IndentCommodity indentCommodity;
    private long excuteTime;//執行時間    
    
 
    public String getBody() {
        return body;
    }
 
    public void setBody(String body) {
        this.body = body;
    }
 
    public long getExcuteTime() {
        return excuteTime;
    }
 
    public void setExcuteTime(long excuteTime) {
        this.excuteTime = excuteTime;
    }
 
    public TaskEntity(int id, String body,long delayTime) {  
        this.id = id;  
        this.body = body;  
        this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();  
    } 
 
    public TaskEntity(int id, IndentCommodity indentCommodity,long delayTime) {  
        this.id = id;  
        this.indentCommodity = indentCommodity;  
        this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();  
    }
    
    public TaskEntity(int id, long delayTime) {
        this.id = id;  
        this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();  
    }

    @Override
    public int compareTo(Delayed delayed) {
        TaskEntity msg = (TaskEntity)delayed;  
        return Integer.valueOf(this.id)>Integer.valueOf(msg.id)?1:( Integer.valueOf(this.id)<Integer.valueOf(msg.id)?-1:0);  
    }
 
    @Override
    public long getDelay(TimeUnit unit) {
        return  unit.convert(this.excuteTime - System.nanoTime(), TimeUnit.NANOSECONDS);   
    }

    public int getId() {
        return id;
    }

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

    public IndentCommodity getIndentCommodity() {
        return indentCommodity;
    }

    public void setIndentCommodity(IndentCommodity indentCommodity) {
        this.indentCommodity = indentCommodity;
    }
 
}

二;延時執行類

package io.jboot.admin.job;

import java.sql.SQLException;
import java.util.concurrent.DelayQueue;

import com.hnzh.wmall.service.api.CommodityDetailsService;
import com.hnzh.wmall.service.api.IndentCommodityService;
import com.hnzh.wmall.service.api.IndentService;
import com.hnzh.wmall.service.entity.CommodityDetails;
import com.hnzh.wmall.service.entity.Indent;
import com.hnzh.wmall.service.entity.IndentCommodity;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;

import io.jboot.Jboot;
import io.jboot.admin.base.common.ServiceConst;
import io.jboot.core.rpc.Jbootrpc;
import io.jboot.core.rpc.JbootrpcServiceConfig;


public class ExecuteClass implements Runnable {
    protected static final Log logger = Log.getLog(ExecuteClass.class);
    // 延時佇列
    private DelayQueue<TaskEntity> queue;
 
    public ExecuteClass(DelayQueue<TaskEntity> queue) {
        this.queue = queue;
    }
 
    @Override
    public void run() {
        //獲取rpc服務
        Jbootrpc jbootrpc = Jboot.me().getRpc();
        JbootrpcServiceConfig serviceConfig = new JbootrpcServiceConfig();
        serviceConfig.setGroup(ServiceConst.SERVICE_WMALL);
        
        CommodityDetailsService detailsService = jbootrpc.serviceObtain(CommodityDetailsService.class, serviceConfig);
        IndentService IndentService = jbootrpc.serviceObtain(IndentService.class, serviceConfig);
        IndentCommodityService indentCommodityService = jbootrpc.serviceObtain(IndentCommodityService.class,
                serviceConfig);
        
        while (true) {
            try {
                //檢測該訂單是否已買單  若已買單對該訂單不做處理,進入下次迴圈
                IndentCommodity indentCommodity  = Jboot.me().getCache().get("DelayExecuteClass", "executeClass"+queue.take().getIndentCommodity().getIndentNumber());
                //如果快取中沒取到訂單物件,表示該訂單已支付,或已取消(支付成功時在快取中清除對應訂單)
                if(indentCommodity == null){
                    continue;
                }
                TaskEntity take = queue.take();

                //業務開始  訂單狀態開始執行更新
                
                //更新完畢後從快取中清除該訂單號
                Jboot.me().getCache().remove("DelayExecuteClass", "executeClass"+take.getIndentCommodity().getIndentNumber());
                logger.debug("訂單號為>>>"+commodity.getIndentNumber() +"的訂單過期狀態更新完畢,cache內已清除");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

三;在定時任務執行類中,將所有需要加入到延時執行佇列中的訂單加入進去。注:定時任務下次更出來吧


    // 初始化訂單延時佇列計數器
    private static int counter = 0;
/**
     * 訂單超時定時任務,若超時,則將該訂單資料更新
     * 
     * @param minutes
     * @return
     */
    public synchronized String indentTimeout(String minutes) {
        //獲取rpc服務
        Jbootrpc jbootrpc = Jboot.me().getRpc();
        JbootrpcServiceConfig serviceConfig = new JbootrpcServiceConfig();
        serviceConfig.setGroup(ServiceConst.SERVICE_WMALL);
        
        IndentCommodityService indentCommodityService = jbootrpc.serviceObtain(IndentCommodityService.class,
                serviceConfig);
        // 查詢出所有未支付訂單
        List<IndentCommodity> commodities = indentCommodityService.findIndentCommodity(null);
        for (int i = 0; i < commodities.size(); i++) {
            counter++;
            // 延時執行時間 轉為毫秒
            long time = Integer.parseInt(minutes) * 60 * 1000;
            long delayTime = time - (new Date().getTime() - commodities.get(i).getCreateTime().getTime());
            TaskEntity te = null;
            if (delayTime < 0) {
                logger.debug("超過" + minutes + "分鐘未加入到佇列,訂單號>>>" + commodities.get(i).getIndentNumber() + ",立即加入佇列");
                te = new TaskEntity(counter, commodities.get(i), 0);
            } else {
                te = new TaskEntity(counter, commodities.get(i), delayTime);
                // 檢測該訂單物件是否在延時佇列中,若存在不做處理
                IndentCommodity indentCommodity = Jboot.me().getCache().get("DelayExecuteClass",
                        "executeClass" + commodities.get(i).getIndentNumber());
                if (indentCommodity != null) {
                    continue;
                }
            }
            // 將需處理的訂單物件加入到快取內
            Jboot.me().getCache().put("DelayExecuteClass", "executeClass" + commodities.get(i).getIndentNumber(),
                    commodities.get(i));
            // 將需處理的訂單物件加入到延時佇列中
            queue.offer(te);
        }
        return "success";
    }