1.2 四大概念
2.1 DSL
DSL是一种工具,它的核心价值在于,它提供了一种手段,可以更加清晰地就系统某部分的意图进行沟通。
这种清晰并非只是审美追求。一段代码越容易看懂,就越容易发现错误,也就越容易对系统进行修改。因此,我们鼓励变量名要有意义,文档要写清楚,代码结构要写清晰。基于同样的理由,我们应该也鼓励采用DSL。
2.2 DSL的分类
builder.externalTransition().from(States.STATE1).to(States.STATE2).on(Events.EVENT1).when(checkCondition()).perform(doAction());
2.3.1 内部DSL示例
def s = new StringWriter()def xml = new MarkupBuilder(s)xml.html{head{title("Hello - DSL")script(ahref:"https://xxxx.com/vue.js")meta(author:"marui116")}body{p("JD-ILT-ITMS")}}println s.toString()
<html><head><title>Hello - DSL</title><script ahref='https://xxxx.com/vue.js' /><meta author='marui116' /></head><body><p>JD-ILT-ITMS</p></body></html>
A helper class for creating XML or HTML markup. The builder supports various 'pretty printed' formats.Example:new MarkupBuilder().root {a( a1:'one' ) {b { mkp.yield( '3 < 5' ) }c( a2:'two', 'blah' )}}Will print the following to System.out:<root><a a1='one'><b>3 < 5</b><c a2='two'>blah</c></a></root>
2.3.2 外部DSL
2.3.3 DSL & DDD(领域驱动)
3.1 Spring Statemachine
-
Easy to use flat one level state machine for simple use cases.(易于使用的扁平单级状态机,用于简单的使用案例。) -
Hierarchical state machine structure to ease complex state configuration.(分层状态机结构,以简化复杂的状态配置。) -
State machine regions to provide even more complex state configurations.(状态机区域提供更复杂的状态配置。) -
Usage of triggers, transitions, guards and actions.(使用触发器、transitions、guards和actions。) -
Type safe configuration adapter.(应用安全的配置适配器。) -
Builder pattern for easy instantiation for use outside of Spring Application context(用于在Spring Application上下文之外使用的简单实例化的生成器模式) -
Recipes for usual use cases(通常用例的手册) -
Distributed state machine based on a Zookeeper State machine event listeners.(基于Zookeeper的分布式状态机状态机事件监听器。) -
UML Eclipse Papyrus modeling.(UML Eclipse Papyrus 建模) -
Store machine config in a persistent storage.(存储状态机配置到持久层) -
Spring IOC integration to associate beans with a state machine.(Spring IOC集成将bean与状态机关联起来)
3.2 COLA状态机DSL实现
-
State:状态 -
Event:事件,状态由事件触发,引起变化 -
Transition:流转,表示从一个状态到另一个状态 -
External Transition:外部流转,两个不同状态之间的流转 -
Internal Transition:内部流转,同一个状态之间的流转 -
Condition:条件,表示是否允许到达某个状态 -
Action:动作,到达某个状态之后,可以做什么 -
StateMachine:状态机
4.1 Spring状态机示例
<dependency><groupId>org.springframework.statemachine</groupId><artifactId>spring-statemachine-core</artifactId><version>3.2.0</version></dependency>
4.1.1 构造状态机
4jpublic class SimpleStateMachineConfiguration extends StateMachineConfigurerAdapter<String, String> {/*** 定义初始节点、结束节点和状态节点* @param states the {@link StateMachineStateConfigurer}* @throws Exception*/public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {states.withStates().initial("SI").end("SF").states(new HashSet<String>(Arrays.asList("S1", "S2", "S3")));}/*** 配置状态节点的流向和事件* @param transitions the {@link StateMachineTransitionConfigurer}* @throws Exception*/public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {transitions.withExternal().source("SI").target("S1").event("E1").action(initAction()).and().withExternal().source("S1").target("S2").event("E2").action(s1Action()).and().withExternal().source("S2").target("SF").event("end");}/*** 初始节点到S1* @return*/public Action<String, String> initAction() {return ctx -> log.info("Init Action -- DO: {}", ctx.getTarget().getId());}/*** S1到S2* @return*/public Action<String, String> s1Action() {return ctx -> log.info("S1 Action -- DO: {}", ctx.getTarget().getId());}}
public class StateMachineListener extends StateMachineListenerAdapter<String, String> {public void stateChanged(State from, State to) {log.info("Transitioned from {} to {}", from == null ? "none" : from.getId(), to.getId());}}
4.1.3 状态机配置
4jpublic class StateMachineConfig implements WebMvcConfigurer {private StateMachine<String, String> stateMachine;private StateMachineListener stateMachineListener;public void init() {stateMachine.addStateListener(stateMachineListener);}}
4.1.4 接口示例
4.1.4.1 获取状态机状态列表
@RequestMapping("info")public String info() {return StringUtils.collectionToDelimitedString(stateMachine.getStates().stream().map(State::getId).collect(Collectors.toList()),",");}
4.1.4.2 状态机开启
public String start() {stateMachine.startReactively().block();return state();}
4.1.4.3 事件操作
("event")public String event((name = "event") String event) {Message<String> message = MessageBuilder.withPayload(event).build();return stateMachine.sendEvent(Mono.just(message)).blockLast().getMessage().getPayload();}
4.1.4.4 获取状态机当前状态
@GetMapping("state")public String state() {return Mono.defer(() -> Mono.justOrEmpty(stateMachine.getState().getId())).block();}
4.1.4.5 一次状态转换的控制台输出
: Completed initialization in 0 ms: Transitioned from none to SI: Init Action -- DO: S1: Transitioned from SI to S1: S1 Action -- DO: S2: Transitioned from S1 to S2: Transitioned from S2 to SF
4.2 COLA状态机示例
4.2.1 构造状态机
StateMachineBuilder<TransNeedStatusEnum, TransNeedEventEnum, Context> builder = StateMachineBuilderFactory.create();// 接单后,运输需求单生成运输规划单builder.externalTransition().from(None).to(UN_ASSIGN_CARRIER).on(Create_Event).when(checkCondition()).perform(doAction());// 运输规划单生成调度单,调度单绑定服务商builder.externalTransition().from(UN_ASSIGN_CARRIER).to(UN_ASSIGN_CAR).on(Assign_Carrier_Event).when(checkCondition()).perform(doAction());// 服务商分配车辆、司机builder.externalTransition().from(UN_ASSIGN_CAR).to(ASSIGNED_CAR).on(Assign_Car_Event).when(checkCondition()).perform(doAction());// 货物揽收builder.externalTransition().from(ASSIGNED_CAR).to(PICKUPED).on(Trans_Job_Status_Change_Event).when(checkCondition()).perform(doAction());// 揽收货物更新到运输中builder.externalTransition().from(ASSIGNED_CAR).to(IN_TRANSIT).on(Trans_Job_Status_Change_Event).when(checkCondition()).perform(doAction());// 运输中更新到过海关builder.externalTransition().from(IN_TRANSIT).to(PASS_CUSTOMS).on(Trans_Job_Status_Change_Event)// 检查是否需要过海关.when(isTransNeedPassCustoms()).perform(doAction());// 妥投builder.externalTransition().from(PASS_CUSTOMS).to(ALL_DELIVERIED).on(All_Delivery_Event).when(checkCondition()).perform(doAction());// 车辆揽收、运输、过海关的运输状态,都可以直接更新到妥投Stream.of(PICKUPED, IN_TRANSIT, PASS_CUSTOMS).forEach(status ->builder.externalTransition().from(status).to(ALL_DELIVERIED).on(Trans_Job_Status_Change_Event).when(checkCondition()).perform(doAction()));// 待分配、待派车、已派车可取消Stream.of(UN_ASSIGN_CARRIER, UN_ASSIGN_CAR, ASSIGNED_CAR).forEach(status ->builder.externalTransition().from(status).to(CANCELED).on(Order_Cancel_Event).when(checkCondition()).perform(doAction()));// 妥投、和取消可结束归档Stream.of(ALL_DELIVERIED, CANCELED).forEach(status ->builder.externalTransition().from(status).to(FINISH).on(Order_Finish).when(checkCondition()).perform(doAction()));stateMachine = builder.build("TransNeedStatusMachine");
@startumlNone --> UN_ASSIGN_CARRIER : Create_EventUN_ASSIGN_CARRIER --> UN_ASSIGN_CAR : Assign_Carrier_EventUN_ASSIGN_CAR --> ASSIGNED_CAR : Assign_Car_EventASSIGNED_CAR --> CANCELED : Order_Cancel_EventASSIGNED_CAR --> PICKUPED : Trans_Job_Status_Change_EventASSIGNED_CAR --> IN_TRANSIT : Trans_Job_Status_Change_EventIN_TRANSIT --> PASS_CUSTOMS : Trans_Job_Status_Change_EventPASS_CUSTOMS --> ALL_DELIVERIED : Trans_Job_Status_Change_EventPASS_CUSTOMS --> ALL_DELIVERIED : All_Delivery_EventIN_TRANSIT --> ALL_DELIVERIED : Trans_Job_Status_Change_EventALL_DELIVERIED --> FINISH : Order_FinisUN_ASSIGN_CAR --> CANCELED : Order_Cancel_EventUN_ASSIGN_CARRIER --> CANCELED : Order_Cancel_EventPICKUPED --> ALL_DELIVERIED : Trans_Job_Status_Change_EventCANCELED --> FINISH : Order_Finis@enduml
4.2.2 状态机事件处理
/*** 一种是通过Event来进行事件分发,不同Event通过EventBus走不同的事件响应* 另一种是在构造状态机时,直接配置不同的Action* @return*/private Action<TransNeedStatusEnum, TransNeedEventEnum, Context> doAction() {log.info("do action");return (from, to, event, ctx) -> {log.info(ctx.getUserName()+" is operating trans need bill "+ctx.getTransNeedId()+" from:"+from+" to:"+to+" on:"+event);if (from != None) {TransNeed transNeed = ctx.getTransNeed();transNeed.setStatus(to.name());transNeed.setUpdateTime(LocalDateTime.now());transNeedService.update(transNeed);}eventBusService.invokeEvent(event, ctx);};}
/*** @author marui116* @version 1.0.0* @className TransNeedAssignCarrierEvent* @description TODO* @date 2023/3/28 11:08*/(event = TransNeedEventEnum.Assign_Carrier_Event)4jpublic class TransNeedAssignCarrierEvent implements EventComponent {public void invokeEvent(Context context) {log.info("分配了服务商,给服务商发邮件和短信,让服务商安排");}}
/*** @author marui116* @version 1.0.0* @className TransNeedAssignCarEvent* @description TODO* @date 2023/3/28 11:05*/(event = TransNeedEventEnum.Assign_Car_Event)4jpublic class TransNeedAssignCarEvent implements EventComponent {public void invokeEvent(Context context) {log.info("分配了车辆信息,给运单中心发送车辆信息");}}
/*** @author marui116* @version 1.0.0* @className EventServiceImpl* @description TODO* @date 2023/3/28 10:57*/public class EventBusServiceImpl implements EventBusService {private ApplicationContextUtil applicationContextUtil;private Map<TransNeedEventEnum, EventComponent> eventComponentMap = new ConcurrentHashMap<>();private void init() {ApplicationContext context = applicationContextUtil.getApplicationContext();Map<String, EventComponent> eventBeanMap = context.getBeansOfType(EventComponent.class);eventBeanMap.values().forEach(event -> {if (event.getClass().isAnnotationPresent(EventAnnonation.class)) {EventAnnonation eventAnnonation = event.getClass().getAnnotation(EventAnnonation.class);eventComponentMap.put(eventAnnonation.event(), event);}});}public void invokeEvent(TransNeedEventEnum eventEnum, Context context) {if (eventComponentMap.containsKey(eventEnum)) {eventComponentMap.get(eventEnum).invokeEvent(context);}}}
4.2.3 状态机上下文
public class Context {private String userName;private Long transNeedId;private TransNeed transNeed;}
4.2.4 状态枚举
public enum TransNeedStatusEnum {/*** 开始状态*/None,/*** 待分配陆运服务商*/UN_ASSIGN_CARRIER,/*** 待分配车辆和司机*/UN_ASSIGN_CAR,/*** 订单已处理,已安排司机提货*/ASSIGNED_CAR,/*** 已完成提货*/PICKUPED,/*** 运输中*/IN_TRANSIT,/*** 已通过内地海关*/PASS_CUSTOMS,/*** 您的货物部分妥投部分投递失败*/PARTIAL_DELIVERIED,/*** 您的货物妥投*/ALL_DELIVERIED,/*** 您的货物被拒收*/ALL_REJECTED,/*** 委托订单被取消*/CANCELED,/*** 单据结束归档*/FINISH;}
4.2.5 事件枚举
public enum TransNeedEventEnum {// 系统事件Create_Event,Normal_Update_Event,/*** 分配服务商事件*/Assign_Carrier_Event,/*** 派车事件*/Assign_Car_Event,// 车辆任务(trans_jbo)执行修改调度单(trans_task)状态的事件Trans_Job_Status_Change_Event,// 派送事件Partial_Delivery_Event,All_Delivery_Event,Partial_Reject_Event,All_Reject_Event,// 调度单中的任务单取消事件Order_Cancel_Event,// 单据结束Order_Finish;public boolean isSystemEvent() {return this == Create_Event ||this == Normal_Update_Event;}}
4.2.6 接口Demo
4.2.6.1 创建需求单
/*** 接单* @return*/public Context start( String fsNo, String remark) {Context context = contextService.getContext();Object newStatus = stateMachine.getStateMachine().fireEvent(TransNeedStatusEnum.None, TransNeedEventEnum.Create_Event, context);TransNeed transNeed = transNeedService.createTransNeed(fsNo, remark, newStatus.toString());context.setTransNeed(transNeed);context.setTransNeedId(transNeed.getId());return context;}
4.2.6.2 分配服务商
/*** 运输规划单生成调度单,调度单绑定服务商*/public Context assignCarrier( Long id) {Context context = contextService.getContext(id);TransNeedStatusEnum prevStatus = TransNeedStatusEnum.valueOf(context.getTransNeed().getStatus());stateMachine.getStateMachine().fireEvent(prevStatus, TransNeedEventEnum.Assign_Carrier_Event, context);return context;}
4.2.6.3 分配车辆
public Context assignCar( Long id) {Context context = contextService.getContext(id);TransNeedStatusEnum prevStatus = TransNeedStatusEnum.valueOf(context.getTransNeed().getStatus());log.info("trans need id: {}, prev status: {}", id, prevStatus);stateMachine.getStateMachine().fireEvent(prevStatus, TransNeedEventEnum.Assign_Car_Event, context);return context;}


