1. 程式人生 > 其它 >HM-RocketMQ2.5【下單業務】

HM-RocketMQ2.5【下單業務】

1 下單業務時序圖

呼叫下單服務:遠端RPC呼叫訂單服務

預訂單:使用者不可見

扣減庫存:遠端RPC呼叫庫存服務

扣減優惠券:遠端RPC呼叫優惠券服務

扣減使用者預測:遠端RPC呼叫使用者服務

確認訂單:將預訂單狀態改為使用者可見

確認訂單成功-->生成訂單成功-->返回下單結果

確認訂單成功-->MQ監聽-->回退以上扣減&取消訂單

2 下單基本流程(下單成功)

2.1 介面定義

shop-api模組

package com.irun2u.api;

import com.irun2u.entity.Result;
import com.irun2u.shop.pojo.TradeOrder;

/**
 * @Author: haifei
 * @Date: 2022/12/5 15:20
 */
public interface OrderService {

    /**
     * 確認訂單
     * @param order
     * @return
     */
    Result confirmOrder(TradeOrder order);
}

2.2 業務類實現

shop-order-service模組

package com.irun2u.service.impl;

import com.irun2u.api.OrderService;
import com.irun2u.entity.Result;
import com.irun2u.shop.pojo.TradeOrder;

/**
 * @Author: haifei
 * @Date: 2022/12/5 15:23
 */
public class OrderServiceImpl implements OrderService {

    @Override
    public Result confirmOrder(TradeOrder order) {
        //1.校驗訂單
        //2.生成預訂單
        try {
            // 保證3-7的原子性
            //3.扣減庫存
            //4.扣減優惠券
            //5.使用餘額
            //6.確認訂單
            //7.返回成功狀態
        } catch (Exception e) {
            //1.確認訂單失敗,傳送訊息
            //2.返回失敗狀態
            //以上具體實現詳見2.9
        }
        return null;
    }

}

2.3 校驗訂單

/**
     * 校驗訂單
     * @param order
     */
private void checkOrder(TradeOrder order) {
    //1.校驗訂單是否存在
    if (order == null){
        CastException.cast(ShopCode.SHOP_ORDER_INVALID);
    }
    //2.校驗訂單中的商品是否存在
    TradeGoods goods = goodsService.findOne(order.getGoodsId());
    if (goods == null){
        CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
    }
    //3.校驗下單使用者是否存在
    TradeUser user = userService.findOne(order.getUserId());
    if (user == null){
        CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
    }
    //4.校驗訂單金額是否合法
    //        if (order.getPayAmount().compareTo(goods.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()))) != 0){
    if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0){
        CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
    }
    //5.校驗訂單商品數量是否合法
    if (order.getGoodsNumber() >= goods.getGoodsNumber()){
        CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
    }
    log.info("校驗訂單通過");
}

2.4 生成預訂單

/**
     * 生成預訂單
     * @param order
     * @return
     */
    private Long savePreOrder(TradeOrder order){
        //1.設定訂單狀態不可見
        order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
        //2.設定訂單id
        long orderId = idWorker.nextId();
        order.setOrderId(orderId);
        //3.核算訂單運費是否合法
        BigDecimal shippingFee = calculateShippinFee(order.getOrderAmount());
        if (order.getShippingFee().compareTo(shippingFee) != 0){
            CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
        }
        //4.核算訂單總金額是否合法
        BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
        orderAmount.add(shippingFee);
        if (order.getOrderAmount().compareTo(orderAmount) != 0){
            CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
        }
        //5.判斷使用者是否使用餘額
        BigDecimal moneyPaid = order.getMoneyPaid();
        if (moneyPaid != null){
            //5.1判斷訂單中餘額是否合法
            int r = moneyPaid.compareTo(BigDecimal.ZERO);
            if (r == -1){ //餘額小於0
                CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
            }
            if (r == 1){ //餘額大於0
                TradeUser user = userService.findOne(order.getUserId());
                if (moneyPaid.compareTo(new BigDecimal(user.getUserMoney())) == 1){ //訂單所需money大於使用者所擁有餘額
                    CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
                }
            }
        }else {
            order.setMoneyPaid(BigDecimal.ZERO);
        }
        //6.判斷使用者是否使用優惠券
        Long couponId = order.getCouponId();
        if (couponId != null){
            TradeCoupon coupon = couponService.findOne(couponId);
            //6.1判斷優惠券是否存在
            if (coupon == null){
                CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
            }
            //6.2判斷優惠券是否已被使用
            if (coupon.getIsUsed().intValue() == ShopCode.SHOP_COUPON_ISUSED.getCode()){
                CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
            }
        }else {
            order.setCouponPaid(BigDecimal.ZERO);
        }
        //7.核算訂單支付金額[=訂單總金額-餘額-優惠券金額]
        BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
        order.setPayAmount(payAmount);
        //8.設定下單時間
        order.setAddTime(new Date());
        //9.儲存訂單到資料庫
        orderMapper.insert(order);
        //10.返回訂單id
        return orderId;
    }

