责任链模式实现订单校验

1.定义接口

/**
 * 订单校验
 *
 * @author mifuRD
 */
public interface OrderCreateValidator {
    /**
     * 设置下一个校验器
     *
     * @param nextValidator
     */
    public void setNext(OrderCreateValidator nextValidator);

    /**
     * 返回下一个校验器
     *
     * @return
     */
    public OrderCreateValidator getNext();

    /**
     * 校验
     *
     * @param request
     * @throws OrderException 订单异常
     */
    public void validate(OrderCreateRequest request) throws OrderException;
}

2.实现校验器(抽象类)

/**
 * 订单校验
 *
 * @author Hollis
 */
public abstract class BaseOrderCreateValidator implements OrderCreateValidator {

    protected OrderCreateValidator nextValidator;

    @Override
    public void setNext(OrderCreateValidator nextValidator) {
        this.nextValidator = nextValidator;
    }

    @Override
    public OrderCreateValidator getNext() {
        return nextValidator;
    }

    /**
     * 校验
     *
     * @param request
     * @throws Exception
     */
    @Override
    public void validate(OrderCreateRequest request) throws OrderException {
        doValidate(request);

        if (nextValidator != null) {
            nextValidator.validate(request);
        }
    }

    /**
     * 校验方法的具体实现
     *
     * @param request
     * @throws OrderException
     */
    protected abstract void doValidate(OrderCreateRequest request) throws OrderException;
}

3.具体的校验方法

/**
 * 1.商品预约校验器
 *
 * @author hollis
 */
public class GoodsBookValidator extends BaseOrderCreateValidator {

    private GoodsFacadeService goodsFacadeService;

    @Override
    protected void doValidate(OrderCreateRequest request) throws OrderException {
        BaseGoodsVO baseGoodsVO = goodsFacadeService.getGoods(request.getGoodsId(), request.getGoodsType());
        if(baseGoodsVO.canBook()){
            Boolean hasBooked = goodsFacadeService.isGoodsBooked(request.getGoodsId(), request.getGoodsType(), request.getBuyerId());

            if (!hasBooked) {
                throw new OrderException(GOODS_NOT_BOOKED);
            }
        }
    }

    public GoodsBookValidator(GoodsFacadeService goodsFacadeService) {
        this.goodsFacadeService = goodsFacadeService;
    }

    public GoodsBookValidator() {
    }
}

/**
 * 2.商品校验器
 *
 * @author hollis
 */
public class GoodsValidator extends BaseOrderCreateValidator {

    private GoodsFacadeService goodsFacadeService;

    @Override
    protected void doValidate(OrderCreateRequest request) throws OrderException {
        BaseGoodsVO baseGoodsVO = goodsFacadeService.getGoods(request.getGoodsId(), request.getGoodsType());

        // 如果商品不是可售状态,则返回失败
        // PS:可售状态为什么要包含SOLD_OUT呢?因为商品查询的接口中去查询了 Redis 的最新库存,而 Redis 的库存在下单时可能已经扣减过刚好为0了,所以这里要包含 SOLD_OUT
        if (baseGoodsVO.getState() != GoodsState.SELLING && baseGoodsVO.getState() != GoodsState.SOLD_OUT) {
            throw new OrderException(GOODS_NOT_AVAILABLE);
        }

        if (baseGoodsVO.getPrice().compareTo(request.getItemPrice()) != 0) {
            throw new OrderException(GOODS_PRICE_CHANGED);
        }
    }

    public GoodsValidator(GoodsFacadeService goodsFacadeService) {
        this.goodsFacadeService = goodsFacadeService;
    }

    public GoodsValidator() {
    }
}


/**
 * 3.库存校验器
 *
 * @author hollis
 */
public class StockValidator extends BaseOrderCreateValidator {

    private InventoryFacadeService inventoryFacadeService;

