利用MQ自動取消未支付超時訂單最佳實踐

一、利用MQ自動取消未支付超時訂單最佳實踐

1、基于 RocketMQ 延遲消息

1.1:延遲消息

當消息寫入到 Broker 后,不會立刻被消費者消費,需要等待指定的時長后才可被消費處理的消息,稱為延時消息。

1.2:實現流程

(1)用戶創建訂單時,發送一個延遲消息到消息隊列,延時時間為訂單的超時時間。
(2)消息到期后,消費者接收到消息,檢查訂單狀態:
如果訂單未支付,則關閉訂單;
如果已支付,則忽略消息。

1.3:優點

高效,解耦,適合高并發場景。
失敗可重試,可靠性高。

1.4:缺點

需要引入消息隊列,增加系統復雜度。

2、RabbitMQ死信隊列

2.1:死信隊列

當 RabbitMQ 中的一條正常消息,因為過了存活時間(TTL 過期)、隊列長度超限、 被消費者拒絕等原因無法被消費時,就會被當成一條死信消息,投遞到死信隊列。

我們可以給消息設置一個 TTL ,然后故意不消費消息,等消息過期就 會進入死信隊列,我們再消費死信隊列即可。

通過這樣的方式,就可以達到同 RocketMQ 延遲消息一樣的效果。

2.2:優點

同 RocketMQ 一樣,RabbitMQ 同樣可以使業務解耦,基于其集群的擴展性, 也可以實現高可用、高性能的目標。

二、RabbitMQ死信隊列實現代碼

1、CancelOrderSender消息的發送者