2.5 扣減庫存

通過dubbo呼叫商品服務完成扣減庫存

/**
     * 扣減庫存
     * @param order
     */
    private void reduceGoodsNum(TradeOrder order) {
        TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
        goodsNumberLog.setOrderId(order.getOrderId());
        goodsNumberLog.setGoodsId(order.getGoodsId());
        goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
        Result result = goodsService.reduceGoodsNum(goodsNumberLog);
        if (result.getSuccess().equals(ShopCode.SHOP_FAIL.getCode())){
            CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
        }
        log.info("訂單:" + order.getOrderId() + "扣減庫存成功");
    }
/**
     * 扣減庫存
     * @param goodsNumberLog
     * @return
     */
    Result reduceGoodsNum(TradeGoodsNumberLog goodsNumberLog);

商品服務GoodsService扣減庫存

@Override
    public Result reduceGoodsNum(TradeGoodsNumberLog goodsNumberLog) {
        if (goodsNumberLog == null ||
            goodsNumberLog.getGoodsNumber() == null ||
            goodsNumberLog.getGoodsId() == null ||
            goodsNumberLog.getOrderId() == null ||
            goodsNumberLog.getGoodsNumber() <= 0){
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }
        TradeGoods goods = goodsMapper.selectByPrimaryKey(goodsNumberLog.getGoodsId());
        if (goods.getGoodsNumber() < goodsNumberLog.getGoodsNumber()){
            //庫存不足
            CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
        }
        //減庫存、更新庫存
        goods.setGoodsNumber(goods.getGoodsNumber() - goodsNumberLog.getGoodsNumber());
        goodsMapper.updateByPrimaryKey(goods);
        //記錄庫存操作的日誌
        goodsNumberLog.setGoodsNumber( -(goodsNumberLog.getGoodsNumber()) );
        goodsNumberLog.setLogTime(new Date());
        goodsNumberLogMapper.insert(goodsNumberLog);
        return new Result(ShopCode.SHOP_SUCCESS.getSuccess(), ShopCode.SHOP_SUCCESS.getMessage());
    }

2.6 扣減優惠券

通過dubbo完成扣減優惠券

/**
     * 扣減優惠券
     * @param order
     */
    private void updateCouponStatus(TradeOrder order) {
        if(order.getCouponId()!=null){
            TradeCoupon coupon = couponService.findOne(order.getCouponId());
            coupon.setOrderId(order.getOrderId());
            coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
            coupon.setUsedTime(new Date());
            //更新優惠券狀態
            Result result =  couponService.updateCouponStatus(coupon);
            if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
                CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
            }
            log.info("訂單:"+order.getOrderId()+",使用優惠券");
        }
    }
/**
     * 更新優惠券狀態
     * @param coupon
     * @return
     */
    Result updateCouponStatus(TradeCoupon coupon);

優惠券服務CouponService更改優惠券狀態

@Override
    public Result updateCouponStatus(TradeCoupon coupon) {
        if(coupon==null||coupon.getCouponId()==null){
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }
        //更新優惠券狀態
        couponMapper.updateByPrimaryKey(coupon);
        return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
    }

2.7 扣減使用者餘額

/**
     * 扣減餘額
     * @param order
     */
    private void reduceMoneyPaid(TradeOrder order) {
        if(order.getMoneyPaid()!=null && order.getMoneyPaid().compareTo(BigDecimal.ZERO)==1){
            TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
            userMoneyLog.setOrderId(order.getOrderId());
            userMoneyLog.setUserId(order.getUserId());
            userMoneyLog.setUseMoney(order.getMoneyPaid());
            userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_PAID.getCode());
            Result result = userService.updateMoneyPaid(userMoneyLog);
            if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
                CastException.cast(ShopCode.SHOP_USER_MONEY_REDUCE_FAIL);
            }
            log.info("訂單:"+order.getOrderId()+",扣減餘額成功");
        }
    }
/**
     * 更新使用者餘額
     * @param userMoneyLog
     * @return
     */
    Result updateMoneyPaid(TradeUserMoneyLog userMoneyLog);
