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.超时未支付订单处理_第1张图片

我们现在可以测试一下延迟队列。
(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.超时未支付订单处理_第2张图片

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.changgougroupId>
	<artifactId>changgou_service_pay_apiartifactId>
	<version>1.0-SNAPSHOTversion>
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();
		}
	}
}

你可能感兴趣的:(电商项目)