问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

电商项目中订单处理的技术实现:超时未支付订单的处理方案

创作时间:
作者:
@小白创作中心

电商项目中订单处理的技术实现:超时未支付订单的处理方案

引用
CSDN
1.
https://blog.csdn.net/kong7906928/article/details/145658490

在电商系统中,订单处理是一个核心功能,其中超时未支付订单的处理尤为重要。本文将详细介绍如何使用RabbitMQ延迟消息队列来实现这一功能,包括需求分析、实现思路、具体的技术细节和代码实现。

一、 需求分析

用户进行了下单操作,生成微信支付二维码,如果当前用户没有进行扫码支付,超过了系统所限定的支付时间,需要对当前订单进行超时处理:

基于微信支付的API 去查询该订单的支付状态。如果支付状态为未支付,会基于微信API关闭该订单,并修改数据库中订单状态为已关闭,并对库存数量进行回滚。如果当前订单已经进行支付,则做数据补偿操作(需要修改数据库中的订单状态修改为已支付,并且记录订单日志)。

二、 实现思路

如何获取超过限定时间的订单?我们可以使用延迟消息队列(死信队列)来实现。

所谓延迟消息队列,就是消息的生产者发送的消息并不会立刻被消费者接收并消费,而是在设定的时间之后才可以消费。

我们可以在创建订单时向消息队列发送一个延迟消息,消息为订单号,系统会在限定时间之后取出这个消息,然后查询订单的支付状态,根据结果做出相应的处理。

应用技术:

分布式架构,RabbitMQ,

三、 Rabbitmq延迟消息

使用RabbitMQ来实现延迟消息必须先了解RabbitMQ的两个概念:消息的TTL和死信Exchange,通过这两者的组合来实现上述需求。

3.1 消息的TTL(Time To Live)

消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。

我们创建一个队列queue.temp,在Arguments 中添加x-message-ttl 为5000 (单位是毫秒),那每一个进入这个队列的消息在5秒后会消失。

3.2 死信交换器 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队列消失,

四、代码实现

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)shangcheng_service_pay的WxPayService新增方法定义


/**
 * 关闭订单
 * @param orderId
 * @return
 */
Map closeOrder(String orderId);  

(3)shangcheng_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)shangcheng_service_pay_api的WxPayFeign新增方法


/**
 * 关闭微信订单
 * @param orderId
 * @return
 */
@PutMapping("/wxpay/close/{orderId}")
public Result closeOrder(@PathVariable("orderId") String orderId);  

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);   
  

4.3 订单关闭逻辑

如果为未支付,查询微信订单

如果确认为未支付,调用关闭本地订单( 修改订单表的订单状态、记录订单日志、恢复商品表库存)和微信订单的逻辑。

如果为已支付进行状态补偿。

(1)shangcheng_service_order新增依赖


<dependency>
    <groupId>com.shangcheng</groupId>
    <artifactId>shangcheng_service_pay_api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>   
  

(2)shangcheng_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 );
    }
}   
  

4.4 延迟消息处理

从消息队列queue.ordertimeout 中提取消息

(1)修改OrderServiceImpl的add方法,追加代码,实现mq发送


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

(2)shangcheng_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();
        }
    }
}   
  
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号