1. 程式人生 > >7天自動收貨,30分鐘不支付訂單自動取消是如何實現的?

7天自動收貨,30分鐘不支付訂單自動取消是如何實現的?

1.我們以支付以後7天自動收貨為例來說明下:

(1)使用者支付完成以後,把訂單ID插入到記憶體的一個DelayQueue中,同時插入到Redis中。

(2)7天之內,使用者點選了確認收貨,則從DelayQueue中刪除,從Redis中刪除。

(3)超過7天,DelayQueue中的訂單ID出隊,查詢資料庫,改狀態為自動收貨,刪除redis。

(4)如果7天之內,web伺服器重啟過,則web伺服器啟動以後,從redis中讀取待收貨的訂單,插入到DelayQueue。

看下具體的程式碼:

@Controller
@RequestMapping(value = "")
public class OrderController {
	@Autowired
	DelayService delayService;
	@Autowired
	RedisService redisServie;
	@Autowired
	ConfigService configService;
	//模擬資料庫
	private List<Long> ordeIds = new ArrayList<Long>();
	private static final Logger log = Logger.getLogger(OrderController.class);
	@RequestMapping(value = "/order", method = RequestMethod.GET)
	public String order(final HttpServletRequest request, final Model model) {
		return "order";
	}
	@RequestMapping(value = "/pay", method = RequestMethod.GET)
	@ResponseBody
	public Response<Void> pay(final HttpServletRequest request, final Model model) {
		final long orderId = Long.parseLong(request.getParameter("orderId"));
		ordeIds.add(orderId);
		log.error("訂單已支付:"+orderId);
		//把訂單插入到待收貨的佇列和redis
		ThreadPoolUtil.execute(new Runnable(){
			@Override
			public void run() {
				//1 插入到待收貨佇列
				DSHOrder dshOrder = new DSHOrder(orderId, configService.getDshTimeOut());
				delayService.add(dshOrder);
				log.error("訂單入隊:"+orderId);
				//2插入到redis
				redisServie.set(Constants.RedisKey.DSH_PREFIX+orderId, dshOrder, RedisService.DB.DSH);
				log.error("訂單入redis:"+orderId);
			}
		});
		return new Response<Void>(0,"成功");
	}
	@RequestMapping(value = "/confirm_delivery", method = RequestMethod.GET)
	@ResponseBody
	public Response<Void> confirm_delivery(final HttpServletRequest request, final Model model) {
		final long orderId = Long.parseLong(request.getParameter("orderId"));
		ordeIds.remove(orderId);
		log.error("訂單已確認收貨:"+orderId);
		//從delay佇列刪除,從redis刪除
		ThreadPoolUtil.execute(new Runnable(){
			public void run(){
				//從delay佇列刪除
				delayService.remove(orderId);
				log.error("訂單手動出隊:"+orderId);
				//從redis刪除
				redisServie.delete(Constants.RedisKey.DSH_PREFIX+orderId, RedisService.DB.DSH);
				log.error("訂單手動出redis:"+orderId);
			}
		});
		return new Response<Void>(0,"成功");
	}
}

@Service
public class DelayService {
	
	private static final Logger log = Logger.getLogger(DelayService.class);
	
	@Autowired
	ConfigService configService;
	
	private boolean start ;  
	private OnDelayedListener listener;
	private DelayQueue<DSHOrder> delayQueue = new DelayQueue<DSHOrder>();
	
	public static interface OnDelayedListener{
		public void onDelayedArrived(DSHOrder order);
	}