@Override
    public Result updateMoneyPaid(TradeUserMoneyLog userMoneyLog) {
        //1.校驗引數是否合法
        if(userMoneyLog==null ||
                userMoneyLog.getUserId()==null ||
                userMoneyLog.getOrderId()==null ||
                userMoneyLog.getUseMoney()==null||
                userMoneyLog.getUseMoney().compareTo(BigDecimal.ZERO)<=0){
            CastException.cast(ShopCode.SHOP_REQUEST_PARAMETER_VALID);
        }

        //2.查詢訂單餘額使用日誌
        TradeUserMoneyLogExample userMoneyLogExample = new TradeUserMoneyLogExample();
        TradeUserMoneyLogExample.Criteria criteria = userMoneyLogExample.createCriteria();
        criteria.andOrderIdEqualTo(userMoneyLog.getOrderId());
        criteria.andUserIdEqualTo(userMoneyLog.getUserId());
        int r = userMoneyLogMapper.countByExample(userMoneyLogExample);

        TradeUser tradeUser = userMapper.selectByPrimaryKey(userMoneyLog.getUserId());

        //3.扣減餘額...
        if(userMoneyLog.getMoneyLogType().intValue()==ShopCode.SHOP_USER_MONEY_PAID.getCode().intValue()){
            if(r>0){
                //已經付款
                CastException.cast(ShopCode.SHOP_ORDER_PAY_STATUS_IS_PAY);
            }
            //減餘額
            tradeUser.setUserMoney(new BigDecimal(tradeUser.getUserMoney()).subtract(userMoneyLog.getUseMoney()).longValue());
            userMapper.updateByPrimaryKey(tradeUser);
        }

        //4.回退餘額...
        if(userMoneyLog.getMoneyLogType().intValue()==ShopCode.SHOP_USER_MONEY_REFUND.getCode().intValue()){
            if(r==0){ //count查詢不可能為複數,所以不會<0,沒查到就是0
                //如果沒有支付,則不能回退餘額
                CastException.cast(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY);
            }
            //防止多次退款
            TradeUserMoneyLogExample userMoneyLogExample2 = new TradeUserMoneyLogExample();
            TradeUserMoneyLogExample.Criteria criteria1 = userMoneyLogExample2.createCriteria();
            criteria1.andOrderIdEqualTo(userMoneyLog.getOrderId());
            criteria1.andUserIdEqualTo(userMoneyLog.getUserId());
            criteria1.andMoneyLogTypeEqualTo(ShopCode.SHOP_USER_MONEY_REFUND.getCode());
            int r2 = userMoneyLogMapper.countByExample(userMoneyLogExample2);
            if(r2>0){
                CastException.cast(ShopCode.SHOP_USER_MONEY_REFUND_ALREADY);
            }
            //退款
            tradeUser.setUserMoney(new BigDecimal(tradeUser.getUserMoney()).add(userMoneyLog.getUseMoney()).longValue());
            userMapper.updateByPrimaryKey(tradeUser);
        }

        //5.記錄訂單餘額使用日誌
        userMoneyLog.setCreateTime(new Date());
        userMoneyLogMapper.insert(userMoneyLog);
        return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
    }

2.8 確認訂單

/**
     * 確認訂單(修改預訂單狀態)
     * @param order
     */
    private void updateOrderStatus(TradeOrder order) {
        order.setOrderStatus(ShopCode.SHOP_ORDER_CONFIRM.getCode());
        order.setPayStatus(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY.getCode());
        order.setConfirmTime(new Date());
        int r = orderMapper.updateByPrimaryKey(order);
        if(r<=0){
            CastException.cast(ShopCode.SHOP_ORDER_CONFIRM_FAIL);
        }
        log.info("訂單:"+order.getOrderId()+"確認訂單成功");
    }

2.9 小結(完整原始碼)

package com.irun2u.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.irun2u.api.CouponService;
import com.irun2u.api.GoodsService;
import com.irun2u.api.OrderService;
import com.irun2u.api.UserService;
import com.irun2u.constant.ShopCode;
import com.irun2u.entity.Result;
import com.irun2u.exception.CastException;
import com.irun2u.mapper.TradeOrderMapper;
import com.irun2u.shop.pojo.*;
import com.irun2u.utils.IDWorker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.Date;

/**
 * @Author: haifei
 * @Date: 2022/12/5 15:23
 */
@Slf4j
@Component
@Service(interfaceClass = OrderService.class)
public class OrderServiceImpl implements OrderService {

    @Reference
    private GoodsService goodsService;

    @Reference
    private UserService userService;

    @Reference
    private CouponService couponService;

    @Autowired
    private IDWorker idWorker;

