1.超時未支付訂單處理

1.超時未支付訂單處理

1.1 需求分析

超過限定時間並未支付的訂單,我們需要進行超時訂單的處理:先調用微信支付api,查詢該訂單的支付狀態。如果未支付調用關閉訂單的api,並修改訂單狀態爲已關閉,並回滾庫存數。如果該訂單已經支付,則做補償操作(修改訂單狀態和記錄)。

1.2 實現思路

如何獲取超過限定時間的訂單?我們可以使用延遲消息隊列(死信隊列)來實現。所謂延遲消息隊列,就是消息的生產者發送的消息並不會立刻被消費,而是在設定的時間之後纔可以消費。我們可以在訂單創建時發送一個延遲消息,消息爲訂單號,系統會在限定時間之後取出這個消息,然後查詢訂單的支付狀態,根據結果做出相應的處理。

1.3 rabbitmq延遲消息

使用RabbitMQ來實現延遲消息必須先了解RabbitMQ的兩個概念:消息的TTL和死信Exchange,通過這兩者的組合來實現上述需求。

1.3.1 消息的TTL(Time To Live)

消息的TTL就是消息的存活時間。RabbitMQ可以對隊列和消息分別設置TTL。對隊列設置就是隊列沒有消費者連着的保留時間,也可以對每一個單獨的消息做單獨的設置。超過了這個時間,我們認爲這個消息就死了,稱之爲死信。
我們創建一個隊列queue.temp,在Arguments 中添加x-message-ttl 爲5000 (單位是毫秒),那每一個進入這個隊列的消息在5秒後會消失。

1.3.2 死信交換器 Dead Letter Exchanges

一個消息在滿足如下條件下,會進死信交換機,記住這裏是交換機而不是隊列,一個交換機可以對應很多隊列。
(1) 一個消息被Consumer拒收了,並且reject方法的參數裏requeue是false。也就是說不會被再次放在隊列裏,被其他消費者使用。
(2)上面的消息的TTL到了,消息過期了。
(3)隊列的長度限制滿了。排在前面的消息會被丟棄或者扔到死信交換機上。

Dead Letter Exchange其實就是一種普通的exchange,和創建其他exchange沒有兩樣。只是在某一個設置Dead Letter Exchange的隊列中有消息過期了,會自動觸發消息的轉發,發送到Dead LetterExchange中去。

在這裏插入圖片描述

我們現在可以測試一下延遲隊列。
(1)創建死信交換器 exchange.ordertimeout (fanout
(2)創建隊列queue.ordertimeout
(3)建立死信交換器 exchange.ordertimeout 與隊列queue.ordertimeout 之間的綁定
(4)創建隊列queue.ordercreate,Arguments添加
x-message-ttl=10000
x-dead-letter-exchange: exchange.ordertimeout
(5)測試:向queue.ordercreate隊列添加消息,等待10秒後消息從ueue.ordercreate隊列消失。
在這裏插入圖片描述

1.4.1 微信支付-關閉訂單
(1)WxPayController新增方法

/**
* 關閉微信訂單
* @param orderId
* @return
*/
@PutMapping("/close/{orderId}")
public Result closeOrder(@PathVariable String orderId){
	Map map = wxPayService.closeOrder( orderId );
	return new Result( true,StatusCode.OK,"",map );
}

(2)changgou_service_pay的WxPayService新增方法定義

/**
* 關閉訂單
* @param orderId
* @return
*/
Map closeOrder(String orderId);

(3)changgou_service_pay的 WxPayServiceImpl實現該方法

@Override
public Map closeOrder(String orderId) {
	Map map=new HashMap( );
	map.put( "out_trade_no",orderId );
	try {
		return wxPay.closeOrder( map );
	} catch (Exception e) {
		e.printStackTrace();
		return null;
	}
}

(4)changgou_service_pay_api的WxPayFeign新增方法

/**
* 關閉微信訂單
* @param orderId
* @return
*/
@PutMapping("/wxpay/close/{orderId}")
public Result closeOrder(@PathVariable("orderId") String orderId);

1.4.2 微信支付-查詢訂單

(1)WxPayController新增方法

/**
* 查詢微信訂單
* @param orderId
* @return
*/
@GetMapping("/query/{orderId}")
public Result queryOrder(@PathVariable String orderId){
	Map map = wxPayService.queryOrder( orderId );
	return new Result( true,StatusCode.OK,"",map );
}

(2)WxPayFeign新增方法

/**
* 查詢微信訂單
* @param orderId
* @return
*/
@GetMapping("/wxpay/query/{orderId}")
public Result queryOrder(@PathVariable("orderId") String orderId);

1.4.3 訂單關閉邏輯

如果爲未支付,查詢微信訂單
如果確認爲未支付,調用關閉本地訂單( 修改訂單表的訂單狀態、記錄訂單日誌、恢復商品表庫存)和微信訂單的邏輯。
如果爲已支付進行狀態補償。
(1)changgou_service_order新增依賴

<dependency>
	<groupId>com.changgou</groupId>
	<artifactId>changgou_service_pay_api</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>

(2)changgou_service_order的OrderService新增方法定義

/**
* 關閉訂單
* @param orderId
*/
void closeOrder(String orderId);

(3)OrderServiceImpl實現該方法
實現邏輯:
1)根據id查詢訂單信息,判斷訂單是否存在,訂單支付狀態是否爲未支付
2)基於微信查詢訂單支付狀態