	public void start(OnDelayedListener listener){
		if(start){
			return;
		}
		log.error("DelayService 啟動");
		start = true;
		this.listener = listener;
		new Thread(new Runnable(){
			public void run(){
				try{
					while(true){
						DSHOrder order = delayQueue.take();
						if(DelayService.this.listener != null){
							DelayService.this.listener.onDelayedArrived(order);
						}
					}
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		}).start();;
	}
	
	public void add(DSHOrder order){
		delayQueue.put(order);
	}

	public boolean remove(DSHOrder order){
		return delayQueue.remove(order);
	}
	
	public void add(long orderId){
		delayQueue.put(new DSHOrder(orderId, configService.getDshTimeOut()));
	}
	
	public void remove(long orderId){
		DSHOrder[] array = delayQueue.toArray(new DSHOrder[]{});
		if(array == null || array.length <= 0){
			return;
		}
		DSHOrder target = null;
		for(DSHOrder order : array){
			if(order.getOrderId() == orderId){
				target = order;
				break;
			}
		}
		if(target != null){
			delayQueue.remove(target);
		}
	}
}

public class DSHOrder implements Delayed {
	
	private long orderId;
	private long startTime;
	
	public DSHOrder(){
		
	}
	
	/**
	 * orderId:訂單id
	 * timeout:自動收貨的超時時間,秒
	 * */
	public DSHOrder(long orderId, int timeout){
		this.orderId = orderId;
		this.startTime = System.currentTimeMillis() + timeout*1000L;
	}
	@Override
	public int compareTo(Delayed other) {
		if (other == this){
			return 0;
		}
		if(other instanceof DSHOrder){
			DSHOrder otherRequest = (DSHOrder)other;
			long otherStartTime = otherRequest.getStartTime();
			return (int)(this.startTime - otherStartTime);
		}
		return 0;
	}

	@Override
	public long getDelay(TimeUnit unit) {
		return unit.convert(startTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (orderId ^ (orderId >>> 32));
		result = prime * result + (int) (startTime ^ (startTime >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		DSHOrder other = (DSHOrder) obj;
		if (orderId != other.orderId)
			return false;
		if (startTime != other.startTime)
			return false;
		return true;
	}

	public long getStartTime() {
		return startTime;
	}

	public long getOrderId() {
		return orderId;
	}

	public void setOrderId(long orderId) {
		this.orderId = orderId;
	}

	public void setStartTime(long startTime) {
		this.startTime = startTime;
	}

	@Override
	public String toString() {
		return "DSHOrder [orderId=" + orderId + ", startTime=" + startTime + "]";
	}
}

@Service
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
	
	private static final Logger log = Logger.getLogger(StartupListener.class);

	@Autowired
	DelayService delayService;
	@Autowired
	RedisService redisService;
	
	
    @Override
    public void onApplicationEvent(ContextRefreshedEvent evt) {
    	log.error(">>>>>>>>>>>>系統啟動完成,onApplicationEvent()");
        if (evt.getApplicationContext().getParent() == null) {
            return;
        }
        //自動收貨
        delayService.start(new OnDelayedListener(){
			@Override
			public void onDelayedArrived(final DSHOrder order) {
				//非同步來做
				ThreadPoolUtil.execute(new Runnable(){
					public void run(){
						long orderId = order.getOrderId();
						//查庫判斷是否需要自動收貨
						log.error("自動確認收貨,onDelayedArrived():"+orderId);
						//從redis刪除
						redisService.delete(Constants.RedisKey.DSH_PREFIX+orderId, RedisService.DB.DSH);
						log.error("自動確認收貨,刪除redis:"+orderId);
					}
				});
			}
        });
        //查詢需要入隊的訂單
        ThreadPoolUtil.execute(new Runnable(){
			@Override
			public void run() {
				log.error("查詢需要入隊的訂單");
				//掃描redis,找到所有可能的orderId
		        List<String> keys = redisService.scan(RedisService.DB.DSH);
		        if(keys == null || keys.size() <= 0){
		        	return;
		        }
		        log.error("需要入隊的訂單keys:"+keys);
		        //寫到DelayQueue
		        for(String key : keys){
		        	DSHOrder order = redisService.get(key, DSHOrder.class, RedisService.DB.DSH);
		        	log.error("讀redis,key:"+key);
		        	if(order != null){
		        		delayService.add(order);	
		        		log.error("訂單自動入隊:"+order.getOrderId());
		        	}
		        } 
			}
        }); 
    }
}

最新的程式碼:https://github.com/xjs1919/util/tree/master/src/main/java/com/github/xjs/util/delay