⽤户下订单之后15分钟⽀付实现_畅购商城(⼗四)订单处理
第14章订单处理
学习⽬标:
掌握rabbitmq的延迟消息的使⽤⽅法
完成批量发货功能,了解第三⽅物流系统
掌握SpringTask使⽤⽅法,完成⾃动收货功能
1. 超时未⽀付订单处理
1.1 需求分析
超过60分钟未⽀付的订单,我们需要进⾏超时订单的处理:先调⽤⽀付api,查询该订单的⽀付状态。如果未⽀付调⽤关闭订单的api,并修改订单状态为已关闭,并回滚库存数。如果该订单已经⽀付,做补偿操作(修改订单状态和记录)。
1.2 实现思路
如何获取超过60分钟的订单?我们可以使⽤延迟消息队列(死信队列)来实现。
所谓延迟消息队列,就是消息的⽣产者发送的消息并不会⽴刻被消费,⽽是在设定的时间之后才可以消费。
我们可以在订单创建时发送⼀个延迟消息,消息为订单号,系统会在60分钟后取出这个消息,然后查询订单的⽀付状态,根据结果做出相应的处理。
1.3 rabbitmq延迟消息
使⽤RabbitMQ来实现延迟消息必须先了解RabbitMQ的两个概念:消息的TTL和死信Exchange,通过这两者的组合来实现上述需求。
1.3.1 消息的TTL(Time To Live)
消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列没有消费者连着的保留时间,也可以对每⼀个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。
我们创建⼀个队列p,在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 Letter Exchange中去。
我们现在可以测试⼀下延迟队列。
(1)创建死信交换器 dertimeout (fanout)
(2)创建队列dertimeout
(3)建⽴死信交换器 dertimeout 与队列dertimeout 之间的绑定
(4)创建队列dercreate,Arguments添加
x-message-ttl=10000
x-dead-letter-exchange:dertimeout
(5)测试:向dercreate队列添加消息,等待10秒后消息从dercreate队列消失,1.4 代码实现
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)changgouservicepay的WxPayService新增⽅法定义
/**
* 关闭订单
* @param orderId
* @return
*/
Map closeOrder(String orderId);
(3)changgouservicepay的 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)changgouservicepay_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)changgouserviceorder新增依赖
com.changgou
changgou_service_pay_api
1.0-SNAPSHOT
(2)changgouserviceorder的OrderService新增⽅法定义
/**
* 关闭订单
* @param orderId
*/
void closeOrder(String orderId);
(3)OrderServiceImpl实现该⽅法
实现逻辑:
1)根据id查询订单信息,判断订单是否存在,订单⽀付状态是否为未⽀付
2)基于查询订单⽀付状态
2.1)如果为success,则修改订单状态
2.2)如果为未⽀付,则修改订单,新增⽇志,恢复库存,关闭订单
@Autowired
private WxPayFeign wxPayFeign;
@Override
@GlobalTransactional
public void closeOrder(String orderId) {
System.out.println("关闭订单开启:"+orderId);
Order order = orderMapper.selectByPrimaryKey( orderId );
if(order==null){
throw new RuntimeException( "订单不存在!" );
}
if(!"0".equals( OrderStatus() )){
System.out.println("此订单不⽤关闭");
return;
}
号改成什么呢好听System.out.println("关闭订单通过校验:"+orderId);
//调⽤订单查询,检测⽀付状态
Map wxQueryMap = (Map)wxPayFeign.queryOrder( orderId ).getData();
System.out.println("查询⽀付订单:"+wxQueryMap);
if("SUCCESS".equals( ( "trade_state" ) ) ){ //如果⽀付状态是成功,进⾏补偿updatePayStatus( orderId, (( "transaction_id" ) );
System.out.println("补偿");
}
if("NOTPAY".equals( ( "trade_state" ) ) ){ //如果是未⽀付,关闭订单System.out.println("执⾏关闭!");
order.setCloseTime( new Date( ) );//关闭时间
order.setOrderStatus( "4" );//关闭状态
orderMapper.updateByPrimaryKeySelective( order );//更新
//记录订单变动⽇志
OrderLog orderLog=new OrderLog();
orderLog.setId( Id()+"" );
orderLog.setOperater("system");// 系统
orderLog.setOperateTime(new Date());//当前⽇期
orderLog.setOrderStatus("4");
orderLog.Id());
orderLogMapper.insert(orderLog);
//恢复库存和销量