2.1)如果爲success,則修改訂單狀態
2.2)如果爲未支付,則修改訂單,新增日誌,恢復庫存,關閉訂單

@Autowired
private WxPayFeign wxPayFeign;
@Override
    @Transactional
    public void closeOrder(String orderId) {
        /**
         * 1.根據訂單id查詢mysql中的訂單信息,判斷訂單是否存在,判斷訂單的支付狀態
         * 2. 基於微信查詢訂單信息(微信)
         * 2.1)如果當前訂單的支付狀態爲已支付,則進行數據補償(mysql)
         * 2.2)如果當前訂單的支付狀態爲未支付,則修改mysql中的訂單信息,新增訂單日誌,恢復商品的庫存,基於微信關閉訂單
         */
        System.out.println("關閉訂單業務開啓:"+orderId);
        Order order = orderMapper.selectByPrimaryKey(orderId);
        if (order == null){
            throw new RuntimeException("訂單不存在!");
        }
        if (!"0".equals(order.getPayStatus())){
            System.out.println("當前訂單不需要關閉");
            return;
        }
        System.out.println("關閉訂單校驗通過:"+orderId);

        //基於微信查詢訂單信息
       Map wxQueryMap = (Map) payFeign.queryOrder(orderId).getData();
       System.out.println("查詢微信支付訂單:"+wxQueryMap);

       //如果訂單的支付狀態爲已支付,進行數據補償(mysql)
        if ("SUCCESS".equals(wxQueryMap.get("trade_state"))){
            this.updatePayStatus(orderId,(String) wxQueryMap.get("transaction_id"));
            System.out.println("完成數據補償");
        }

        //如果訂單的支付狀態爲未支付,則修改mysql中的訂單信息,新增訂單日誌,恢復商品的庫存,基於微信關閉訂單
        if ("NOTPAY".equals(wxQueryMap.get("trade_state"))){
            System.out.println("執行關閉");
            order.setUpdateTime(new Date());
            order.setOrderStatus("4"); //訂單已關閉
            orderMapper.updateByPrimaryKeySelective(order);

            //新增訂單日誌
            OrderLog orderLog = new OrderLog();
            orderLog.setId(idWorker.nextId()+"");
            orderLog.setOperater("system");
            orderLog.setOperateTime(new Date());
            orderLog.setOrderStatus("4");
            orderLog.setOrderId(order.getId());
            orderLogMapper.insert(orderLog);

            //恢復商品的庫存
            OrderItem _orderItem = new OrderItem();
            _orderItem.setOrderId(orderId);
            List<OrderItem> orderItemList = orderItemMapper.select(_orderItem);

            for (OrderItem orderItem : orderItemList) {
                skuFeign.resumeStockNum(orderItem.getSkuId(),orderItem.getNum());
            }

            //基於微信關閉訂單
            payFeign.closeOrder(orderId);

        }

    }

1.4.4 延遲消息處理

從消息隊列queue.ordertimeout 中提取消息
(1)修改OrderServiceImpl的add方法,追加代碼,實現mq發送

rabbitTemplate.convertAndSend( "","queue.ordercreate", orderId);

(2)changgou_service_order新建監聽類

@Component
public class OrderTimeoutListener {
@Autowired
private OrderService orderService;
	/**
	* 更新支付狀態
	* @param orderId
	*/
	@RabbitListener(queues = "queue.ordertimeout")
	public void closeOrder(String orderId){
		System.out.println("接收到關閉訂單消息:"+orderId);
		try {
			orderService.closeOrder( orderId );
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章