/*** 取消訂單消息的發送者*/
@Component
public class CancelOrderSender {private static final Logger LOGGER = LoggerFactory.getLogger(CancelOrderSender.class);@Autowiredprivate AmqpTemplate amqpTemplate;public void sendMessage(Long orderId,final long delayTimes){//給延遲隊列發送消息amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {//給消息設置延遲毫秒值message.getMessageProperties().setExpiration(String.valueOf(delayTimes));return message;}});LOGGER.info("send orderId:{}",orderId);}
}

2、CancelOrderReceiver消息的接收者

/*** 取消訂單消息的接收者*/
@Component
@RabbitListener(queues = "mall.order.cancel")
public class CancelOrderReceiver {private static final Logger LOGGER = LoggerFactory.getLogger(CancelOrderReceiver.class);@Autowiredprivate OmsPortalOrderService portalOrderService;@RabbitHandlerpublic void handle(Long orderId){portalOrderService.cancelOrder(orderId);LOGGER.info("process orderId:{}",orderId);}
}

3、QueueEnum消息隊列枚舉類

@Getter
public enum QueueEnum {/*** 消息通知隊列*/QUEUE_ORDER_CANCEL("mall.order.direct", "mall.order.cancel", "mall.order.cancel"),/*** 消息通知ttl隊列*/QUEUE_TTL_ORDER_CANCEL("mall.order.direct.ttl", "mall.order.cancel.ttl", "mall.order.cancel.ttl");/*** 交換名稱*/private final String exchange;/*** 隊列名稱*/private final String name;/*** 路由鍵*/private final String routeKey;QueueEnum(String exchange, String name, String routeKey) {this.exchange = exchange;this.name = name;this.routeKey = routeKey;}
}

4、OmsPortalOrderServiceImpl前臺訂單管理實現

這里核心是在創建訂單后,發送此訂單到死信隊列,用于后續MQ的監聽消費。

@Slf4j
@Service
public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {@Overridepublic Map<String, Object> generateOrder(OrderParam orderParam) {List<OmsOrderItem> orderItemList = new ArrayList<>();//校驗收貨地址if(orderParam.getMemberReceiveAddressId()==null){Asserts.fail("請選擇收貨地址!");}//獲取購物車及優惠信息UmsMember currentMember = memberService.getCurrentMember();List<CartPromotionItem> cartPromotionItemList = cartItemService.listPromotion(currentMember.getId(), orderParam.getCartIds());for (CartPromotionItem cartPromotionItem : cartPromotionItemList) {//生成下單商品信息OmsOrderItem orderItem = new OmsOrderItem();orderItem.setProductId(cartPromotionItem.getProductId());orderItem.setProductQuantity(cartPromotionItem.getQuantity());orderItem.setProductSkuId(cartPromotionItem.getProductSkuId());orderItem.setProductSkuCode(cartPromotionItem.getProductSkuCode());orderItem.setProductCategoryId(cartPromotionItem.getProductCategoryId());orderItem.setPromotionAmount(cartPromotionItem.getReduceAmount());orderItem.setPromotionName(cartPromotionItem.getPromotionMessage());orderItem.setGiftIntegration(cartPromotionItem.getIntegration());orderItem.setGiftGrowth(cartPromotionItem.getGrowth());orderItemList.add(orderItem);}//判斷購物車中商品是否都有庫存if (!hasStock(cartPromotionItemList)) {Asserts.fail("庫存不足,無法下單");}//判斷使用使用了優惠券//計算order_item的實付金額//進行庫存鎖定//根據商品合計、運費、活動優惠、優惠券、積分計算應付金額OmsOrder order = new OmsOrder();order.setDiscountAmount(new BigDecimal(0));order.setTotalAmount(calcTotalAmount(orderItemList));order.setFreightAmount(new BigDecimal(0));order.setPromotionAmount(calcPromotionAmount(orderItemList));order.setPromotionInfo(getOrderPromotionInfo(orderItemList));if (orderParam.getCouponId() == null) {order.setCouponAmount(new BigDecimal(0));} else {order.setCouponId(orderParam.getCouponId());order.setCouponAmount(calcCouponAmount(orderItemList));}if (orderParam.getUseIntegration() == null) {order.setIntegration(0);order.setIntegrationAmount(new BigDecimal(0));} else {order.setIntegration(orderParam.getUseIntegration());order.setIntegrationAmount(calcIntegrationAmount(orderItemList));}order.setPayAmount(calcPayAmount(order));//轉化為訂單信息并插入數據庫order.setMemberId(currentMember.getId());order.setCreateTime(new Date());order.setMemberUsername(currentMember.getUsername());//支付方式:0->未支付;1->支付寶;2->微信order.setPayType(orderParam.getPayType());//訂單來源:0->PC訂單;1->app訂單order.setSourceType(1);//訂單狀態:0->待付款;1->待發貨;2->已發貨;3->已完成;4->已關閉;5->無效訂單order.setStatus(0);//訂單類型:0->正常訂單;1->秒殺訂單order.setOrderType(0);//收貨人信息:姓名、電話、郵編、地址UmsMemberReceiveAddress address = memberReceiveAddressService.getItem(orderParam.getMemberReceiveAddressId());order.setReceiverName(address.getName());order.setReceiverPhone(address.getPhoneNumber());order.setReceiverPostCode(address.getPostCode());order.setReceiverProvince(address.getProvince());order.setReceiverCity(address.getCity());order.setReceiverRegion(address.getRegion());order.setReceiverDetailAddress(address.getDetailAddress());//0->未確認;1->已確認order.setConfirmStatus(0);order.setDeleteStatus(0);//計算贈送積分order.setIntegration(calcGifIntegration(orderItemList));//計算贈送成長值order.setGrowth(calcGiftGrowth(orderItemList));//生成訂單號order.setOrderSn(generateOrderSn(order));//設置自動收貨天數List<OmsOrderSetting> orderSettings = orderSettingMapper.selectByExample(new OmsOrderSettingExample());if(CollUtil.isNotEmpty(orderSettings)){order.setAutoConfirmDay(orderSettings.get(0).getConfirmOvertime());}//插入order表和order_item表orderMapper.insert(order);for (OmsOrderItem orderItem : orderItemList) {orderItem.setOrderId(order.getId());orderItem.setOrderSn(order.getOrderSn());}orderItemDao.insertList(orderItemList);//刪除購物車中的下單商品deleteCartItemList(cartPromotionItemList, currentMember);//發送延遲消息取消訂單sendDelayMessageCancelOrder(order.getId());Map<String, Object> result = new HashMap<>();result.put("order", order);result.put("orderItemList", orderItemList);return result;}
}    

具體的取消實現方法

@Overridepublic void cancelOrder(Long orderId) {//查詢未付款的取消訂單OmsOrderExample example = new OmsOrderExample();example.createCriteria().andIdEqualTo(orderId).andStatusEqualTo(0).andDeleteStatusEqualTo(0);List<OmsOrder> cancelOrderList = orderMapper.selectByExample(example);if (CollectionUtils.isEmpty(cancelOrderList)) {return;}OmsOrder cancelOrder = cancelOrderList.get(0);if (cancelOrder != null) {//修改訂單狀態為取消cancelOrder.setStatus(4);orderMapper.updateByPrimaryKeySelective(cancelOrder);OmsOrderItemExample orderItemExample = new OmsOrderItemExample();orderItemExample.createCriteria().andOrderIdEqualTo(orderId);List<OmsOrderItem> orderItemList = orderItemMapper.selectByExample(orderItemExample);//解除訂單商品庫存鎖定if (!CollectionUtils.isEmpty(orderItemList)) {for (OmsOrderItem orderItem : orderItemList) {int count = portalOrderDao.releaseStockBySkuId(orderItem.getProductSkuId(),orderItem.getProductQuantity());if(count==0){Asserts.fail("庫存不足,無法釋放!");}}}//修改優惠券使用狀態updateCouponStatus(cancelOrder.getCouponId(), cancelOrder.getMemberId(), 0);//返還使用積分if (cancelOrder.getUseIntegration() != null) {UmsMember member = memberService.getById(cancelOrder.getMemberId());memberService.updateIntegration(cancelOrder.getMemberId(), member.getIntegration() + cancelOrder.getUseIntegration());}}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/72634.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/72634.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/72634.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于 ChatGPT 創建專屬 GPTs

文章目錄 基于 ChatGPT 創建專屬 GPTs一、效果展示1.1 中文命名專家1.2 行程小助手 二、核心配置2.1 Instructions2.3 Actions 三、Agent 簡介3.1 功能框架3.2 工作流程3.3 意圖識別 四、數據流程 基于 ChatGPT 創建專屬 GPTs ChatGPT 具備定制 GPTs 的能力&#xff0c;能夠通…

Spring Boot WebFlux 中 WebSocket 生命周期解析

Spring Boot WebFlux 中的 WebSocket 提供了一種高效、異步的方式來處理客戶端與服務器之間的雙向通信。WebSocket 連接的生命周期包括連接建立、消息傳輸、連接關閉以及資源清理等過程。此外&#xff0c;為了確保 WebSocket 連接的穩定性和可靠性&#xff0c;我們可以加入重試…

【數據挖掘】異構圖與同構圖

在圖論&#xff08;Graph Theory&#xff09;中&#xff0c;異構圖&#xff08;Heterogeneous Graph&#xff09;和同構圖&#xff08;Homogeneous Graph&#xff09;是兩種不同的圖結構概念&#xff0c;它們的主要區別在于節點和邊的類型是否單一。 1. 異構圖&#xff08;Hete…

Golang實踐錄:go發布版本信息收集

go發布版本信息收集。 背景 本文從官方、網絡資料收羅有關go的發布歷史概況。主要目的是能快速了解golang不同版本的變更。鑒于官方資料為英文&#xff0c;為方便閱讀&#xff0c;使用工具翻譯成中文&#xff0c;重要特性參考其它資料補充/修改。由于發布版本內容較多&#xf…

【C++】: STL詳解 —— set和map類

目錄 關聯式容器 鍵值對 set set的概念 set的構造函數 set的使用 map map的概念 map的構造函數 map的使用 multiset multimap 關聯式容器 C標準庫提供了多種容器&#xff0c;用于高效管理和操作數據集合。這些容器可分為以下幾類&#xff1a; 順序容器&#xff08;…

DeepSeek:構筑大數據平臺底座的最優解

一、大數據平臺底座的重要性 在數字化浪潮席卷全球的當下,數據已成為企業乃至整個社會最具價值的資產之一 。大數據平臺底座作為數據處理和業務支撐的核心樞紐,其重要性不言而喻,猶如大廈的基石,關乎整個數據生態系統的穩定與發展。 從數據處理角度來看,隨著互聯網、物聯…

Minix OS的配置 SSH C程序編譯

Minix3的下載 官網&#xff1a;https://www.minix3.org/ 安裝 平臺&#xff1a;VMware 開機后進入系統使用setup命令來配置和安裝盡量配置一個DNS服務器&#xff0c;比如8.8.8.8 SSH 安裝&#xff1a;pkgin install openssh 修改配置文件&#xff0c;需要&#xff1a; 修…

ubuntu20 安裝python2

1. 確保啟用了 Universe 倉庫 在某些情況下&#xff0c;python2-minimal 包可能位于 Universe 倉庫中。你可以通過以下命令啟用 Universe 倉庫并更新軟件包列表&#xff1a; bash復制 sudo add-apt-repository universe sudo apt update 然后嘗試安裝&#xff1a; bash復制…

STM32---FreeRTOS中斷管理試驗

一、實驗 實驗目的&#xff1a;學會使用FreeRTOS的中斷管理 創建兩個定時器&#xff0c;一個優先級為4&#xff0c;另一個優先級為6&#xff1b;注意&#xff1a;系統所管理的優先級范圍 &#xff1a;5~15 現象&#xff1a;兩個定時器每1s&#xff0c;打印一段字符串&#x…

docker利用docker-compose-gpu.yml啟動RAGFLOW,文檔解析出錯【親測已解決】

0.問題說明 想要讓RAGFLOW利用GPU資源跑起來&#xff0c;可以選擇docker-compose-gpu.yml啟動。&#xff08;但是官網啟動案例是86平臺的不是NVIDIA GPU的&#xff0c;docker-compose-gpu.yml又是第三方維護&#xff0c;所以稍有問題&#xff09; 1.問題 docker利用docker-c…

【AI深度學習網絡】卷積神經網絡(CNN)入門指南:從生物啟發的原理到現代架構演進

深度神經網絡系列文章 【AI深度學習網絡】卷積神經網絡&#xff08;CNN&#xff09;入門指南&#xff1a;從生物啟發的原理到現代架構演進【AI實踐】基于TensorFlow/Keras的CNN&#xff08;卷積神經網絡&#xff09;簡單實現&#xff1a;手寫數字識別的工程實踐 引言 在當今…

【ThreeJS Basics 06】Camera

文章目錄 Camera 相機PerspectiveCamera 透視相機正交相機用鼠標控制相機大幅度轉動&#xff08;可以看到后面&#xff09; 控制組件FlyControls 飛行組件控制FirstPersonControls 第一人稱控制PointerLockControls 指針鎖定控制OrbitControls 軌道控制TrackballControls 軌跡球…

Linux | Ubuntu 與 Windows 雙系統安裝 / 高頻故障 / UEFI 安全引導禁用

注&#xff1a;本文為 “buntu 與 Windows 雙系統及高頻故障解決” 相關文章合輯。 英文引文&#xff0c;機翻未校。 How to install Ubuntu 20.04 and dual boot alongside Windows 10 如何將 Ubuntu 20.04 和雙啟動與 Windows 10 一起安裝 Dave’s RoboShack Published in…

在 C++ 中,通常會使用 `#define` 來定義宏,并通過這種方式發出警告或提示。

在 C++ 中,通常會使用 #define 來定義宏,并通過這種方式發出警告或提示。 如何實現 GBB_DEPRECATED_MSG 宏: 你可以通過以下方式定義一個宏,顯示棄用警告: #include <iostream>// 定義一個宏,用來打印棄用警告 #define GBB_DEPRECATED_MSG(msg

el-tree右鍵節點動態位置展示菜單;el-tree的節點圖片動態根據節點屬性color改變背景色;加遮罩層(opacity)

一、el-tree右鍵節點動態位置展示菜單 關鍵:@node-contextmenu="handleRightClick"與@node-click=“handleNodeClick” <div class="content"><el-tabs class="tabs" @tab-click="handleClick" v-model="Modal"…

Leetcode 378-有序矩陣中第 K 小的元素

給你一個 n x n 矩陣 matrix &#xff0c;其中每行和每列元素均按升序排序&#xff0c;找到矩陣中第 k 小的元素。 請注意&#xff0c;它是 排序后 的第 k 小元素&#xff0c;而不是第 k 個 不同 的元素。 你必須找到一個內存復雜度優于 O(n2) 的解決方案。 示例 1&#xff1…

【二.提示詞工程與實戰應用篇】【3.Prompt調優:讓AI更懂你的需求】

最近老張在朋友圈秀出用AI生成的國風水墨畫,隔壁王姐用AI寫了份驚艷全場的年終總結,就連樓下小賣部老板都在用AI生成營銷文案。你看著自己跟AI對話時滿屏的"我不太明白您的意思",是不是懷疑自己買了臺假電腦?別慌,這可能是你的打開方式不對。今天咱們就聊聊這個…

UNIAPP前端配合thinkphp5后端通過高德API獲取當前城市天氣預報

如何通過 UniApp 前端項目與 ThinkPHP5 后端結合高德天氣 API 獲取天氣預報信息。我們將分為前端和后端兩部分進行實現。以下是一個完整的代碼. 一、項目結構 project/ ├── frontend/ (UniApp 項目) │ ├── pages/ │ │ └── weather/ │ │ ├── in…

藍橋杯C組真題——巧克力

題目如下 思路 代碼及解析如下 謝謝觀看

CSDN博客寫作教學(五):從寫作到個人IP的體系化構建(完結篇)

導語 (第一篇)Markdown編輯器基礎 (第二篇)Markdown核心語法 (第三篇)文章結構化思維 (第四篇)標題優化與SEO實戰 通過前四篇教程,你已掌握技術寫作的“術”——排版、標題、流量與數據。但真正的價值在于將技能升維為“道”:用技術博客為支點,撬動個人品牌與職業發…