超时未支付订单处理
需求分析#
超过限定时间并未支付的订单,我们需要进行超时订单的处理:先调用微信支付api,查询该订单的支付状态。如果未支付调用关闭订单的api,并修改订单状态为已关闭,并回滚库存数。如果该订单已经支付,则做补偿操作(修改订单状态和记录)。
实现思路#
如何获取超过限定时间的订单?我们可以使用延迟消息队列(死信队列)来实现。
所谓延迟消息队列,就是消息的生产者发送的消息并不会立刻被消费,而是在设定的时间之后才可以消费。
我们可以在订单创建时发送一个延迟消息,消息为订单号,系统会在限定时间之后取出这个消息,然后查询订单的支付状态,根据结果做出相应的处理。
rabbitmq延迟消息#
使用RabbitMQ来实现延迟消息必须先了解RabbitMQ的两个概念:消息的TTL和死信Exchange,通过这两者的组合来实现上述需求。
消息的TTL(Time To Live)#
消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。
我们创建一个队列queue.temp,在Arguments 中添加x-message-ttl 为5000 (单位是毫秒),那每一个进入这个队列的消息在5秒后会消失。
死信交换器 Dead Letter Exchanges#
一个消息在满足如下条件下,会进死信交换机,记住这里是交换机而不是队列,一个交换机可以对应很多队列。
(1) 一个消息被Consumer拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。
(2)上面的消息的TTL到了,消息过期了。
(3)队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信交换机上。
Dead Letter Exchange其实就是一种普通的exchange,和创建其他exchange没有两样。只是在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去。
我们现在可以测试一下延迟队列。
(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秒后消息从queue.ordercreate队列消失
代码实现#
微信支付-关闭订单#
(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)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)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) { System.out.println("关闭订单开启:"+orderId); Order order = orderMapper.selectByPrimaryKey( orderId ); if(order==null){ throw new RuntimeException( "订单不存在!" ); } if(!"0".equals( order.getOrderStatus() )){ System.out.println("此订单不用关闭"); return; } System.out.println("关闭订单通过校验:"+orderId); //调用微信订单查询,检测支付状态 Map wxQueryMap = (Map)wxPayFeign.queryOrder( orderId ).getData(); System.out.println("查询微信支付订单:"+wxQueryMap); if("SUCCESS".equals( wxQueryMap.get( "trade_state" ) ) ){ //如果支付状态是成功,进行补偿 updatePayStatus( orderId, (String)wxQueryMap.get( "transaction_id" ) ); System.out.println("补偿"); } if("NOTPAY".equals( wxQueryMap.get( "trade_state" ) ) ){ //如果是未支付,关闭订单 System.out.println("执行关闭!"); order.setCloseTime( 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()); } //关闭微信订单 wxPayFeign.closeOrder( orderId ); } }
延迟消息处理#
从消息队列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(); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)