【发布时间】:2019-09-30 15:39:08
【问题描述】:
我正在做一个项目,我们使用状态机来实现工作流。我在对已实施的内容感到满意时遇到了一些麻烦,我想看看是否有更好的设计/实现来解决我的问题。
我将尝试展示我们目前拥有的东西。
请暂时忽略process_agent,我想只关注process_state 只是开始。我只是想创建一个进程,状态机将立即从 CREATED 转换为 ASSIGNED 并将该状态保存在 Entity 表中(默认情况下,我暂时将当前用户设置为代理)。
有一个表Entity有两个信息:process_agent和process_state
目前只有三个States,定义为Enums:CREATED、ASSIGNED和IN_PROCESS
目前只有两个Events,定义为Enums:ASSIGN_TO_AGENT和START_PROCESS
控制器中有一个端点,用于创建一个简单地移交给服务的进程:
// In the Controller
// mapper is a MapStruct mapper, it simply copies fields from view to entity and vice versa
ResponseEntity<EntityView> create(@RequestBody final EntityView view) {
final Entity createdEntity = service.create(entityView);
final EntityView createdEntityView = mapper.toView(createdEntity); //map the entity to its view
return status(CREATED).body(createdEntityView);
}
// In the Service
// mapper is a MapStruct mapper, it simply copies fields from view to entity and vice versa
// stateHandler is a custom class to handle an event, see below
Entity entity = new Entity();
mapper.updateFromView(entityView, entity);
entity.setInitState(CREATED);
final Message<Event> message = MessageBuilder.withPayload(Event.ASSIGN_TO_AGENT).setHeader("ENTITY_HEADER", entity);
stateHandler.handleEvent(message);
entity.setProcessAgent(...get the current user's id somehow...);
...
return entity;
StateHandler 处理事件消息。那是我觉得困难并且觉得我应该质疑的部分。基本上得到一个状态机,将其重置为给定状态并运行它以拦截转换;一旦被拦截,新的目标状态就会被持久化到实体表中:
// stateMachineFactory is auto wired into the state handler
// repository is auto wired in the state handler
public void handleEvent(final Message<Event> message) {
final Entity entity = message.getHeaders().get("ENTITY_HEADER", Entity.class);
final State currentState = entity.getProcessState();
StateMachine<State, Event> machine = stateMachineFactory.getStateMachine();
machine.getStateMachineAccessor().doWithAllRegions(accessor -> accessor.resetStateMachine(
new DefaultStateMachineContext<State, Event>(currentState, null, null, null, null)
));
machine.getStateMachineAccessor().doWithAllRegions(accessor -> accessor.addStateMachineInterceptor(
@Override
public StateContext<State, Event> postTransition(final StateContext<State, Event> stateContext) {
final Entity entity1 = stateContext.getMessage().getHeaders.get("ENTITY_HEADER", Entity.class);
if (entity != null) {
entity1.setState(stateContext.getTarget().getId());
repository.save(entity1);
return stateContext;
}
// if entity is null then throw exception
... omitted exception handling
}
);
log.debug("Starting state machine to process [{}]", entity);
stateMachine.start();
stateMachine.sendEvent(message);
stateMachine.stop();
}
为了完整起见,以下StateMachineConfig:
@Override
public void configure(final StateMachineConfigurationConfigurer<State, Event> config) throws Exception {
config.withConfiguration()
.autoStartup(false);
}
@Override
public void configure(final StateMachineStateConfigurer<State, Event> sates) throws Exception {
states.withStates()
.initial(State.CREATED)
.states(EnumSet.allOf(State.class));
}
@Override
public void configure(final StateMachineTransitionConfigurer<State, Event> transitions) throws Exception {
transitions.withExternal()
.source(State.CREATED)
.target(State.ASSIGNED)
.event(Event.ASSIGN_TO_AGENT)
.and()
.withExternal()
.source(State.ASSIGNED)
.target(State.IN_PROCESS)
.event(Event.START_PROCESS);
}
我希望我能尽可能完整。如果需要任何说明,请告诉我。
我的问题是:是否有更好的设计来实现这个状态机,或者在这里可以看到一种合理的方法?
【问题讨论】:
标签: spring-boot spring-statemachine