第15章-支付之后业务处理.md 26 KB

第15章-支付成功后续业务&取消关闭订单业务

学习目标:

  • 完成支付成功后续业务处理
    • 订单状态修改
    • 对接库存系统库存(锁定库存)
    • 订单拆单
  • 完成订单关闭后续业务处理
    • 修改订单状态
    • 关闭本地交易记录
    • 关闭支付宝交易记录

1、支付成功处理

1.1 更改订单状态

订单支付成功后,我们已经更改了订单支付记录状态,接下来我还有更改订单状态,因为他们是不同的微服务模块,所以我们采用消息队列的方式,保证数据最终一致性

1.1.1 在MqConst常量类添加变量

/**
 * 订单支付
 */
public static final String EXCHANGE_DIRECT_PAYMENT_PAY = "exchange.direct.payment.pay";
public static final String ROUTING_PAYMENT_PAY = "payment.pay";
//队列
public static final String QUEUE_PAYMENT_PAY  = "queue.payment.pay";
/**
 * 减库存
 */
public static final String EXCHANGE_DIRECT_WARE_STOCK = "exchange.direct.ware.stock";
public static final String ROUTING_WARE_STOCK = "ware.stock";
//队列
public static final String QUEUE_WARE_STOCK  = "queue.ware.stock";
/**
 * 减库存成功,更新订单状态
 */
public static final String EXCHANGE_DIRECT_WARE_ORDER = "exchange.direct.ware.order";
public static final String ROUTING_WARE_ORDER = "ware.order";
//队列
public static final String QUEUE_WARE_ORDER  = "queue.ware.order";

1.1.2 在service-payment 中添加依赖和配置

<dependency>
    <groupId>com.atguigu.gmall</groupId>
    <artifactId>rabbit-util</artifactId>
    <version>1.0</version>
</dependency>

1.1.3 支付成功发送消息

service-payment模块com.atguigu.gmall.payment.service.impl.AlipayServiceImpl#paySuccessNotify方法

/**
 * 支付宝异步回调:通知商户系统支付结果
 *
 * @param paramsMap: 支付宝回调提交参数
 * @return
 */
@Override
public String paySuccessNotify(Map paramsMap) {
    try {
        //1. 验签:在支付系统验证提交数据是否为支付宝官方发出,以及在网格传输过程中是否被非法篡改过.避免"虚假"通知.
            // TODO 验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure
            //2. 验证订单以及订单金额是否一致(支付宝提交交易金额跟本地交易记录中金额一致)
            //2.1 获取提交参数中订单编号 查询本地交易记录
            //3. 验证appId是否为商家的
            //4. 验证支付宝端交易结果
            String tradeStatus = (String) paramsMap.get("trade_status");
            if ("TRADE_SUCCESS".equals(tradeStatus)) {
                //5. 修改本地交易记录支付状态
                //6. 发送消息到MQ通知订单系统修改订单状态
                rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_PAY, MqConst.ROUTING_PAYMENT_PAY, paymentInfo.getOrderId());
                return "success";
            }
        }
    } catch (Exception e) {
        log.error("[支付系统],处理支付回调异常:{}", e);
        throw new RuntimeException(e);
    }
    return "failure";
}

1.1.4 service-order模块接收消息

service-order模块中创建OrderReceiver类添加方法

package com.atguigu.gmall.order.receiver;

import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.rabbit.config.MqConst;
import com.atguigu.gmall.enums.model.OrderStatus;
import com.atguigu.gmall.enums.model.ProcessStatus;
import com.atguigu.gmall.order.model.OrderInfo;
import com.atguigu.gmall.order.service.OrderInfoService;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author: atguigu
 * @create: 2023-06-27 11:56
 */
