全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华

DBC 1.6K 0
温馨提示

流程极其优雅,不过也蛮复杂,已经简化过了,大厂应该会更加逻辑刺激!其中的设计思想非常重要,需要理解好这种设计模式,策略模式+工厂模式!此文章重在理解全链路思维,具体代码看上git!

我们从接口位置开始理解整体思想

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图

这个代码应该很好理解,我们要知道,成功之后他返回的是from,咱们用网页打开它,就会来到对应的支付宝页面了

    @ApiOperation("提交订单")
    @PostMapping("confirm")
    public void confirmOrder(@ApiParam("订单对象") @RequestBody ConfirmOrderRequest orderRequest, HttpServletResponse response) {

        JsonData jsonData = orderService.confirmOrder(orderRequest);

        if (jsonData.getCode() == 0) {

            String client = orderRequest.getClientType();
            String payType = orderRequest.getPayType();

            //如果是支付宝网页支付,都是跳转网页,APP除外
            if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) {

                log.info("创建支付宝订单成功:{}", orderRequest.toString());

                if (client.equalsIgnoreCase(ClientType.H5.name())) {
                    writeData(response, jsonData);

                } else if (client.equalsIgnoreCase(ClientType.APP.name())) {
                    //APP SDK支付  TODO
                }

            } else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) {

                //微信支付 TODO
            }

        } else {

            log.error("创建订单失败{}", jsonData.toString());

        }
    }

更加复杂且精彩的要开始了!让我们走进订单的实现方法!首先我们得先看看我们的大致流程!
* * 防重提交
* * 用户微服务-确认收货地址
* * 商品微服务-获取最新购物项和价格
* * 订单验价
* * 优惠券微服务-获取优惠券
* * 验证价格
* * 锁定优惠券
* * 锁定商品库存
* * 创建订单对象
* * 创建子订单对象
* * 发送延迟消息-用于自动关单
* * 创建支付信息-对接三方支付

走进这个方法confirmOrder

  @Override
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {

        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);


        //获取收货地址详情
        ProductOrderAddressVO addressVO = this.getUserAddress(orderRequest.getAddressId());

        log.info("收货地址信息:{}",addressVO);

        //获取用户加入购物车的商品
        List<Long> productIdList = orderRequest.getProductIdList();

        JsonData cartItemDate = productFeignService.confirmOrderCartItem(productIdList);
        List<OrderItemVO> orderItemList  = cartItemDate.getData(new TypeReference<List<OrderItemVO>>(){});
        log.info("获取的商品:{}",orderItemList);
        if(orderItemList == null){
            //购物车商品不存在
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_CART_ITEM_NOT_EXIST);
        }

        //验证价格,减去商品优惠券
        this.checkPrice(orderItemList,orderRequest);

        //锁定优惠券
        this.lockCouponRecords(orderRequest ,orderOutTradeNo );

        //锁定库存
        this.lockProductStocks(orderItemList,orderOutTradeNo);


        //创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest,loginUser,orderOutTradeNo,addressVO);

        //创建订单项
        this.saveProductOrderItems(orderOutTradeNo,productOrderDO.getId(),orderItemList);

        //发送延迟消息,用于自动关单
        OrderMessage orderMessage = new OrderMessage();
        orderMessage.setOutTradeNo(orderOutTradeNo);
        rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(),rabbitMQConfig.getOrderCloseDelayRoutingKey(),orderMessage);


        //创建支付
        PayInfoVO payInfoVO = new PayInfoVO(orderOutTradeNo,
                productOrderDO.getPayAmount(),orderRequest.getPayType(),
                orderRequest.getClientType(), orderItemList.get(0).getProductTitle(),"",TimeConstant.ORDER_PAY_TIMEOUT_MILLS);

        String payResult = payFactory.pay(payInfoVO);
        if(StringUtils.isNotBlank(payResult)){
            log.info("创建支付订单成功:payInfoVO={},payResult={}",payInfoVO,payResult);
            return JsonData.buildSuccess(payResult);
        }else {
            log.error("创建支付订单失败:payInfoVO={},payResult={}",payInfoVO,payResult);
            return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
        }

    }