    @Autowired
    private TradeOrderMapper orderMapper;

    @Override
    public Result confirmOrder(TradeOrder order) {
        //1.校驗訂單
        checkOrder(order);
        //2.生成預訂單
        Long orderId = savePreOrder(order);
        try {// 保證3-7的原子性
            //3.扣減庫存
            reduceGoodsNum(order);
            //4.扣減優惠券
            updateCouponStatus(order);
            //5.扣減餘額
            reduceMoneyPaid(order);
            //6.確認訂單
            updateOrderStatus(order);
            //7.返回成功狀態
            return new Result(ShopCode.SHOP_SUCCESS.getSuccess(),ShopCode.SHOP_SUCCESS.getMessage());
        } catch (Exception e) {
            //1.確認訂單失敗,傳送訊息

            //2.返回失敗狀態
            return new Result(ShopCode.SHOP_FAIL.getSuccess(),ShopCode.SHOP_FAIL.getMessage());
        }
    }

    /**
     * 確認訂單(修改預訂單狀態)
     * @param order
     */
    private void updateOrderStatus(TradeOrder order) {
        order.setOrderStatus(ShopCode.SHOP_ORDER_CONFIRM.getCode());
        order.setPayStatus(ShopCode.SHOP_ORDER_PAY_STATUS_NO_PAY.getCode());
        order.setConfirmTime(new Date());
        int r = orderMapper.updateByPrimaryKey(order);
        if(r<=0){
            CastException.cast(ShopCode.SHOP_ORDER_CONFIRM_FAIL);
        }
        log.info("訂單:"+order.getOrderId()+"確認訂單成功");
    }