    @Override
    public void doValidate(OrderCreateRequest request) throws OrderException {

        InventoryRequest inventoryRequest = new InventoryRequest();
        inventoryRequest.setGoodsId(request.getGoodsId());
        inventoryRequest.setGoodsType(request.getGoodsType());
        inventoryRequest.setIdentifier(request.getIdentifier());
        inventoryRequest.setInventory(request.getItemCount());

        SingleResponse<Integer> response = inventoryFacadeService.queryInventory(inventoryRequest);

        if (!response.getSuccess()) {
            throw new OrderException(INVENTORY_NOT_ENOUGH);
        }

        Integer inventory = response.getData();

        if (inventory == 0) {
            throw new OrderException(INVENTORY_NOT_ENOUGH);
        }

        if (inventory < request.getItemCount()) {
            throw new OrderException(INVENTORY_NOT_ENOUGH);
        }
    }

    public StockValidator(InventoryFacadeService inventoryFacadeService) {
        this.inventoryFacadeService = inventoryFacadeService;
    }

    public StockValidator() {
    }
}


/**
 * 4.用户校验器
 *
 * @author hollis
 */
public class UserValidator extends BaseOrderCreateValidator {

    private UserFacadeService userFacadeService;

    @Override
    public void doValidate(OrderCreateRequest request) throws OrderException {
        String buyerId = request.getBuyerId();
        UserQueryRequest userQueryRequest = new UserQueryRequest(Long.valueOf(buyerId));
        UserQueryResponse<UserInfo> userQueryResponse = userFacadeService.query(userQueryRequest);
        if (userQueryResponse.getSuccess() && userQueryResponse.getData() != null) {
            UserInfo userInfo = userQueryResponse.getData();
            if (userInfo.getUserRole() != null && !userInfo.getUserRole().equals(UserRole.CUSTOMER)) {
                throw new OrderException(BUYER_IS_PLATFORM_USER);
            }
            //判断买家状态
            if (userInfo.getState() != null && !userInfo.getState().equals(UserStateEnum.ACTIVE.name())) {
                throw new OrderException(BUYER_STATUS_ABNORMAL);
            }
            //判断买家状态
            if (userInfo.getState() != null && !userInfo.getCertification()) {
                throw new OrderException(BUYER_NOT_AUTH);
            }
        }
    }

    public UserValidator(UserFacadeService userFacadeService) {
        this.userFacadeService = userFacadeService;
    }

    public UserValidator() {
    }
}

4.校验器配置

/**
 * 1.订单创建校验器配置
 *
 * @author hollis
 */
@Configuration
public class OrderCreateAndConfirmValidatorConfig {

    @Autowired
    private GoodsValidator goodsValidator;

    @Autowired
    private UserValidator userValidator;

    @Bean
    public OrderCreateValidator orderConfirmValidatorChain() {
        userValidator.setNext(goodsValidator);
        return userValidator;
    }
}


/**
 * 2.订单创建校验器配置
 *
 * @author hollis
 */
@Configuration
public class OrderCreateValidatorConfig {

    @Autowired
    private GoodsValidator goodsValidator;

    @Autowired
    private UserValidator userValidator;

    @Autowired
    private GoodsBookValidator goodsBookValidator;

    @Bean
    public OrderCreateValidator orderValidatorChain() {
        userValidator.setNext(goodsValidator);
        goodsValidator.setNext(goodsBookValidator);
        return userValidator;
    }
}


/**
 * 3.订单创建前置校验器配置
 *
 * @author hollis
 */
@Configuration
public class OrderPreValidatorConfig {

    @Autowired
    private GoodsValidator goodsValidator;

    @Autowired
    private UserValidator userValidator;

    @Autowired
    private GoodsBookValidator goodsBookValidator;

    @Autowired
    private StockValidator stockValidator;

    @Bean
    public OrderCreateValidator orderPreValidatorChain() {
        userValidator.setNext(goodsValidator);
        goodsValidator.setNext(stockValidator);
        stockValidator.setNext(goodsBookValidator);
        return userValidator;
    }

}