@Slf4j
@Component
public class OrderReceiver {
    @Autowired
    private OrderInfoService orderInfoService;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 监听支付成功消息,完成订单状态修改
     *
     * @param orderId 订单ID
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_PAYMENT_PAY, durable = "true"),
            value = @Queue(value = MqConst.QUEUE_PAYMENT_PAY, durable = "true"),
            key = MqConst.ROUTING_PAYMENT_PAY
    ))
    public void processPaySucess(Long orderId, Message message, Channel channel) {
        if (orderId != null) {
            log.info("[订单服务]监听到支付成功消息,订单ID:{}", orderId);
            //1.业务处理
            String key = "mq:" + orderId + ":md";
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, orderId, 10, TimeUnit.MINUTES);
            if (!flag) {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            //1.1 调用业务层完成订单状态修改
            orderInfoService.updateOrderStatus(orderId, ProcessStatus.PAID);
            //1.2 发送MQ消息通知库存系统进行【拆单】锁定商品库存 -- 分析得知库存系统不允许同一订单多次进行商品锁定,做好幂等性处理
            orderInfoService.sendLockStockMsg(orderId);
            //2.手动应答消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }
}

1.2 订单模块发送减库存通知

订单模块除了接收到请求改变单据状态,还要发送库存系统

查看看《库存管理系统接口手册》中【减库存的消息队列消费端接口】中的描述,组织相应的消息数据进行传递。

1.1.1 OrderService接口

/**
 * 发送MQ消息通知库存系统锁定库存
 * @param orderId
 */
void sendLockStockMsg(Long orderId);


/**
 * 构建第三方库存系统要求参数(包含订单信息,订单明细信息)
 */
Map initWareMap(OrderInfo orderInfo);

1.1.2 编写实现类

/**
 * 发送MQ消息通知库存系统锁定库存
 * 要求:必须要严格按照库存系统接口要求提交参数
 *
 * @param orderId
 */
@Override
public void sendLockStockMsg(Long orderId) {
    //1.根据订单ID查询订单信息以及明细信息
    OrderInfo orderInfo = this.getOrderInfo(orderId);
    if (orderInfo != null) {
        //2.按照接口要求封装库存系统所需MQ参数
        Map<String, Object> mapResult = initWareMap(orderInfo);
        rabbitService.sendMsg(MqConst.EXCHANGE_DIRECT_WARE_STOCK, MqConst.ROUTING_WARE_STOCK, JSON.toJSONString(mapResult));
    }
}

/**
 * 根据订单以及订单明细构建仓库系统所需参数(扣减库存,拆单结果都需要复用该方法)
 *
 * @param orderInfo
 * @return
 */
private Map<String, Object> initWareMap(OrderInfo orderInfo) {
    Map<String, Object> mapResult = new HashMap<>();
    //1.构建订单相关字段
    mapResult.put("orderId", orderInfo.getId());
    mapResult.put("consignee", orderInfo.getConsignee());
    mapResult.put("consigneeTel", orderInfo.getConsigneeTel());
    mapResult.put("orderComment", orderInfo.getOrderComment());
    mapResult.put("orderBody", orderInfo.getTradeBody());
    mapResult.put("deliveryAddress", orderInfo.getDeliveryAddress());
    mapResult.put("paymentWay", PaymentWay.ONLINE.name().equals(orderInfo.getPaymentWay()) ? 1 : 2);
    //特别重要:一定要保证拆单响应结果包含该属性
    mapResult.put("wareId", orderInfo.getWareId());

    //2.循环遍历订单明细构建订单明细对象相关字段
    List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
    if (!CollectionUtils.isEmpty(orderDetailList)) {
        List<Map<Object, Object>> orderDetailMapList = orderDetailList.stream().map(orderDetail -> {
            Map<Object, Object> detailMap = new HashMap<>();
            detailMap.put("skuId", orderDetail.getSkuId());
            detailMap.put("skuName", orderDetail.getSkuName());
            detailMap.put("skuNum", orderDetail.getSkuNum());
            return detailMap;
        }).collect(Collectors.toList());
        mapResult.put("details", orderDetailMapList);
    }
    return mapResult;
}

1.3 消费减库存结果

给仓库系统发送减库存消息后,还要接受减库存成功或者失败的消息。

同样根据《库存管理系统接口手册》中【商品减库结果消息】的说明完成。消费该消息的消息队列监听程序。

接受到消息后主要做的工作就是更新订单状态。

在订单项目中OrderReceiver

/**
 * 监听库存系统反馈锁定库存结果
 * 根据锁定库存结果更新订单状态
 *
 * @param stockLockStr
 * @param message
 * @param channel
 */
@SneakyThrows
@RabbitListener(bindings = @QueueBinding(
        exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_WARE_ORDER, durable = "true"),
        value = @Queue(value = MqConst.QUEUE_WARE_ORDER, durable = "true"),
        key = MqConst.ROUTING_WARE_ORDER
))
public void processStockLockResult(String stockLockStr, Message message, Channel channel) {
    if (StringUtils.isNotBlank(stockLockStr)) {
        log.info("[订单服务]监听库存锁定结果:{}", stockLockStr);

        //根据库存提交扣减结果修改订单状态
        Map map = JSON.parseObject(stockLockStr, Map.class);
        String orderId = (String) map.get("orderId");
        String status = (String) map.get("status");
        if ("DEDUCTED".equals(status)) {
            orderInfoService.updateOrderStatus(Long.valueOf(orderId), ProcessStatus.WAITING_DELEVER);
        }
        if("OUT_OF_STOCK".equals(status)){
            orderInfoService.updateOrderStatus(Long.valueOf(orderId), ProcessStatus.STOCK_EXCEPTION);
        }
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

1.4 拆单接口

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/747

1.3.1 库存系统配置拆单回调接口

1.3.2 拆单接口控制器

service-order订单模块OrderApiController处理拆单请求

/**
 * 处理库存系统发送拆单请求
 *
 * @param orderId
 * @param wareSkuMap
 * @return
 */
@PostMapping("/orderSplit")
public String orderSplit(@RequestParam("orderId") Long orderId, @RequestParam("wareSkuMap") String wareSkuMap) {
    return orderInfoService.orderSplit(orderId, wareSkuMap);
}

1.3.3 订单实现拆单接口

OrderInfoService

/**
 * 根据库存系统反馈进行拆单
 * @param orderId 订单ID
 * @param wareSkuMap 仓库跟商品Sku列表映射关系
 * @return
 */
String orderSplit(Long orderId, String wareSkuMap);

1.3.4 拆单接口实现类

OrderInfoServiceImpl

/**
 * 根据库存系统反馈进行拆单
 * 本质:将参数wareSkuMap中集合中元素有几个 新增几个子订单 skuIds中有几件商品就有几个订单明细
 *
 * @param orderId    订单ID
 * @param wareSkuMap 仓库跟商品Sku列表映射关系 [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]
 * @return
 */
@Override
public String orderSplit(Long orderId, String wareSkuMap) {
    log.info("【订单服务】接收库存提交拆单参数:订单id:{},仓库跟sku映射关系:{}", orderId, wareSkuMap);
    //1.将接收到参数wareSkuMap转为List集合 泛型:Map
    if (StringUtils.isNotBlank(wareSkuMap)) {
        List<Map> wareSkuMapList = JSON.parseArray(wareSkuMap, Map.class);

        //2.获取原始订单以及订单明细(为后续新增子订单提供基础数据)
        OrderInfo originOrderInfo = this.getOrderInfo(orderId);
        List<OrderDetail> originOrderOrderDetailList = originOrderInfo.getOrderDetailList();

        //3.遍历仓库跟SKU映射Map构建新订单以及订单明细并且将其保存(拆单)--每遍历一次就会产生新子订单
        List<OrderInfo> subOrderInfoList = wareSkuMapList.stream().map(wareSkuIdMap -> {
            //3.1.获取订单对应出货仓库ID
            String wareId = (String) wareSkuIdMap.get("wareId");
            //3.2.获取仓库对应商品skuId列表
            List<String> skuIds = (List<String>) wareSkuIdMap.get("skuIds");

            //3.3. 构建新子订单对象-从原始订单中拷贝给属性赋值
            OrderInfo subOrderInfo = new OrderInfo();
            BeanUtils.copyProperties(originOrderInfo, subOrderInfo);
            //3.3.1 为新子订单属性某些属性进行手动赋值 主键、总金额、父订单ID
            subOrderInfo.setId(null); //手动将主键改为空
            subOrderInfo.setParentOrderId(originOrderInfo.getId());
            //因为相应结果时候必须要指定订单对应出库仓库ID 在这里设置 为新订单关联仓库ID
            subOrderInfo.setWareId(wareId);

            //3.3.2 找打当前新子订单包含哪些商品 将原始子订单集合进行过滤获取当前新子订单中订单明细商品
            List<OrderDetail> subOrderDetailList = originOrderOrderDetailList.stream().filter(originOrderDetail -> {
                return skuIds.contains(originOrderDetail.getSkuId().toString());
            }).collect(Collectors.toList());
            subOrderInfo.setOrderDetailList(subOrderDetailList);
            subOrderInfo.sumTotalAmount();

            //3.4 将新订单以及订单明细进行保存
            //3.4.1 保存订单
            this.save(subOrderInfo);
            Long subOrderId = subOrderInfo.getId();

            //3.4.2 保存订单明细
            subOrderDetailList.forEach(orderDetail -> {
                orderDetail.setOrderId(subOrderId);
            });
            orderDetailService.saveBatch(subOrderDetailList);
            return subOrderInfo;
        }).collect(Collectors.toList());

        //4.修改原始订单状态为“SPIT”
        this.updateOrderStatus(orderId, ProcessStatus.SPLIT);

        //5.按照仓库系统要求进行拆单接口响应(注意:wareId 每个订单对应出货仓库id设置)
        List<Map<String, Object>> collect = subOrderInfoList.stream().map(subOrderInfo -> {
            Map<String, Object> mapResult = this.initWareMap(subOrderInfo);
            return mapResult;
        }).collect(Collectors.toList());

        return JSON.toJSONString(collect);
    }
    return null;
}

1.3.5 我的订单列表

订单持久层Mapper映射文件中,增加过滤订单条件,将拆分原始订单排除掉 OrderInfoMapper.xml

<!--查询用户订单列表-->
<select id="getOrderList" resultMap="orderInfoMap">
    select
        oi.id,
        oi.consignee,
        oi.consignee_tel,
        oi.total_amount,
        oi.order_status,
        oi.user_id,
        oi.payment_way,
        oi.delivery_address,
        oi.order_comment,
        oi.out_trade_no,
        oi.trade_body,
        oi.create_time,
        od.id order_deatil_id,
        od.sku_id,
        od.sku_name,
        od.img_url,
        od.order_price,
        od.sku_num
    from order_info oi inner join  order_detail od on od.order_id = oi.id
    where oi.user_id = #{userId} and oi.order_status != 'SPLIT'
    order by  oi.create_time
</select>

2、超时订单业务补充

2.1 在MqConst中添加常量

/**
 * 关闭交易
 */
public static final String EXCHANGE_DIRECT_PAYMENT_CLOSE = "exchange.direct.payment.close";
public static final String ROUTING_PAYMENT_CLOSE = "payment.close";
//队列
public static final String QUEUE_PAYMENT_CLOSE  = "queue.payment.close";

2.2 在取消订单监听器发送关闭交易消息

service-order中修改com.atguigu.gmall.order.service.impl.OrderInfoServiceImpl#execExpiredOrder方法

/**
 * 关闭订单
 *
 * @param orderId
 */
@Override
public void execExpiredOrder(Long orderId) {
    //1.查询订单状态 判断状态 是否为待支付
    OrderInfo orderInfo = this.getById(orderId);
    if (orderInfo != null && (OrderStatus.UNPAID.name().equals(orderInfo.getOrderStatus()) || OrderStatus.PAID.name().equals(orderInfo.getOrderStatus()))) {
        //2.修改为关闭
        this.updateOrderStatus(orderId, ProcessStatus.CLOSED);
        //3.新增-发送关闭订单消息到MQ通知支付系统关闭交易记录
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, MqConst.ROUTING_PAYMENT_CLOSE, orderId);
    }
}

2.3 service-payment模块接收消息

2.3.1 编写消费者

package com.atguigu.gmall.payment.receiver;

import com.atguigu.gmall.common.rabbit.config.MqConst;
import com.atguigu.gmall.enums.model.PaymentStatus;
import com.atguigu.gmall.enums.model.PaymentType;
import com.atguigu.gmall.payment.model.PaymentInfo;
import com.atguigu.gmall.payment.service.PaymentInfoService;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @author: atguigu
 * @create: 2023-06-30 09:35
 */
@Slf4j
@Component
public class PaymentReceiver {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private PaymentInfoService paymentInfoService;


    /**
     * 监听关闭订单消息,关闭交易记录(本地跟支付宝)
     *
     * @param orderId
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, durable = "true"),
            value = @Queue(value = MqConst.QUEUE_PAYMENT_CLOSE, durable = "true"),
            key = MqConst.ROUTING_PAYMENT_CLOSE
    ))
    public void processClosePayment(Long orderId, Message message, Channel channel) {
        if (orderId != null) {
            log.info("[支付服务]监听到关闭交易消息:{}", orderId);
            //1.消息幂等性 set nx
            String key = "mq:" + orderId + ":close";
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, orderId, 5, TimeUnit.SECONDS);
            if (!flag) {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                return;
            }
            //2.关闭交易记录
            //2.1 根据订单ID查询本地交易记录
            PaymentInfo paymentInfo = paymentInfoService.getPayInfoByOrderIdOrOutTradeNo(orderId.toString(), PaymentType.ALIPAY.name());
            if (paymentInfo != null && PaymentStatus.UNPAID.name().equals(paymentInfo.getPaymentStatus())) {
                //2.2 修改本地交易记录状态
                paymentInfo.setPaymentStatus(PaymentStatus.CLOSED.name());
                paymentInfoService.updateById(paymentInfo);
            }
        }
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

2.3.2 查询本地交易记录接口与实现类

PaymentInfoService

/**
 * 根据订单编号/订单ID +支付方式 查询本地交易记录
 * @param orderId
 * @param paymentType
 * @return
 */
PaymentInfo getPayInfoByOrderIdOrOutTradeNo(String orderId, String paymentType);

PaymentInfoServiceImpl

/**
 * 根据订单编号/订单ID +支付方式 查询本地交易记录
 *
 * @param orderId
 * @param paymentType
 * @return
 */
@Override
public PaymentInfo getPayInfoByOrderIdOrOutTradeNo(String orderId, String paymentType) {
    LambdaQueryWrapper<PaymentInfo> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(PaymentInfo::getPaymentType, paymentType);
    queryWrapper.and(a -> a.eq(PaymentInfo::getOrderId, orderId).or().eq(PaymentInfo::getOutTradeNo, orderId));
    PaymentInfo paymentInfo = this.getOne(queryWrapper);
    return paymentInfo;
}

2.4 支付宝关闭交易

2.4.2 编写接口

AlipayService接口

/**
 * 关闭支付宝交易
 * @param paymentInfo
 */
void closeAlipayTrade(PaymentInfo paymentInfo);

2.4.3 编写实现类

AlipayServiceImpl

/**
 * 关闭支付宝交易
 *
 * @param paymentInfo
 */
@Override
public void closeAlipayTrade(PaymentInfo paymentInfo) {
    try {
        AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
        JSONObject bizContent = new JSONObject();
        if (StringUtils.isNotBlank(paymentInfo.getOutTradeNo())) {
            bizContent.put("out_trade_no", paymentInfo.getOutTradeNo());
        }
        if (StringUtils.isNotBlank(paymentInfo.getTradeNo())) {
            //todo 支付宝交易号,只有付款成功后才有值
            bizContent.put("trade_no", paymentInfo.getTradeNo());
        }
        request.setBizContent(bizContent.toString());
        AlipayTradeCloseResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            System.out.println("支付宝交易关闭成功");
        } else {
            System.out.println("支付宝交易调用失败");
        }
    } catch (Exception e) {
        log.error("[支付宝]关闭交易异常:{}", e);
        throw new RuntimeException(e);
    }
}

2.5 查询支付交易记录

2.5.1 编写接口

AlipayService

/**
 * 获取支付宝交易状态
 * @param paymentInfo
 * @return
 */
String getAliPayTradeSatus(PaymentInfo paymentInfo);

2.5.2 编写实现类

AlipayServiceImpl

/**
 * 查询支付宝交易状态
 *
 * @param paymentInfo
 * @return
 */
@Override
public String getAliPayTradeSatus(PaymentInfo paymentInfo) {
    try {
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        JSONObject jsonObject = new JSONObject();
        if (StringUtils.isNotBlank(paymentInfo.getOutTradeNo())) {
            jsonObject.put("out_trade_no", paymentInfo.getOutTradeNo());
        }
        if (StringUtils.isNotBlank(paymentInfo.getTradeNo())) {
            //todo 支付宝交易号,只有付款成功后才有值
            jsonObject.put("trade_no", paymentInfo.getTradeNo());
        }
        //jsonObject.put("query_options", Arrays.asList("trade_settle_info"));
        request.setBizContent(jsonObject.toJSONString());
        AlipayTradeQueryResponse response = alipayClient.execute(request);
        if (response.isSuccess()) {
            return response.getTradeStatus();
        }
    } catch (Exception e) {
        log.error("[支付宝]获取交易状态异常:{}", e);
        throw new RuntimeException(e);
    }
    return null;
}

2.6 整合关闭过期订单

service-payment支付模块监听器中处理

package com.atguigu.gmall.payment;

import com.atguigu.gmall.enums.model.PaymentStatus;
import com.atguigu.gmall.enums.model.PaymentType;
import com.atguigu.gmall.enums.model.PaymentWay;
import com.atguigu.gmall.payment.model.PaymentInfo;
import com.atguigu.gmall.payment.service.AlipayService;
import com.atguigu.gmall.payment.service.PaymentInfoService;
import com.atguigu.gmall.rabbit.config.MqConst;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @author: atguigu
 * @create: 2023-08-15 09:26
 */
@Slf4j
@Component
public class PaymentRecevier {


    @Autowired
    private PaymentInfoService paymentInfoService;

    @Autowired
    private AlipayService alipayService;

    /***
     * 监听关闭交易消息(订单超时关闭),将本地交易,支付宝交易同样关闭
     * @param outTradeNo 订单编号
     * @param channel
     * @param message
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE, durable = "true"),
            value = @Queue(value = MqConst.QUEUE_PAYMENT_CLOSE, durable = "true"),
            key = MqConst.ROUTING_PAYMENT_CLOSE
    ))
    public void processCloseTrade(String outTradeNo, Channel channel, Message message) {
        if (StringUtils.isNotBlank(outTradeNo)) {
            log.info("[支付服务]监听到关闭交易消息:{}", outTradeNo);
            //1.查询本地交易记录是否存在,如果存在修改状态为关闭
            PaymentInfo paymentInfo = paymentInfoService.getPaymentInfo(outTradeNo, PaymentType.ALIPAY.name());
            if (paymentInfo != null) {
                paymentInfo.setPaymentStatus(PaymentStatus.CLOSED.name());
                paymentInfoService.updateById(paymentInfo);
                //2.查询支付宝交易记录是否存在,如果存在将交易关闭
                //2.1 调用支付宝接口查询支付宝交易 如果状态:等待买家付款
                String alipayStatus = alipayService.getAliPayTradeSatus(paymentInfo);
                //2.2 调用支付宝接口关闭支付宝交易
                if("WAIT_BUYER_PAY".equals(alipayStatus)){
                    alipayService.closeAlipayTrade(paymentInfo);
                }
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }
}