    /**
     * 扣減餘額
     * @param order
     */
    private void reduceMoneyPaid(TradeOrder order) {
        if(order.getMoneyPaid()!=null && order.getMoneyPaid().compareTo(BigDecimal.ZERO)==1){
            TradeUserMoneyLog userMoneyLog = new TradeUserMoneyLog();
            userMoneyLog.setOrderId(order.getOrderId());
            userMoneyLog.setUserId(order.getUserId());
            userMoneyLog.setUseMoney(order.getMoneyPaid());
            userMoneyLog.setMoneyLogType(ShopCode.SHOP_USER_MONEY_PAID.getCode());
            Result result = userService.updateMoneyPaid(userMoneyLog);
            if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
                CastException.cast(ShopCode.SHOP_USER_MONEY_REDUCE_FAIL);
            }
            log.info("訂單:"+order.getOrderId()+",扣減餘額成功");
        }
    }

    /**
     * 扣減優惠券
     * @param order
     */
    private void updateCouponStatus(TradeOrder order) {
        if(order.getCouponId()!=null){
            TradeCoupon coupon = couponService.findOne(order.getCouponId());
            coupon.setOrderId(order.getOrderId());
            coupon.setIsUsed(ShopCode.SHOP_COUPON_ISUSED.getCode());
            coupon.setUsedTime(new Date());
            //更新優惠券狀態
            Result result =  couponService.updateCouponStatus(coupon);
            if(result.getSuccess().equals(ShopCode.SHOP_FAIL.getSuccess())){
                CastException.cast(ShopCode.SHOP_COUPON_USE_FAIL);
            }
            log.info("訂單:"+order.getOrderId()+",使用優惠券");
        }
    }

    /**
     * 扣減庫存
     * @param order
     */
    private void reduceGoodsNum(TradeOrder order) {
        TradeGoodsNumberLog goodsNumberLog = new TradeGoodsNumberLog();
        goodsNumberLog.setOrderId(order.getOrderId());
        goodsNumberLog.setGoodsId(order.getGoodsId());
        goodsNumberLog.setGoodsNumber(order.getGoodsNumber());
        Result result = goodsService.reduceGoodsNum(goodsNumberLog);
        if (result.getSuccess().equals(ShopCode.SHOP_FAIL.getCode())){
            CastException.cast(ShopCode.SHOP_REDUCE_GOODS_NUM_FAIL);
        }
        log.info("訂單:" + order.getOrderId() + "扣減庫存成功");
    }


    /**
     * 校驗訂單
     * @param order
     */
    private void checkOrder(TradeOrder order) {
        //1.校驗訂單是否存在
        if (order == null){
            CastException.cast(ShopCode.SHOP_ORDER_INVALID);
        }
        //2.校驗訂單中的商品是否存在
        TradeGoods goods = goodsService.findOne(order.getGoodsId());
        if (goods == null){
            CastException.cast(ShopCode.SHOP_GOODS_NO_EXIST);
        }
        //3.校驗下單使用者是否存在
        TradeUser user = userService.findOne(order.getUserId());
        if (user == null){
            CastException.cast(ShopCode.SHOP_USER_NO_EXIST);
        }
        //4.校驗訂單金額是否合法
//        if (order.getPayAmount().compareTo(goods.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()))) != 0){
        if (order.getGoodsPrice().compareTo(goods.getGoodsPrice()) != 0){
            CastException.cast(ShopCode.SHOP_GOODS_PRICE_INVALID);
        }
        //5.校驗訂單商品數量是否合法
        if (order.getGoodsNumber() >= goods.getGoodsNumber()){
            CastException.cast(ShopCode.SHOP_GOODS_NUM_NOT_ENOUGH);
        }
        log.info("校驗訂單通過");
    }


    /**
     * 生成預訂單
     * @param order
     * @return
     */
    private Long savePreOrder(TradeOrder order){
        //1.設定訂單狀態不可見
        order.setOrderStatus(ShopCode.SHOP_ORDER_NO_CONFIRM.getCode());
        //2.設定訂單id
        long orderId = idWorker.nextId();
        order.setOrderId(orderId);
        //3.核算訂單運費是否合法
        BigDecimal shippingFee = calculateShippinFee(order.getOrderAmount());
        if (order.getShippingFee().compareTo(shippingFee) != 0){
            CastException.cast(ShopCode.SHOP_ORDER_SHIPPINGFEE_INVALID);
        }
        //4.核算訂單總金額是否合法
        BigDecimal orderAmount = order.getGoodsPrice().multiply(new BigDecimal(order.getGoodsNumber()));
        orderAmount.add(shippingFee);
        if (order.getOrderAmount().compareTo(orderAmount) != 0){
            CastException.cast(ShopCode.SHOP_ORDERAMOUNT_INVALID);
        }
        //5.判斷使用者是否使用餘額
        BigDecimal moneyPaid = order.getMoneyPaid();
        if (moneyPaid != null){
            //5.1判斷訂單中餘額是否合法
            int r = moneyPaid.compareTo(BigDecimal.ZERO);
            if (r == -1){ //餘額小於0
                CastException.cast(ShopCode.SHOP_MONEY_PAID_LESS_ZERO);
            }
            if (r == 1){ //餘額大於0
                TradeUser user = userService.findOne(order.getUserId());
                if (moneyPaid.compareTo(new BigDecimal(user.getUserMoney())) == 1){ //訂單所需money大於使用者所擁有餘額
                    CastException.cast(ShopCode.SHOP_MONEY_PAID_INVALID);
                }
            }
        }else {
            order.setMoneyPaid(BigDecimal.ZERO);
        }
        //6.判斷使用者是否使用優惠券
        Long couponId = order.getCouponId();
        if (couponId != null){
            TradeCoupon coupon = couponService.findOne(couponId);
            //6.1判斷優惠券是否存在
            if (coupon == null){
                CastException.cast(ShopCode.SHOP_COUPON_NO_EXIST);
            }
            //6.2判斷優惠券是否已被使用
            if (coupon.getIsUsed().intValue() == ShopCode.SHOP_COUPON_ISUSED.getCode()){
                CastException.cast(ShopCode.SHOP_COUPON_ISUSED);
            }
        }else {
            order.setCouponPaid(BigDecimal.ZERO);
        }
        //7.核算訂單支付金額[=訂單總金額-餘額-優惠券金額]
        BigDecimal payAmount = order.getOrderAmount().subtract(order.getMoneyPaid()).subtract(order.getCouponPaid());
        order.setPayAmount(payAmount);
        //8.設定下單時間
        order.setAddTime(new Date());
        //9.儲存訂單到資料庫
        orderMapper.insert(order);
        //10.返回訂單id
        return orderId;
    }

    /**
     * 核算運費
     * 【BigDecimal】https://baike.baidu.com/item/BigDecimal/5131707?fr=aladdin
     * @param orderAmount
     * @return
     */
    private BigDecimal calculateShippinFee(BigDecimal orderAmount) {
        //小於100不收運費;大於100收10
        if (orderAmount.compareTo(new BigDecimal(100)) == 1){
            return BigDecimal.ZERO;
        }else {
            return new BigDecimal(10);
        }
    }
}

2.10 sb整合junit測試下單流程

zk叢集啟動

dubbo-admin啟動

3 失敗補償機制(下單失敗)

3.1 訊息傳送方

3.2 訊息接收方

3.2.1 回退庫存

3.2.2 回退優惠券

3.2.3 回退餘額

3.2.4 取消訂單

4 測試