DSL是一种工具,它的核心价值在于,它提供了一种手段,可以更加清晰地就系统某部分的意图进行沟通。
这种清晰并非只是审美追求。一段代码越容易看懂,就越容易发现错误,也就越容易对系统进行修改。因此,我们鼓励变量名要有意义,文档要写清楚,代码结构要写清晰。基于同样的理由,我们应该也鼓励采用DSL。
builder.externalTransition()
.from(States.STATE1)
.to(States.STATE2)
.on(Events.EVENT1)
.when(checkCondition())
.perform(doAction());
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>
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-core</artifactId>
<version>3.2.0</version>
</dependency>
4j
public 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());
}
}
4j
public class StateMachineConfig implements WebMvcConfigurer {
private StateMachine<String, String> stateMachine;
private StateMachineListener stateMachineListener;
public void init() {
stateMachine.addStateListener(stateMachineListener);
}
}
@RequestMapping("info")
public String info() {
return StringUtils.collectionToDelimitedString(
stateMachine.getStates()
.stream()
.map(State::getId)
.collect(Collectors.toList()),
",");
}
public String start() {
stateMachine.startReactively().block();
return state();
}
"event") (
public String event( (name = "event") String event) {
Message<String> message = MessageBuilder.withPayload(event).build();
return stateMachine.sendEvent(Mono.just(message)).blockLast().getMessage().getPayload();
}
@GetMapping("state")
public String state() {
return Mono.defer(() -> Mono.justOrEmpty(stateMachine.getState().getId())).block();
}
: 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
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");
@startuml
None --> UN_ASSIGN_CARRIER : Create_Event
UN_ASSIGN_CARRIER --> UN_ASSIGN_CAR : Assign_Carrier_Event
UN_ASSIGN_CAR --> ASSIGNED_CAR : Assign_Car_Event
ASSIGNED_CAR --> CANCELED : Order_Cancel_Event
ASSIGNED_CAR --> PICKUPED : Trans_Job_Status_Change_Event
ASSIGNED_CAR --> IN_TRANSIT : Trans_Job_Status_Change_Event
IN_TRANSIT --> PASS_CUSTOMS : Trans_Job_Status_Change_Event
PASS_CUSTOMS --> ALL_DELIVERIED : Trans_Job_Status_Change_Event
PASS_CUSTOMS --> ALL_DELIVERIED : All_Delivery_Event
IN_TRANSIT --> ALL_DELIVERIED : Trans_Job_Status_Change_Event
ALL_DELIVERIED --> FINISH : Order_Finis
UN_ASSIGN_CAR --> CANCELED : Order_Cancel_Event
UN_ASSIGN_CARRIER --> CANCELED : Order_Cancel_Event
PICKUPED --> ALL_DELIVERIED : Trans_Job_Status_Change_Event
CANCELED --> FINISH : Order_Finis
@enduml
/**
* 一种是通过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)
4j
public 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)
4j
public 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);
}
}
}
public class Context {
private String userName;
private Long transNeedId;
private TransNeed transNeed;
}
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;
}
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;
}
}
/**
* 接单
* @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;
}
/**
* 运输规划单生成调度单,调度单绑定服务商
*/
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;
}
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;
}