利用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.mzph.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实战 通过前四篇教程,你已掌握技术写作的“术”——排版、标题、流量与数据。但真正的价值在于将技能升维为“道”:用技术博客为支点,撬动个人品牌与职业发…