详细解读

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图2

接下来让我们走进更加细节方法中了解思路!

进入

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图4

通过feign调用对应的方法

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图6

相信看起来很简单,无非就是Redis中删掉购物车中对应的商品罢了

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图8

进入

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图10

注释已经很明白了!

/**
     * 验证价格
     * 1)统计全部商品的价格
     * 2) 获取优惠券(判断是否满足优惠券的条件),总价再减去优惠券的价格 就是 最终的价格
     *
     * @param orderItemList
     * @param orderRequest
     */
    private void checkPrice(List<OrderItemVO> orderItemList, ConfirmOrderRequest orderRequest) {

        //统计商品总价格
        BigDecimal realPayAmount = new BigDecimal("0");
        if (orderItemList != null) {
            for(OrderItemVO orderItemVO : orderItemList){
                BigDecimal itemRealPayAmount = orderItemVO.getTotalAmount();
                realPayAmount = realPayAmount.add(itemRealPayAmount);
            }
        }

        //获取优惠券,判断是否可以使用
        CouponRecordVO couponRecordVO = getCartCouponRecord(orderRequest.getCouponRecordId());

        //计算购物车价格,是否满足优惠券满减条件
        if(couponRecordVO!=null){

            //计算是否满足满减
            if(realPayAmount.compareTo(couponRecordVO.getConditionPrice()) < 0){
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_COUPON_FAIL);
            }
            if(couponRecordVO.getPrice().compareTo(realPayAmount)>0){
                realPayAmount = BigDecimal.ZERO;

            }else {
                realPayAmount = realPayAmount.subtract(couponRecordVO.getPrice());
            }

        }

        if(realPayAmount.compareTo(orderRequest.getRealPayAmount()) !=0 ){
            log.error("订单验价失败:{}",orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }
    }
这两个其实是一样的,无非就是对商品进行锁定,用到的是mq,具体较复杂,这里就不细说了,主要是学习链路思想,rabbitMQ其实是没有延时队列这种说法的,但是却有死信队列,咱们可以通过死信队列来实现相应的延时队列,具体直接博主上面文章搜索也可以!

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图12

温馨提示

这里插一句,这里的链路其实也是不完全的,也是简化了的,会有很多bug,至于优化,咱们根据业务而定,其实设计思想已经提出,相信也可以进行举一反三!很多位置可以进行优化,就比如说咱们的购物车位置,是不是可以进行优化呢?比如在购物车添加删除什么的地方加入MQ,那么其设计思想是不是就更加的完整了!

进入

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图14

    /**
     * 创建订单
     * @param orderRequest
     * @param loginUser
     * @param orderOutTradeNo
     * @param addressVO
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductOrderAddressVO addressVO) {

        ProductOrderDO productOrderDO = new ProductOrderDO();
        productOrderDO.setUserId(loginUser.getId());
        productOrderDO.setHeadImg(loginUser.getHeadImg());
        productOrderDO.setNickname(loginUser.getName());

        productOrderDO.setOutTradeNo(orderOutTradeNo);
        productOrderDO.setCreateTime(new Date());
        productOrderDO.setDel(0);
        productOrderDO.setOrderType(ProductOrderTypeEnum.DAILY.name());

        //实际支付的价格
        productOrderDO.setPayAmount(orderRequest.getRealPayAmount());

        //总价,未使用优惠券的价格
        productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
        productOrderDO.setState(ProductOrderStateEnum.NEW.name());
        productOrderDO.setPayType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());

        productOrderDO.setReceiverAddress(JSON.toJSONString(addressVO));

        productOrderMapper.insert(productOrderDO);

        return productOrderDO;

    }
进入

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图16

/**
     * 新增订单项
     * @param orderOutTradeNo
     * @param orderId
     * @param orderItemList
     */
    private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemList) {


        List<ProductOrderItemDO> list = orderItemList.stream().map(
                obj->{
                    ProductOrderItemDO itemDO = new ProductOrderItemDO();
                    itemDO.setBuyNum(obj.getBuyNum());
                    itemDO.setProductId(obj.getProductId());
                    itemDO.setProductImg(obj.getProductImg());
                    itemDO.setProductName(obj.getProductTitle());

                    itemDO.setOutTradeNo(orderOutTradeNo);
                    itemDO.setCreateTime(new Date());

                    //单价
                    itemDO.setAmount(obj.getAmount());
                    //总价
                    itemDO.setTotalAmount(obj.getTotalAmount());
                    itemDO.setProductOrderId(orderId);
                    return itemDO;
                }
        ).collect(Collectors.toList());


        orderItemMapper.insertBatch(list);


    }