5.使用

    // 1.注入
    @Autowired
    private OrderCreateValidator orderPreValidatorChain;
    @Autowired
    private OrderCreateValidator orderConfirmValidatorChain;
    @Autowired
    private OrderCreateValidator orderPreValidatorChain;
  
    // 2.1 使用示例举例
	    /**
     * 秒杀下单(不基于inventory hint的实现),热点商品
     *
     * @param
     * @return 幂等号
     */
    @PostMapping("/newBuy")
    public Result<String> newBuy(@Valid @RequestBody BuyParam buyParam) {
        OrderCreateRequest orderCreateRequest = null;

        try {
            orderCreateRequest = getOrderCreateRequest(buyParam);
            orderPreValidatorChain.validate(orderCreateRequest);

            //消息监听:NewBuyMsgListener or NewBuyBatchMsgListener
            boolean result = streamProducer.send("newBuy-out-0", buyParam.getGoodsType(), JSON.toJSONString(orderCreateRequest));

            if (!result) {
                throw new TradeException(TradeErrorCode.ORDER_CREATE_FAILED);
            }

            //因为不管本地事务是否成功,只要一阶段消息发成功都会返回 true,所以这里需要确认是否成功
            //因为上面是用了MQ的事务消息,Redis的库存扣减是在事务消息的本地事务中同步执行的(InventoryDecreaseTransactionListener#executeLocalTransaction),所以只要成功了,这里一定能查到
            InventoryRequest inventoryRequest = new InventoryRequest(orderCreateRequest);
            SingleResponse<String> response = inventoryFacadeService.getInventoryDecreaseLog(inventoryRequest);

            if (response.getSuccess() && response.getData() != null) {
                inventoryBypassVerify(inventoryRequest);
                return Result.success(orderCreateRequest.getOrderId());
            }
        } catch (OrderException | TradeException e) {
            return Result.error(e.getErrorCode().getCode(), e.getErrorCode().getMessage());
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        return Result.error(TradeErrorCode.ORDER_CREATE_FAILED.getCode(), TradeErrorCode.ORDER_CREATE_FAILED.getMessage());
    }
  
    // 2.2
	@Override
    @DistributeLock(keyExpression = "#request.identifier", scene = "ORDER_CREATE")
    @Facade
    public OrderResponse createAndConfirm(OrderCreateAndConfirmRequest request) {
        try {
            orderConfirmValidatorChain.validate(request);
        } catch (OrderException e) {
            return new OrderResponse.OrderResponseBuilder().orderId(request.getOrderId()).buildFail(ORDER_CREATE_VALID_FAILED.getCode(), e.getErrorCode().getMessage());
        }
        GoodsSaleRequest goodsSaleRequest = new GoodsSaleRequest(request);

        GoodsSaleResponse response = goodsFacadeService.saleWithoutHint(goodsSaleRequest);

        if (!response.getSuccess()) {
            return new OrderResponse.OrderResponseBuilder().buildFail(response.getResponseMessage(), response.getResponseCode());
        }
        return orderService.createAndConfirm(request);
    }
  
    // 2.3
    @Override
    @DistributeLock(keyExpression = "#request.identifier", scene = "ORDER_CREATE")
    @Facade
    public OrderResponse create(OrderCreateRequest request) {
        try {
            orderValidatorChain.validate(request);
        } catch (OrderException e) {
            return new OrderResponse.OrderResponseBuilder().buildFail(ORDER_CREATE_VALID_FAILED.getCode(), e.getErrorCode().getMessage());
        }

        InventoryRequest inventoryRequest = new InventoryRequest(request);
        SingleResponse<Boolean> decreaseResult = inventoryFacadeService.decrease(inventoryRequest);

        if (decreaseResult.getSuccess()) {
            return orderService.createAndAsyncConfirm(request);
        }
        throw new OrderException(OrderErrorCode.INVENTORY_DECREASE_FAILED);
    }

    // 2.4 ...