责任链模式实现订单校验
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 ...