可以好好学习一下这种批量插入的方法
    <!--批量插入-->
    <insert id="insertBatch">

        insert into product_order_item
        (
        <include refid="Base_Column_List_No_Id"/>
        )
        values
        <foreach collection="orderItemList" item="item" index="index" separator=",">
            (
            #{item.productOrderId},
            #{item.outTradeNo},
            #{item.productId},
            #{item.productName},
            #{item.productImg},
            #{item.buyNum},
            #{item.createTime},
            #{item.totalAmount},
            #{item.amount}
            )
        </foreach>

    </insert>

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图18

进入

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图20

温馨提示

这个位置更加好的体现了策略模式+工厂模式,

我们先来看构件的VO类:PayInfoVO
package net.xdclass.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;


@Data
@AllArgsConstructor
@NoArgsConstructor
public class PayInfoVO {

    /**
     * 订单号
     */
    private String outTradeNo;

    /**
     * 订单总金额
     */
    private BigDecimal payFee;

    /**
     * 支付类型 微信-支付宝-银行-其他
     */
    private String payType;

    /**
     * 端类型 APP/H5/PC
     */
    private String clientType;

    /**
     * 标题
     */
    private String title;

    /**
     * 描述
     */
    private String description;


    /**
     * 订单支付超时时间,毫秒
     */
    private long orderPayTimeoutMills;

}
温馨提示</legend这是一般第三方支付都有的各种字段,如果想要更加复杂,可以根据业务需求,深层次的自定义!
我们来看看简单的工厂模式——PayFactory
package net.xdclass.component;

import lombok.extern.slf4j.Slf4j;
import net.xdclass.enums.ProductOrderPayTypeEnum;
import net.xdclass.vo.PayInfoVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;



@Component
@Slf4j
public class PayFactory {


    @Autowired
    private AlipayStrategy alipayStrategy;

    @Autowired
    private WechatPayStrategy wechatPayStrategy;


    /**
     * 创建支付,简单工程模式
     * @param payInfoVO
     * @return
     */
    public String pay(PayInfoVO payInfoVO){

        String payType = payInfoVO.getPayType();

        if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){
            //支付宝支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy);

            return payStrategyContext.executeUnifiedorder(payInfoVO);

        } else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){
            //微信支付 暂未实现
            PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);

            return payStrategyContext.executeUnifiedorder(payInfoVO);
        }


        return "";
    }


    /**
     * 查询订单支付状态
     *
     * 支付成功返回非空,其他返回空
     *
     * @param payInfoVO
     * @return
     */
    public String queryPaySuccess(PayInfoVO payInfoVO){
        String payType = payInfoVO.getPayType();

        if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){
            //支付宝支付
            PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy);

            return payStrategyContext.executeQueryPaySuccess(payInfoVO);

        } else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){
            //微信支付 暂未实现
            PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);

            return payStrategyContext.executeQueryPaySuccess(payInfoVO);
        }


        return "";


    }




}

这个位置也就是判断各种支付了,需要更深层次了解,可以直接搜索设计模式!

全链路(涉及4个微服务)订单支付宝支付思想回顾(最终版)——博主备忘、精华插图22

发表评论 取消回复
表情 图片 链接 代码

分享