业务场景
最最近项目中有这样的一个业务场景:
用户下单->管理员审核->配送员接单->配送中->送达–>签收->完成
整个业务以这种流程的形式存在,每个流程状态的业务不一样,考虑到多种状态如果直接写一个接口肯定会嵌套太多的if else,于是这里使用了策略模式。
对策略模式的基础这里不做概述
接口设计
策略行为接口的设计
public interface OrderTraceChangeStrategy {
/**
* 用于判断策略是否支持
*
* @param traceDto
* @param wxPreOrder
* @return
*/
boolean support(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder);
/**
* 业务参数校验
*
* @param traceDto
* @param wxPreOrder
* @return
*/
void check(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder);
/**
* 状态变操作
*
* @param traceDto
* @param wxPreOrder
*/
void change(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder);
}
定义了一个接口,该接口提供了三个方法:support、check、change。
support方法用于判断策略是否支持
check方法用于对业务参数进行校验
change方法用于执行状态转换以及其他业务操作操作
策略实现类的设计
这里列举一个实现类
@Slf4j
@Component
public class AuditRefuseStrategyImpl implements OrderTraceChangeStrategy {
@Resource
private WxPreOrderService wxPreOrderService;
@Override
public boolean support(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder) {
return traceDto.getAfterPreOrderStatusEnum()==PreOrderStatusEnum.REJECT_REQ;
}
@Override
public void check(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder) {
//当前订单为待审核才可以拒单
Assert.isTrue(wxPreOrder.getOrderStatus().equals(PreOrderStatusEnum.WAIT_CHECK.getCode()), WeiXinError.COMMON_ERROR, "当前订单状态非待审核状态,不支持拒单");
}
@Override
@Transactional(rollbackFor = Throwable.class)
public void change(WxPreOrderTraceDto traceDto, WxPreOrder wxPreOrder) {
//更新主表数据
WxPreOrder wxPreOrderUpdate = new WxPreOrder();
wxPreOrderUpdate.setId(wxPreOrder.getId());
//变更为拒单状态
wxPreOrderUpdate.setOrderStatus(PreOrderStatusEnum.REJECT_REQ.getCode());
wxPreOrderUpdate.setRejectReqReason(traceDto.getRemark());
wxPreOrderService.updateById(wxPreOrderUpdate);
log.info("订单状态流转,订单id:{},审核拒绝:{}------------>{}", wxPreOrder.getId(), wxPreOrder.getOrderStatus(), traceDto.getAfterPreOrderStatusEnum().getCode());
}
}
业务层的应用
@Autowired
private List<OrderTraceChangeStrategy> orderTraceChangeStrategy;
@Override
@Transactional(rollbackFor = Throwable.class)
public void traceOrderStatus(WxPreOrderTraceDto traceDto) {
//查询当前订单信息
WxPreOrder preOrder = wxPreOrderService.getById(traceDto.getOrderId());
Assert.isNotNull(preOrder, WeiXinError.COMMON_ERROR, "预订单不存在");
for (OrderTraceChangeStrategy item : orderTraceChangeStrategy) {
//获取支持的策略
boolean support = item.support(traceDto, preOrder);
if (support) {
//参数校验
item.check(traceDto, preOrder);
//业务处理
item.change(traceDto, preOrder);
//公共业务保存订单状态变更流水信息
WxPreOrderSaveFlowDto saveFlowDto = WxPreOrderSaveFlowDto.builder().preStatus(preOrder.getOrderStatus()).afterStatus(traceDto.getAfterPreOrderStatusEnum().getCode()).orderId(preOrder.getId()).build();
saveOrderFlow(saveFlowDto);
//处理完业务直接返回
return;
}
}
//上面已经return了 如果走到这里说明前端传递的参数没有和任何策略命中,直接抛出异常
Assert.Error( WeiXinError.COMMON_ERROR, "错误的订单状态");
}
重点来了:这里使用一次注入多个策略的方式,直接注入到容器一个集合,对于这些集合中的策略执行哪一个,使用
循环判断的方式。
接受参数使用枚举的方式,使用枚举对应类型强校验,不传参会相应前端页面400。
关于枚举在项目中的使用骚操作,后面的文章还会更新。请持续关注,保证你眼前一亮。
策略业务对应的入参:
Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class WxPreOrderTraceDto {
@ApiModelProperty("订单ID")
@NotNull(message = "订单ID不能为空")
private Long orderId;
/**
* 使用枚举强校验,前端传错会报400
*/
@ApiModelProperty("订单变更后的状态")
@NotNull(message = "流程变更状态不能为空")
private PreOrderStatusEnum afterPreOrderStatusEnum;
@ApiModelProperty("原因,备注")
private String remark;
这样就实现了不同流程状态的业务拆分,即使再多的状态也不用担心代码无法维护了。
其实还有一个需要解释一下,在业务层,下面这些代码,其实属于设计模式中的模板方法,将这些业务统一抽取成一个
模版,执行类似的业务,也可以在抽象类中去继续封装这些算法行为。这里暂时不做赘述。
//参数校验
item.check(traceDto, preOrder);
//业务处理
item.change(traceDto, preOrder);
//公共业务保存订单状态变更流水信息
WxPreOrderSaveFlowDto saveFlowDto = WxPreOrderSaveFlowDto.builder().preStatus(preOrder.getOrderStatus()).afterStatus(traceDto.getAfterPreOrderStatusEnum().getCode()).orderId(preOrder.getId()).build();
saveOrderFlow(saveFlowDto);
//处理完业务直接返回
总结一下
- 定义策略接口,定义不同行为
- 通过List批量注入不同策略
- 通过不同状态枚举判断不同状态的业务,获取不同状态的策略类
- 通过模板方法,抽取公共业务