昨天刚写了 可编排策略在交易系统的应用,今天小伙伴就给我提了个需求。
背景
最近由于新业务的需要,需要对原有工单的推单逻辑进行变动。我是想着尽可能不对原有逻辑进行改动,毕竟业务还一直在跑,就开启了一个小模块进行代码的组合。
在设计评审中,我一直强调要设计的灵活些。
校验规则一定要原子化,然后可编排在任何一处场景。校验的的入参和出参必须抽取,并标准化。
直接把我昨天写的文章扔过去了。
然后就差一个状态机了,得我就把框架搭起来。
设计
工单的流转完全可以状态驱动,如果发现两个状态都指向了一个处理逻辑,那就说明,你需要加一个状态。本着这个原则开始设计。
执行策略的实现
首先定义工单状态策略
/**
* 工单状态流转策略
* @Author: yxkong
* @Date: 2022/8/23 11:28 AM
* @version: 1.0
*/
public interface OrderStateStrategy {
/**
* 状态流转前置校验
* @param context
* @return
*/
OrderStateResult check(OrderStateContext context);
/**
* 状态流转统一入口
* 执行的模板
* @param context
* @return
*/
OrderStateResult execute(OrderStateContext context);
/**
* 状态流转处理器,由各个策略实现
* 如果需要重试,在code = -1的时候,retry = true
* @param context
* @return
*/
OrderStateResult handler(OrderStateContext context);
/**
* 状态流转后置处理
* @param context
* @param rst
* @return
*/
boolean after(OrderStateContext context, OrderStateResult rst);
}
进行模板方法封装,将整个公共部分抽取
/**
* 公共处理
* @Author: yxkong
* @Date: 2022/8/23 11:47 AM
* @version: 1.0
*/
public abstract class AbstractOrderStateStrategy implements OrderStateStrategy {
@Override
public OrderStateResult check(OrderStateContext context) {
/**
* 此处公共校验
* 根据工单id查询工单的状态
*/
OrderStateEnum dbStatus = OrderStateEnum.init;
if (context.getCurrentState() != dbStatus){
return new OrderStateResult(null,false,CodeStateEnum.fail,"当前状态不匹配");
}
return new OrderStateResult(CodeStateEnum.succ,"校验通过!");
}
@Override
public OrderStateResult execute(OrderStateContext context) {
OrderStateResult check = check(context);
if (!check.isSucc()){
/**
* 这块埋点,因为什么拦截的
*/
return check ;
}
OrderStateResult handler = handler(context);
//执行后的统一处理
boolean rst = this.after(context, handler);
if (!rst){
/**
* 要保证最终一致性,这里业务已经处理完了
* 记录日志或者,推入消息队列做补偿
*/
}
return handler;
}
@Override
public boolean after(OrderStateContext context, OrderStateResult rst) {
/**
* 统一发送领域事件
* 统一记录日志,失败也需要记录日志
* 处理成功,返回true,处理失败,返回false,需要补偿
*/
return false;
}
}
具体的策略实现
/**
* 关单策略实现
* @Author: yxkong
* @Date: 2022/8/23 12:16 PM
* @version: 1.0
*/
@Service("closeOrderStrategy")
public class CloseOrderStrategyImpl extends AbstractOrderStateStrategy {
@Override
public OrderStateResult handler(OrderStateContext context) {
return null;
}
}
/**
* 推单策略实现
* @Author: yxkong
* @Date: 2022/8/23 11:59 AM
* @version: 1.0
*/
@Service("pushOrderStrategy")
public class PushOrderStrategyImpl extends AbstractOrderStateStrategy {
@Override
public OrderStateResult check(OrderStateContext context) {
/**
* 定制化拦截
* 比如,查询推单开关是否开启,重试的次数
*/
String closeSwitch = "open";
if ("close".equals(closeSwitch)){
return new OrderStateResult(CodeStateEnum.fail,"推单开关关闭,不允许推单");
}
return super.check(context);
}
@Override
public OrderStateResult handler(OrderStateContext context) {
/**
* 推单的处理逻辑
*/
return null;
}
}
/**
* 重新路由策略实现
* @Author: yxkong
* @Date: 2022/8/23 12:10 PM
* @version: 1.0
*/
@Service("reRouteStrategy")
public class ReRouteStrategyImpl extends AbstractOrderStateStrategy {
@Override
public OrderStateResult handler(OrderStateContext context) {
/**
* 路由到具体的机构后,将数据库工单改成推单状态
*/
return new OrderStateResult(OrderStateEnum.pass, CodeStateEnum.succ,"路由成功");
}
}
最后类结构如下:
状态机的实现
状态机接口定义
/**
* 状态机服务
* @Author: yxkong
* @Date: 2022/8/23 5:40 PM
* @version: 1.0
*/
public interface OrderStateMachineService {
/**
* 状态机执行
* @param context
* @return
*/
OrderStateResult execute(OrderStateContext context);
}
状态机实现
/**
* 工单状态机
* @Author: yxkong
* @Date: 2022/8/23 12:32 PM
* @version: 1.0
*/
@Service
public class OrderStateMachineServiceImpl implements OrderStateMachineService {
@Autowired
private Map<String, OrderStateStrategy> map;
/**
* 状态机执行
* @param context
* @return
*/
@Override
public OrderStateResult execute(OrderStateContext context){
OrderStateEnum currentState = context.getCurrentState();
if ( OrderStateEnum.isFinalState(currentState)){
//终态不处理,直接返回
return null;
}
OrderStateMappingEnum mapping = OrderStateMappingEnum.of(currentState.getState());
if (mapping == null){
throw new RuntimeException("暂不支持该状态的处理"+ currentState);
}
//获取对应状态的处理策略
OrderStateStrategy stateStrategy = map.get(mapping.getHandlerBean());
//执行具体的策略
OrderStateResult execute = stateStrategy.execute(context);
//是否需要递归调用
if (execute.isExecute()){
/**
* 一个状态执行完,重新赋值状态
*/
context.setCurrentState(execute.getOrderStatus());
context.setRetry(execute.isRetry());
return this.execute(context);
}
return execute;
}
}
在这里利用spring的DI机制,将同类型的注入到map里,方便使用
同时利用枚举来封装状态与处理器的关系。
枚举类:
/**
* 状态与处理映射
* @author yxkong
*/
public enum OrderStateMappingEnum {
closeOrder(5,"closeOrderStrategy","关单"),
reroute(1,"reRouteStrategy","重新路由"),
retry(2,"retryStrategy", "重试"),
blockOrder(3,"blockOrderStrategy", "卡单");
private int state;
private String handlerBean;
private String desc;
OrderStateMappingEnum(int state, String handlerBean, String desc) {
this.state = state;
this.handlerBean = handlerBean;
this.desc = desc;
}
public int getState() {
return state;
}
public String getHandlerBean() {
return handlerBean;
}
public String getDesc() {
return desc;
}
/**
* 获取枚举
*
* @param state 枚举值
* @return OrderStateMappingEnum
*/
public static OrderStateMappingEnum of(final int state) {
for (final OrderStateMappingEnum s : OrderStateMappingEnum.values()) {
if (Objects.equals(s.state, state)) {
return s;
}
}
return null;
}
}
/**
* 状态枚举
* @Author: yxkong
* @Date: 2022/8/23 11:41 AM
* @version: 1.0
*/
public enum OrderStateEnum {
init(0,"初始化工单"),
waitingClose(8,"等待关闭"),
waitingRoute(2,"等待路由"),
refuse(6,"工单拒绝"),
pass(5,"工单通过");
private int state;
private String desc;
private OrderStateEnum(int state, String desc) {
this.state = state;
this.desc = desc;
}
public static OrderStateEnum of(final int state) {
for (final OrderStateEnum s : OrderStateEnum.values()) {
if (Objects.equals(s.state, state)) {
return s;
}
}
return null;
}
/**
* 是否终态
* @param state
* @return
*/
public static boolean isFinalState(OrderStateEnum state){
return OrderStateEnum.refuse.equals(state) || OrderStateEnum.pass.equals(state);
}
public int getState() {
return state;
}
public String getDesc() {
return desc;
}
}
/**
* 返回code枚举
* @Author: yxkong
* @Date: 2022/8/23 5:56 PM
* @version: 1.0
*/
public enum CodeStateEnum {
succ(1,"成功"),
fail(0,"失败"),
error(-1,"异常");
private int code;
private String desc;
private CodeStateEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static CodeStateEnum of(final int code) {
for (final CodeStateEnum s : CodeStateEnum.values()) {
if (Objects.equals(s.code, code)) {
return s;
}
}
return null;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
上下文与出参
/**
* 工单状态上下文
* @Author: yxkong
* @Date: 2022/8/23 11:28 AM
* @version: 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderStateContext {
/**工单id*/
private String appId;
/**当前状态*/
private OrderStateEnum currentState;
/**重试*/
private boolean retry;
}
/**
* 状态流转返回结果
*
* @Author: yxkong
* @Date: 2022/8/23 11:30 AM
* @version: 1.0
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class OrderStateResult {
/**执行后的工单状态*/
private OrderStateEnum orderStatus;
/**是否重试*/
private boolean retry ;
/**当前执行状态码 1成功,0失败,-1 异常*/
private CodeStateEnum code;
/**当前执行结果描述*/
private String message;
public OrderStateResult(CodeStateEnum code, String message) {
this.code = code;
this.message = message;
}
public OrderStateResult(OrderStateEnum orderStatus, CodeStateEnum code, String message) {
this.orderStatus = orderStatus;
this.code = code;
this.message = message;
}
/**
* 是否成功
* @return
*/
public boolean isSucc(){
return this.code == CodeStateEnum.succ;
}
/**
* 是否继续执行
* @return
*/
public boolean isExecute(){
if (CodeStateEnum.succ == this.code || (CodeStateEnum.error == this.code && retry)){
return Boolean.TRUE;
}
return Boolean.FALSE;
}
}
整个代码结构如下:
这么一来,我们只需要关心具体的业务逻辑就行了,状态不够就加状态,入口统一,关系映射在一处,也方便排查问题。
这里不像上一篇里的规则,定义以后可以用在多个地方,每个地方,我都可以编排。
这里就是一对一的映射。
文章评论