├── easy-statemachine-common ├── README.md ├── src │ └── main │ │ └── java │ │ └── ambitor │ │ └── easy │ │ └── statemachine │ │ ├── ra │ │ ├── vo │ │ │ └── StateMachineClient.java │ │ └── RegistrationClient.java │ │ └── messaging │ │ ├── vo │ │ ├── MessageType.java │ │ ├── NettyMessage.java │ │ └── Header.java │ │ ├── coder │ │ ├── MarshallingEncoder.java │ │ ├── MarshallingDecoder.java │ │ ├── ChannelBufferByteOutput.java │ │ ├── ChannelBufferByteInput.java │ │ ├── MarshallingCodeCFactory.java │ │ ├── NettyMessageDecoder.java │ │ └── NettyMessageEncoder.java │ │ ├── handler │ │ ├── LoginRequestHandler.java │ │ ├── HeartbeatHandler.java │ │ └── LoginResponseHandler.java │ │ ├── ProtocolServer.java │ │ └── ProtocolClient.java ├── easy-statemachine-common.iml └── pom.xml ├── easy-statemachine-wokflow ├── 主表.png ├── 日志表.png ├── src │ └── main │ │ ├── java │ │ └── ambitor │ │ │ └── easy │ │ │ └── statemachine │ │ │ └── workflow │ │ │ ├── model │ │ │ ├── StateMachineConstant.java │ │ │ ├── TaskStatus.java │ │ │ ├── StateMachineLog.java │ │ │ └── StateMachineTask.java │ │ │ ├── service │ │ │ ├── StateMachineLogService.java │ │ │ ├── StateMachineService.java │ │ │ └── StateMachineTaskService.java │ │ │ ├── action │ │ │ └── WorkFlowAction.java │ │ │ └── interceptor │ │ │ └── PersistStateMachineInterceptor.java │ │ └── resources │ │ └── script │ │ └── init.sql └── pom.xml ├── easy-statemachine-core ├── src │ └── main │ │ └── java │ │ └── ambitor │ │ └── easy │ │ └── statemachine │ │ ├── core │ │ ├── context │ │ │ ├── Message.java │ │ │ ├── DefaultMessage.java │ │ │ ├── StateContext.java │ │ │ ├── MessageHeaders.java │ │ │ └── DefaultStateContext.java │ │ ├── enumerate │ │ │ └── TransitionType.java │ │ ├── builder │ │ │ └── ConfigurerBuilder.java │ │ ├── guard │ │ │ ├── Guard.java │ │ │ └── DefaultGuard.java │ │ ├── interceptor │ │ │ ├── StateMachineInterceptorConfigurer.java │ │ │ ├── StateMachineInterceptor.java │ │ │ ├── AbstractStateMachineInterceptor.java │ │ │ └── StateMachineInterceptorList.java │ │ ├── transition │ │ │ ├── config │ │ │ │ ├── TransitionConfigurer.java │ │ │ │ ├── ChoiceData.java │ │ │ │ ├── StandardTransitionConfigurer.java │ │ │ │ ├── ChoiceTransitionConfigurer.java │ │ │ │ ├── DefaultStandardTransitionConfigurer.java │ │ │ │ ├── BaseTransitionConfigurer.java │ │ │ │ └── DefaultChoiceTransitionConfigurer.java │ │ │ ├── StandardTransition.java │ │ │ ├── Transition.java │ │ │ └── AbstractTransition.java │ │ ├── annotation │ │ │ └── EnableWithStateMachine.java │ │ ├── DefaultStateMachine.java │ │ ├── action │ │ │ ├── Actions.java │ │ │ └── Action.java │ │ ├── configurer │ │ │ ├── StateMachineConfigurer.java │ │ │ └── adapter │ │ │ │ └── AbstractStateMachineConfigurerAdapter.java │ │ ├── state │ │ │ ├── State.java │ │ │ ├── DefaultState.java │ │ │ └── config │ │ │ │ ├── StateConfigurer.java │ │ │ │ └── DefaultStateConfigurer.java │ │ ├── exception │ │ │ ├── StateMachineRetryException.java │ │ │ └── StateMachineException.java │ │ ├── StateMachine.java │ │ ├── factory │ │ │ └── StateMachineFactory.java │ │ └── AbstractStateMachine.java │ │ └── parser │ │ ├── StateMachineParser.java │ │ ├── yml │ │ ├── StateMachineYmlConfig.java │ │ └── StateMachineYmlParser.java │ │ └── loader │ │ └── ParserLoader.java ├── easy-statemachine-core.iml ├── pom.xml └── README.md ├── easy-statemachine-demo ├── src │ └── main │ │ ├── java │ │ └── ambitor │ │ │ └── easy │ │ │ └── statemachine │ │ │ ├── sf │ │ │ ├── enumerate │ │ │ │ ├── GrantEvent.java │ │ │ │ ├── GrantState.java │ │ │ │ └── GrantConstant.java │ │ │ ├── action │ │ │ │ ├── CreateCardIIAction.java │ │ │ │ ├── FinishAction.java │ │ │ │ ├── GrantAction.java │ │ │ │ └── DocumentCreditAction.java │ │ │ └── statemachine │ │ │ │ └── GrantStateMachineConfig.java │ │ │ ├── eat │ │ │ └── action │ │ │ │ ├── WifeWash.java │ │ │ │ ├── CookFood.java │ │ │ │ ├── CookRice.java │ │ │ │ ├── HusbandWash.java │ │ │ │ ├── BuyFood.java │ │ │ │ └── EatRice.java │ │ │ ├── service │ │ │ ├── StateMachineLogServiceImpl.java │ │ │ ├── StateMachineServiceImpl.java │ │ │ └── StateMachineTaskServiceImpl.java │ │ │ └── Application.java │ │ └── resources │ │ └── statemachine │ │ ├── eat-statemachine.yml │ │ └── sf-statemachine.yml ├── pom.xml └── README.md ├── .gitignore ├── easy-statemachine.iml ├── pom.xml └── README.md /easy-statemachine-common/README.md: -------------------------------------------------------------------------------- 1 | # 轻量级状态机工作流引擎公共包 2 | 公共包正在不断增强,目前主要包括以下实现: 3 | - 集群间消息通信 4 | - 集群注册中心 -------------------------------------------------------------------------------- /easy-statemachine-wokflow/主表.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacktao219/easy-statemachine/HEAD/easy-statemachine-wokflow/主表.png -------------------------------------------------------------------------------- /easy-statemachine-wokflow/日志表.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacktao219/easy-statemachine/HEAD/easy-statemachine-wokflow/日志表.png -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/context/Message.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.context; 2 | 3 | public interface Message { 4 | 5 | T getPayload(); 6 | 7 | MessageHeaders getHeaders(); 8 | } -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/model/StateMachineConstant.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.model; 2 | 3 | /** 4 | * Created by Ambitor on 2019/1/21 5 | */ 6 | public class StateMachineConstant { 7 | public static final String TASK_HEADER = "state_machine_task"; 8 | } 9 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/enumerate/TransitionType.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.enumerate; 2 | 3 | /** 4 | * 转换器类型枚举 5 | * Created by Ambitor on 2019/4/10 6 | * @author Ambitor 7 | */ 8 | public enum TransitionType { 9 | //标准 10 | standard, 11 | //选择转换器 12 | choice 13 | } 14 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/builder/ConfigurerBuilder.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.builder; 2 | 3 | /** 4 | * 类似StringBuilder 需要拼接的对象 5 | * Created by Ambitor on 2019-01-21. 6 | */ 7 | public interface ConfigurerBuilder { 8 | /** 9 | * 拼接方法 10 | * @return 需要拼接的类 11 | */ 12 | I and(); 13 | } 14 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/service/StateMachineLogService.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.service; 2 | 3 | import ambitor.easy.statemachine.workflow.model.StateMachineLog; 4 | 5 | /** 6 | * 状态机日志记录 7 | */ 8 | public interface StateMachineLogService { 9 | 10 | int insertSelective(StateMachineLog record); 11 | 12 | } -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/ra/vo/StateMachineClient.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.ra.vo; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | /** 7 | * 状态机 8 | * Created by Ambitor on 2019/6/26 9 | */ 10 | @Getter 11 | @Setter 12 | public class StateMachineClient { 13 | private String host; 14 | private String hostName; 15 | } 16 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/sf/enumerate/GrantEvent.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.sf.enumerate; 2 | 3 | public enum GrantEvent { 4 | //开二类户 5 | CREATE_CARDII, 6 | //建档授信 7 | DOCUMENT_CREDIT, 8 | //建档授信回调 9 | DOCUMENT_CREDIT_CALLBACK, 10 | //放款 11 | GRANTED, 12 | //放款校验 13 | GRANT_CHECKED, 14 | //结束 15 | FINISHED 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # ide 14 | .iml 15 | .eclipse 16 | 17 | # Package Files # 18 | *.jar 19 | *.war 20 | *.nar 21 | *.ear 22 | *.zip 23 | *.tar.gz 24 | *.rar 25 | 26 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 27 | hs_err_pid* 28 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/guard/Guard.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.guard; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | 6 | /** 7 | * 断言接口 8 | * Created by Ambitor on 2019/1/21 9 | * @param 状态 10 | * @param 事件 11 | */ 12 | public interface Guard { 13 | 14 | boolean evaluate(StateContext context); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/interceptor/StateMachineInterceptorConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.interceptor; 2 | 3 | /** 4 | * 状态机拦截器配置 5 | * @author Ambitor 6 | * @param 状态 7 | * @param 事件 8 | */ 9 | public interface StateMachineInterceptorConfigurer { 10 | 11 | /** 12 | * 注册拦截器 13 | * @param interceptor 14 | * @return 15 | */ 16 | boolean register(StateMachineInterceptor interceptor); 17 | } 18 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/parser/StateMachineParser.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.parser; 2 | 3 | import ambitor.easy.statemachine.core.configurer.StateMachineConfigurer; 4 | 5 | /** 6 | * 状态机解析器 7 | * Created by Ambitor on 2019/4/10 8 | * @author Ambitor 9 | */ 10 | public interface StateMachineParser { 11 | 12 | /** 13 | * 解析器 14 | * @param config 15 | * @return 16 | */ 17 | StateMachineConfigurer parser(T config); 18 | } 19 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/model/TaskStatus.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.model; 2 | 3 | public enum TaskStatus { 4 | /** 5 | * 待执行 6 | */ 7 | open, 8 | /** 9 | * 执行中 10 | */ 11 | running, 12 | /** 13 | * 执行失败 14 | */ 15 | error, 16 | /** 17 | * 执行结束 18 | */ 19 | close, 20 | /** 21 | * 重试后最终执行失败 22 | */ 23 | terminal, 24 | /** 25 | * 添加此状态,用来让任务挂起不被扫描 26 | */ 27 | suspend 28 | } 29 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/eat/action/WifeWash.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.eat.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | public class WifeWash implements WorkFlowAction { 12 | 13 | @Override 14 | public void execute(StateContext context) { 15 | log.info("老婆洗碗,洗的很仔细"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/eat/action/CookFood.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.eat.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | public class CookFood implements WorkFlowAction { 12 | 13 | @Override 14 | public void execute(StateContext context) { 15 | log.info("开始炒菜"); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/eat/action/CookRice.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.eat.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | public class CookRice implements WorkFlowAction { 12 | 13 | @Override 14 | public void execute(StateContext context) { 15 | log.info("开始煮饭"); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/eat/action/HusbandWash.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.eat.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | public class HusbandWash implements WorkFlowAction { 12 | 13 | @Override 14 | public void execute(StateContext context) { 15 | log.info("老公洗碗,随便洗洗"); 16 | } 17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /easy-statemachine.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/context/DefaultMessage.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.context; 2 | 3 | public class DefaultMessage implements Message { 4 | private T payload; 5 | private MessageHeaders headers; 6 | 7 | public DefaultMessage(T payload, MessageHeaders headers) { 8 | this.payload = payload; 9 | this.headers = headers; 10 | } 11 | 12 | @Override 13 | public T getPayload() { 14 | return payload; 15 | } 16 | 17 | @Override 18 | public MessageHeaders getHeaders() { 19 | return headers; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/sf/enumerate/GrantState.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.sf.enumerate; 2 | 3 | /** 4 | */ 5 | public enum GrantState { 6 | //等待开二类户 7 | WAIT_CREATE_CARDII, 8 | //开二类户失败 9 | CREATE_CARDII_FAILED, 10 | //建档授信 11 | WAIT_DOCUMENT_CREDIT, 12 | //等待建档授信回调 13 | WAIT_DOCUMENT_CREDIT_CALLBACK, 14 | //建档授信失败 15 | DOCUMENT_CREDIT_FAILED, 16 | //放款 17 | WAIT_GRANT, 18 | //放款失败 19 | GRANT_FAILED, 20 | //等待放款校验 21 | WAIT_GRANT_CHECK, 22 | //主流程完成 23 | GRANT_TASK_SAVE, 24 | //结束流程 25 | GRANT_SUCCESS 26 | } 27 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/vo/MessageType.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.vo; 2 | 3 | /** 4 | * @Description: ${todo}(描述完成的功能) 5 | * Created by Ambitor on 2017/4/26. 6 | */ 7 | public class MessageType { 8 | 9 | public final static byte SERVICE_REQ = 0; 10 | public final static byte SERVICE_RES = 1; 11 | public final static byte SERVICE_ONE_WAY = 2; 12 | public final static byte ACCEPT_REQ = 3; 13 | public final static byte ACCEPT_RES = 4; 14 | public final static byte HEARTBEAT_REQ = 5; 15 | public final static byte HEARTBEAT_RES = 6; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/ra/RegistrationClient.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.ra; 2 | 3 | import ambitor.easy.statemachine.ra.vo.StateMachineClient; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 注册客户端 9 | * Created by Ambitor on 2019/6/26 10 | */ 11 | public interface RegistrationClient { 12 | /** 13 | * 向注册中心注册 14 | * @param client 客户端 15 | * @return 仅当注册失败了返回false 否则true 16 | */ 17 | boolean register(StateMachineClient client); 18 | 19 | /** 20 | * 获取活跃的客户端 21 | * @return 没有的话返回null 22 | */ 23 | List getAliveClient(); 24 | } 25 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/eat/action/BuyFood.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.eat.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Slf4j 10 | @Component 11 | public class BuyFood implements WorkFlowAction { 12 | 13 | @Override 14 | public void execute(StateContext context) { 15 | String state = context.getSource().getId(); 16 | log.info("去沃尔玛买菜"); 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/model/StateMachineLog.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.Date; 7 | 8 | @Getter 9 | @Setter 10 | public class StateMachineLog { 11 | private Integer id; 12 | private String machineCode; 13 | private String source; 14 | private String target; 15 | private String event; 16 | private String transitionResult; 17 | private Date createTime; 18 | private Date updateTime; 19 | private String machineContext; 20 | private String request; 21 | private String response; 22 | } -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/context/StateContext.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.context; 2 | 3 | 4 | import ambitor.easy.statemachine.core.state.State; 5 | import ambitor.easy.statemachine.core.transition.Transition; 6 | 7 | /** 8 | * 状态上下文 9 | * Created by Ambitor on 2019-01-21 10 | * @param 状态 11 | * @param 事件 12 | */ 13 | public interface StateContext { 14 | 15 | Message getMessage(); 16 | 17 | E getEvent(); 18 | 19 | State getSource(); 20 | 21 | State getTarget(); 22 | 23 | Exception getException(); 24 | 25 | void setException(Exception exception); 26 | 27 | Transition getTransition(); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/config/TransitionConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition.config; 2 | 3 | 4 | import ambitor.easy.statemachine.core.builder.ConfigurerBuilder; 5 | 6 | /** 7 | * 状态转换定义 8 | * Created by Ambitor on 2019/1/21 9 | * @param 状态 10 | * @param 事件 11 | */ 12 | public interface TransitionConfigurer extends ConfigurerBuilder> { 13 | 14 | /** 15 | * 新建一个标准Transition配置 16 | */ 17 | StandardTransitionConfigurer standardTransition(); 18 | 19 | /** 20 | * 新建一个选择Transition配置 21 | */ 22 | ChoiceTransitionConfigurer choiceTransition(); 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/config/ChoiceData.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition.config; 2 | 3 | import ambitor.easy.statemachine.core.guard.Guard; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | /** 8 | * 选择转换器类 9 | * Created by Ambitor on 2019/1/21 10 | * @param 状态 11 | * @param 事件 12 | */ 13 | @Getter 14 | @Setter 15 | public class ChoiceData { 16 | private final S source; 17 | private final S target; 18 | private final Guard guard; 19 | 20 | public ChoiceData(S source, S target, Guard guard) { 21 | this.source = source; 22 | this.target = target; 23 | this.guard = guard; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/service/StateMachineLogServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.service; 2 | 3 | import ambitor.easy.statemachine.workflow.model.StateMachineLog; 4 | import ambitor.easy.statemachine.workflow.service.StateMachineLogService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * Created by Ambitor on 2019/4/2 10 | */ 11 | @Slf4j 12 | @Component 13 | public class StateMachineLogServiceImpl implements StateMachineLogService { 14 | @Override 15 | public int insertSelective(StateMachineLog record) { 16 | log.info("插入状态机日志 StateMachineCode ->{}", record.getMachineCode()); 17 | return 1; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/sf/action/CreateCardIIAction.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.sf.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.sf.enumerate.GrantEvent; 6 | import ambitor.easy.statemachine.sf.enumerate.GrantState; 7 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Slf4j 12 | @Component 13 | public class CreateCardIIAction implements WorkFlowAction { 14 | 15 | public void execute(StateContext context) { 16 | log.info("开二类户"); 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/sf/action/FinishAction.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.sf.action; 2 | 3 | import ambitor.easy.statemachine.core.context.StateContext; 4 | import ambitor.easy.statemachine.sf.enumerate.GrantEvent; 5 | import ambitor.easy.statemachine.sf.enumerate.GrantState; 6 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Slf4j 11 | @Component 12 | public class FinishAction implements WorkFlowAction { 13 | /** 14 | * 放款成功后的action 15 | */ 16 | @Override 17 | public void execute(StateContext context) { 18 | log.info("放款成功后生成还款计划"); 19 | } 20 | } -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/annotation/EnableWithStateMachine.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.annotation; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.stereotype.Component; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Inherited; 9 | import java.lang.annotation.Retention; 10 | import java.lang.annotation.RetentionPolicy; 11 | import java.lang.annotation.Target; 12 | 13 | /** 14 | * Created by Ambitor on 2019-01-21. 15 | */ 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.TYPE) 18 | @Inherited 19 | @Documented 20 | @Component 21 | @Configuration 22 | public @interface EnableWithStateMachine { 23 | } 24 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/context/MessageHeaders.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.context; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class MessageHeaders { 7 | 8 | public MessageHeaders() { 9 | this.headers = new HashMap<>(); 10 | } 11 | 12 | private Map headers; 13 | 14 | public Map getHeaders() { 15 | return headers; 16 | } 17 | 18 | public Object getHeader(String key) { 19 | return headers.get(key); 20 | } 21 | 22 | public void setHeaders(Map headers) { 23 | this.headers = headers; 24 | } 25 | 26 | public void addHeader(String key, Object value) { 27 | headers.put(key, value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/vo/NettyMessage.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.vo; 2 | 3 | 4 | /** 5 | * @Description: ${todo}(描述完成的功能) 6 | * Created by Ambitor on 2017/4/26. 7 | */ 8 | public class NettyMessage { 9 | private Header header; 10 | private Object body; 11 | 12 | public Object getBody() { 13 | return body; 14 | } 15 | 16 | public void setBody(Object body) { 17 | this.body = body; 18 | } 19 | 20 | public Header getHeader() { 21 | return header; 22 | } 23 | 24 | public void setHeader(Header header) { 25 | this.header = header; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return header.toString() + " body:" + body.toString(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/guard/DefaultGuard.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.guard; 2 | 3 | 4 | import ambitor.easy.statemachine.core.exception.StateMachineException; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.util.StringUtils; 7 | 8 | /** 9 | * @author Ambitor 10 | */ 11 | @Slf4j 12 | public class DefaultGuard { 13 | public static Guard condition(String key, String expect) { 14 | return (s) -> { 15 | if (StringUtils.isEmpty(key) || StringUtils.isEmpty(expect)) { 16 | throw new StateMachineException("key or expect can not be blank..."); 17 | } 18 | Object actualValue = s.getMessage().getHeaders().getHeader(key); 19 | if (actualValue == null) return false; 20 | return expect.equals(actualValue); 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/model/StateMachineTask.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.Date; 7 | 8 | @Getter 9 | @Setter 10 | public class StateMachineTask { 11 | private Integer id; 12 | private String machineCode; 13 | private String machineState; 14 | private String machineType; 15 | private String scanStatus; 16 | private String transactionId; 17 | private Integer currentTrytimes; 18 | private Integer retryTimes; 19 | private Date nextRunTime; 20 | private Date createTime; 21 | private Date updateTime; 22 | private String machineContext; 23 | private String requestData; 24 | private String responseData; 25 | 26 | public boolean isLastRetry() { 27 | return getCurrentTrytimes() >= getRetryTimes(); 28 | } 29 | } -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/DefaultStateMachine.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core; 2 | 3 | 4 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorList; 5 | import ambitor.easy.statemachine.core.state.State; 6 | import ambitor.easy.statemachine.core.transition.Transition; 7 | 8 | import java.util.Collection; 9 | import java.util.Map; 10 | 11 | /** 12 | * 默认状态机 13 | * Created by Ambitor on 2019/1/21 14 | * @param 状态 15 | * @param 事件 16 | */ 17 | public class DefaultStateMachine extends AbstractStateMachine { 18 | 19 | public DefaultStateMachine(Map> states, Map>> transitions, State initialState, State currentState, Exception currentError, StateMachineInterceptorList interceptors) { 20 | super(states, transitions, initialState, currentState, currentError, interceptors); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/eat/action/EatRice.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.eat.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.Random; 10 | 11 | /** 12 | * @author Ambitor 13 | */ 14 | @Slf4j 15 | @Component 16 | public class EatRice implements WorkFlowAction { 17 | 18 | @Override 19 | public void execute(StateContext context) { 20 | log.info("开始吃饭"); 21 | boolean delicious = new Random().nextBoolean(); 22 | String result; 23 | //菜做的不好吃老公洗碗,否者老婆洗碗 24 | if (!delicious) { 25 | result = "Husband"; 26 | } else { 27 | result = "Wife"; 28 | } 29 | addHeader(context, "Eat_Status", result); 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/StandardTransition.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.guard.Guard; 6 | import ambitor.easy.statemachine.core.state.State; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | import java.util.Collection; 10 | 11 | /** 12 | * 默认转换器 13 | * @param 状态 14 | * @param 事件 15 | */ 16 | @Slf4j 17 | public class StandardTransition extends AbstractTransition { 18 | 19 | public StandardTransition(State source, State target, E event, Guard guard, Collection> actions) { 20 | super(source, target, event, guard, actions); 21 | } 22 | 23 | @Override 24 | public int hashCode() { 25 | return super.hashCode(); 26 | } 27 | 28 | @Override 29 | public boolean equals(Object obj) { 30 | return super.equals(obj); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/action/Actions.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.DefaultStateContext; 5 | 6 | /** 7 | * 转换操作,在执行某个转换时执行的具体操作 8 | * Created by Ambitor on 2019-01-21. 9 | */ 10 | public final class Actions { 11 | 12 | private Actions() { 13 | // 工具类 14 | } 15 | 16 | public static Action errorCallingAction(final Action action, final Action errorAction) { 17 | return context -> { 18 | try { 19 | action.execute(context); 20 | } catch (Exception exception) { 21 | try { 22 | errorAction.execute(new DefaultStateContext<>(context.getMessage(), context.getTransition(), context.getSource(), 23 | context.getTarget(), exception)); 24 | } catch (Exception e) { 25 | throw exception; 26 | } 27 | } 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/service/StateMachineService.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.service; 2 | 3 | import ambitor.easy.statemachine.core.configurer.StateMachineConfigurer; 4 | import ambitor.easy.statemachine.workflow.model.StateMachineTask; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 状态机Service 10 | * Created by Ambitor on 2019/4/2 11 | */ 12 | public interface StateMachineService { 13 | 14 | /** 15 | * 根据状态机名获取状态机配置 16 | * @param stateMachineName 17 | * @param 18 | * @param 19 | * @return 20 | */ 21 | StateMachineConfigurer getByName(String stateMachineName); 22 | 23 | /** 24 | * 通过定时调度启动任务 25 | * 1、从数据库中将任务查询出来 26 | * 2、标记任务为运行中 27 | * 3、将任务放入到MQ中 28 | * 注意事务处理,忽略超时重试的场景 29 | * @return 返回获取到的task 30 | */ 31 | List execute(); 32 | 33 | /** 34 | * 执行task 35 | * @param task 任务 36 | */ 37 | void processTask(StateMachineTask task); 38 | } 39 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/sf/action/GrantAction.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.sf.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.sf.enumerate.GrantEvent; 6 | import ambitor.easy.statemachine.sf.enumerate.GrantState; 7 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Component; 10 | 11 | import static ambitor.easy.statemachine.sf.enumerate.GrantConstant.GRANT_STATUS; 12 | import static ambitor.easy.statemachine.sf.enumerate.GrantConstant.GRANT_SUCCESS; 13 | 14 | @Component 15 | @Slf4j 16 | public class GrantAction implements WorkFlowAction { 17 | 18 | /** 19 | * 放款 20 | * @param context 上下文 21 | */ 22 | @Override 23 | public void execute(StateContext context) { 24 | System.out.println("放款"); 25 | addHeader(context, GRANT_STATUS, GRANT_SUCCESS); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/interceptor/StateMachineInterceptor.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.interceptor; 2 | 3 | import ambitor.easy.statemachine.core.StateMachine; 4 | import ambitor.easy.statemachine.core.context.Message; 5 | import ambitor.easy.statemachine.core.state.State; 6 | import ambitor.easy.statemachine.core.transition.Transition; 7 | 8 | /** 9 | * 状态机拦截器 Created by Ambitor on 2019/1/21 10 | * 11 | * @param 状态 12 | * @param 事件 13 | */ 14 | public interface StateMachineInterceptor { 15 | 16 | Message preEvent(Message message, StateMachine stateMachine); 17 | 18 | void preStateChange(State state, Message message, Transition transition, 19 | StateMachine stateMachine); 20 | 21 | void afterStateChange(State state, Message message, Transition transition, 22 | StateMachine stateMachine); 23 | 24 | Exception stateMachineError(StateMachine stateMachine, Message eventMsg, Exception exception); 25 | } 26 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/Transition.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.context.StateContext; 6 | import ambitor.easy.statemachine.core.guard.Guard; 7 | import ambitor.easy.statemachine.core.state.State; 8 | 9 | import java.util.Collection; 10 | 11 | /** 12 | * 转换器接口 13 | * Created by Ambitor on 2019/1/21 14 | * @param 状态 15 | * @param 事件 16 | */ 17 | public interface Transition { 18 | 19 | /** 20 | * 是否转换 21 | * @param context 22 | * @return 23 | */ 24 | boolean transit(StateContext context); 25 | 26 | void executeTransitionActions(StateContext context); 27 | 28 | Collection> getActions(); 29 | 30 | State getSource(); 31 | 32 | State getTarget(); 33 | 34 | Guard guard(); 35 | 36 | E getEvent(); 37 | 38 | String SUCCESS = "SUCCESS"; 39 | String FAILED = "FAILED"; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/Application.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine; 2 | 3 | import ambitor.easy.statemachine.workflow.model.StateMachineTask; 4 | import ambitor.easy.statemachine.workflow.service.StateMachineService; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.ConfigurableApplicationContext; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by Ambitor on 2019/3/20 13 | */ 14 | @SpringBootApplication(scanBasePackages = {"ambitor"}) 15 | public class Application { 16 | 17 | public static void main(String[] args) { 18 | ConfigurableApplicationContext context = SpringApplication.run(Application.class); 19 | StateMachineService stateMachineService = context.getBean(StateMachineService.class); 20 | List tasks = stateMachineService.execute(); 21 | for (StateMachineTask task : tasks) { 22 | stateMachineService.processTask(task); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/configurer/StateMachineConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.configurer; 2 | 3 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorConfigurer; 4 | import ambitor.easy.statemachine.core.state.config.StateConfigurer; 5 | import ambitor.easy.statemachine.core.transition.config.TransitionConfigurer; 6 | 7 | /** 8 | * 状态机配置 9 | * Created by Ambitor on 2019-01-21 10 | * @author Ambitor 11 | * @param 状态 12 | * @param 事件 13 | */ 14 | public interface StateMachineConfigurer { 15 | /** 16 | * 状态机名称 17 | */ 18 | String getName(); 19 | /** 20 | * 状态配置 21 | * @param states 状态 22 | * @throws Exception 23 | */ 24 | void configure(StateConfigurer states); 25 | 26 | /** 27 | * 转换配置 28 | * @param transitions 转换 29 | * @throws Exception 30 | */ 31 | void configure(TransitionConfigurer transitions); 32 | 33 | /** 34 | * 配置拦截器 35 | * @param interceptors 拦截器 36 | */ 37 | void configure(StateMachineInterceptorConfigurer interceptors); 38 | } 39 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/state/State.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.state; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.context.Message; 6 | 7 | import java.util.Collection; 8 | 9 | /** 10 | * 状态接口 11 | * Created by Ambitor on 2019/1/21 12 | * @param 状态 13 | * @param 事件 14 | */ 15 | public interface State { 16 | 17 | /** 18 | * 发送事件的包装类. 19 | * @param event 事件包装类 20 | * @return 如果事件被接受返回true 否则false 21 | */ 22 | boolean sendEvent(Message event); 23 | 24 | /** 25 | * 获取状态ID 26 | */ 27 | S getId(); 28 | 29 | /** 30 | * 是否初始化状态 31 | */ 32 | boolean isInitial(); 33 | 34 | /** 35 | * 是否结束状态 36 | */ 37 | boolean isEnd(); 38 | 39 | /** 40 | * 是否为挂起状态 41 | */ 42 | boolean isSuspend(); 43 | 44 | /** 45 | * 进入Actions 46 | */ 47 | Collection> getEntryActions(); 48 | 49 | /** 50 | * 所有退出的actions 51 | * @return actions 52 | */ 53 | Collection> getExitActions(); 54 | } -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/service/StateMachineTaskService.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.service; 2 | 3 | import ambitor.easy.statemachine.workflow.model.StateMachineTask; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 任务service 9 | * Created by Ambitor on 2019/3/20 10 | */ 11 | public interface StateMachineTaskService { 12 | 13 | /** 14 | * 新增状态机 15 | * @param task 状态机 16 | * @return 影响行 17 | */ 18 | int insertSelective(StateMachineTask task); 19 | 20 | /** 21 | * 根据机器code查询 22 | * @param code 机器码 23 | * @return 状态机 24 | */ 25 | StateMachineTask findByCode(String code); 26 | 27 | /** 28 | * 根据机器TransactionId查询 29 | * @param transactionId 唯一编号 30 | * @return 状态机 31 | */ 32 | StateMachineTask findByTransactionId(String transactionId); 33 | 34 | /** 35 | * 根据主键修改状态机任务 36 | * @param task 状态机 37 | * @return 影响行 38 | */ 39 | int updateByPrimaryKeySelective(StateMachineTask task); 40 | 41 | /** 42 | * 获取需要执行的状态机任务 43 | * @return 状态机任务 44 | */ 45 | List getExecuteTask(); 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/sf/action/DocumentCreditAction.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.sf.action; 2 | 3 | import ambitor.easy.statemachine.core.action.Action; 4 | import ambitor.easy.statemachine.core.context.StateContext; 5 | import ambitor.easy.statemachine.sf.enumerate.GrantEvent; 6 | import ambitor.easy.statemachine.sf.enumerate.GrantState; 7 | import ambitor.easy.statemachine.workflow.action.WorkFlowAction; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.stereotype.Component; 10 | 11 | import static ambitor.easy.statemachine.sf.enumerate.GrantConstant.DOCUMENT_CREDIT_STATUS; 12 | import static ambitor.easy.statemachine.sf.enumerate.GrantConstant.DOCUMENT_CREDIT_SUCCESS; 13 | 14 | @Slf4j 15 | @Component 16 | public class DocumentCreditAction implements WorkFlowAction { 17 | 18 | @Override 19 | public void execute(StateContext context) { 20 | log.info("建档授信"); 21 | addHeader(context,DOCUMENT_CREDIT_STATUS,DOCUMENT_CREDIT_SUCCESS); 22 | } 23 | 24 | public Action errorAction() { 25 | return (s) -> log.info("建档授信异常,最后一次尝试"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/coder/MarshallingEncoder.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.coder; 2 | 3 | 4 | import io.netty.buffer.ByteBuf; 5 | import org.jboss.marshalling.Marshaller; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @Description: 编码器 11 | * Created by Ambitor on 2017/4/26. 12 | */ 13 | public class MarshallingEncoder { 14 | 15 | private static final byte[] LENGTH_PLACEHOLDER = new byte[4]; 16 | 17 | private Marshaller marshaller; 18 | 19 | public MarshallingEncoder() throws Exception { 20 | this.marshaller = MarshallingCodeCFactory.buildMarshaller(); 21 | } 22 | 23 | public void encode(Object attachment, ByteBuf out) throws IOException { 24 | try { 25 | int lengthPostion = out.writerIndex(); 26 | //空出32位标志附件的长度 27 | out.writeBytes(LENGTH_PLACEHOLDER); 28 | ChannelBufferByteOutput output = new ChannelBufferByteOutput(out); 29 | marshaller.start(output); 30 | marshaller.writeObject(attachment); 31 | marshaller.finish(); 32 | //往lengthPostion位置写入附件长度 33 | out.setInt(lengthPostion, out.writerIndex() - lengthPostion - 4); 34 | } catch (IOException e) { 35 | marshaller.close(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/config/StandardTransitionConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition.config; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | 6 | /** 7 | * 标准转换器 8 | * Created by Ambitor on 2019/1/21 9 | * @param 状态 10 | * @param 事件 11 | */ 12 | public interface StandardTransitionConfigurer extends TransitionConfigurer { 13 | /** 14 | * 源状态 15 | * @param source 源状态S 16 | */ 17 | StandardTransitionConfigurer source(S source); 18 | 19 | /** 20 | * 目标状态 21 | * @param target 目标状态S 22 | */ 23 | StandardTransitionConfigurer target(S target); 24 | 25 | /** 26 | * 触发状态扭转 Source -> Target 的事件 27 | * @param event 事件E 28 | */ 29 | StandardTransitionConfigurer event(E event); 30 | 31 | /** 32 | * action执行成功,状态扭转 source -> target 33 | * 如抛出异常则状态不会扭转 34 | * @param action 事件触发后执行的action 35 | */ 36 | StandardTransitionConfigurer action(Action action); 37 | 38 | /** 39 | * action执行成功状态从 source -> target 40 | * 注意,如果有error函数,当action执行失败时会调用error,且如果error执行成功无异常后 41 | * 状态也会扭转 source -> target 42 | */ 43 | StandardTransitionConfigurer action(Action action, Action error); 44 | } 45 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/action/Action.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.action; 2 | 3 | 4 | import ambitor.easy.statemachine.core.context.MessageHeaders; 5 | import ambitor.easy.statemachine.core.context.StateContext; 6 | 7 | 8 | /** 9 | * 转换操作,在执行某个转换时执行的具体操作 10 | * Created by Ambitor on 2019-01-21. 11 | * @param 状态 12 | * @param 事件 13 | */ 14 | public interface Action { 15 | 16 | /** 17 | * 执行动作,状态机不保证幂等,请在execute方法内自己实现业务幂等 18 | * 如果action成功返回则代表事件触发成功,否则请抛出异常 19 | * @param context 上下文 20 | */ 21 | void execute(StateContext context); 22 | 23 | /** 24 | * 添加信息到上下文中,会被序列化到数据库中的Response_data 25 | * @param context 上下文 26 | * @param key key 27 | * @param value value 28 | */ 29 | default void addHeader(StateContext context, String key, String value) { 30 | MessageHeaders headers = context.getMessage().getHeaders(); 31 | headers.addHeader(key, value); 32 | } 33 | 34 | /** 35 | * 上下文中获取信息,会被序列化到数据库中的Response_data 36 | * @param context 上下文 37 | * @param key key 38 | */ 39 | default T getHeader(StateContext context, String key) { 40 | MessageHeaders headers = context.getMessage().getHeaders(); 41 | return (T) headers.getHeader(key); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /easy-statemachine-common/easy-statemachine-common.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/coder/MarshallingDecoder.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.coder; 2 | 3 | 4 | import io.netty.buffer.ByteBuf; 5 | import org.jboss.marshalling.Unmarshaller; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @Description: 解码器 11 | * Created by Ambitor on 2017/4/26. 12 | */ 13 | public class MarshallingDecoder { 14 | 15 | private Unmarshaller unmarshaller; 16 | 17 | public MarshallingDecoder() throws Exception { 18 | unmarshaller = MarshallingCodeCFactory.buildUnmarshaller(); 19 | } 20 | 21 | public Object decode(ByteBuf in) throws IOException { 22 | try { 23 | int objectSize = in.readInt(); 24 | if (objectSize == 0) return null; 25 | ChannelBufferByteInput byteInput = new ChannelBufferByteInput(in.slice(in.readerIndex(), objectSize)); 26 | unmarshaller.start(byteInput); 27 | Object attachment = unmarshaller.readObject(); 28 | unmarshaller.finish(); 29 | //unmarshaller并不会吧position设置到附件后,所有读完后需要把position置为附件结束位置 30 | in.readerIndex(in.readerIndex() + objectSize); 31 | return attachment; 32 | } catch (Exception e) { 33 | unmarshaller.close(); 34 | System.out.println(e); 35 | } 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/service/StateMachineServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.service; 2 | 3 | import ambitor.easy.statemachine.workflow.model.StateMachineTask; 4 | import ambitor.easy.statemachine.workflow.service.AbstractStateMachineService; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * Created by Ambitor on 2019/4/2 10 | */ 11 | @Slf4j 12 | @Component 13 | public class StateMachineServiceImpl extends AbstractStateMachineService { 14 | /** 15 | * 放入MQ 16 | * @param task 17 | */ 18 | @Override 19 | public void sendToMq(StateMachineTask task) { 20 | log.info("发送状态机消息到MQ transactionId->{}", task.getTransactionId()); 21 | } 22 | 23 | /** 24 | * 对machineTask加锁 25 | * @param transactionId 状态机的事务ID 26 | * @return true 成功 false 失败 27 | */ 28 | @Override 29 | public boolean lock(String transactionId) { 30 | log.info("对状态机加锁 transactionId ->{}", transactionId); 31 | return true; 32 | } 33 | 34 | /** 35 | * 对machineTask解锁 36 | * @param transactionId 状态机的事务ID 37 | * @return true 成功 false 失败 38 | */ 39 | @Override 40 | public boolean unLock(String transactionId) { 41 | log.info("对状态机解锁 transactionId ->{}", transactionId); 42 | return true; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/coder/ChannelBufferByteOutput.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.coder; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import org.jboss.marshalling.ByteOutput; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * netty源码,因为引入不了所以直接复制出来 10 | * {@link ByteOutput} implementation which writes the data to a {@link ByteBuf} 11 | */ 12 | class ChannelBufferByteOutput implements ByteOutput { 13 | 14 | private final ByteBuf buffer; 15 | 16 | /** 17 | * Create a new instance which use the given {@link ByteBuf} 18 | */ 19 | ChannelBufferByteOutput(ByteBuf buffer) { 20 | this.buffer = buffer; 21 | } 22 | 23 | @Override 24 | public void close() { 25 | // Nothing to do 26 | } 27 | 28 | @Override 29 | public void flush() { 30 | // nothing to do 31 | } 32 | 33 | @Override 34 | public void write(int b) { 35 | buffer.writeByte(b); 36 | } 37 | 38 | @Override 39 | public void write(byte[] bytes) { 40 | buffer.writeBytes(bytes); 41 | } 42 | 43 | @Override 44 | public void write(byte[] bytes, int srcIndex, int length) { 45 | buffer.writeBytes(bytes, srcIndex, length); 46 | } 47 | 48 | /** 49 | * Return the {@link ByteBuf} which contains the written content 50 | * 51 | */ 52 | ByteBuf getBuffer() { 53 | return buffer; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/resources/statemachine/eat-statemachine.yml: -------------------------------------------------------------------------------- 1 | #状态机名称 2 | name: eat 3 | 4 | #拦截器配置 5 | interceptor: 6 | - PersistStateMachineInterceptor.class 7 | 8 | #状态配置 9 | states: 10 | init: 准备食材 11 | suspend: 12 | # - 不用 13 | end: 14 | - 休息中 15 | other: 16 | - 准备米饭 17 | - 准备炒菜 18 | - 准备吃饭 19 | - 老公准备洗碗 #-----由后吃完的人洗碗 20 | - 老婆准备洗碗 21 | 22 | #事件配置 23 | events: 24 | - 买菜 25 | - 煮饭 26 | - 炒菜 27 | - 吃饭 28 | - 洗碗 29 | 30 | #转换器配置 31 | transitions: 32 | - type: standard 33 | source: 准备食材 34 | target: 准备米饭 35 | event: 买菜 36 | action: BuyFood.class 37 | errorAction: 38 | 39 | - type: standard 40 | source: 准备米饭 41 | target: 准备炒菜 42 | event: 煮饭 43 | action: CookRice.class 44 | errorAction: 45 | 46 | - type: standard 47 | source: 准备炒菜 48 | target: 准备吃饭 49 | event: 炒菜 50 | action: CookFood.class 51 | errorAction: 52 | 53 | - type: choice 54 | source: 准备吃饭 55 | event: 吃饭 56 | action: EatRice.class 57 | first: {status: Eat_Status,equals: Husband,target: 老公准备洗碗} 58 | last: {target: 老婆准备洗碗} 59 | 60 | - type: standard 61 | source: 老婆准备洗碗 62 | target: 休息中 63 | event: 洗碗 64 | action: WifeWash.class 65 | 66 | - type: standard 67 | source: 老公准备洗碗 68 | target: 休息中 69 | event: 洗碗 70 | action: HusbandWash.class -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/interceptor/AbstractStateMachineInterceptor.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.interceptor; 2 | 3 | import ambitor.easy.statemachine.core.StateMachine; 4 | import ambitor.easy.statemachine.core.context.Message; 5 | import ambitor.easy.statemachine.core.state.State; 6 | import ambitor.easy.statemachine.core.transition.Transition; 7 | import lombok.extern.slf4j.Slf4j; 8 | 9 | /** 10 | * 状态机拦截器 Created by Ambitor on 2019/1/21 11 | * 12 | * @param 状态 13 | * @param 事件 14 | */ 15 | @Slf4j 16 | public class AbstractStateMachineInterceptor implements StateMachineInterceptor { 17 | @Override 18 | public Message preEvent(Message message, StateMachine stateMachine) { 19 | log.info("preEvent In StateMachineInterceptor..."); 20 | return message; 21 | } 22 | 23 | @Override 24 | public void preStateChange(State state, Message message, Transition transition, 25 | StateMachine stateMachine) { 26 | log.info("preStateChange In StateMachineInterceptor..."); 27 | } 28 | 29 | @Override 30 | public void afterStateChange(State state, Message message, Transition transition, 31 | StateMachine stateMachine) { 32 | log.info("afterStateChange In StateMachineInterceptor..."); 33 | } 34 | 35 | @Override 36 | public Exception stateMachineError(StateMachine stateMachine, Message eventMsg, Exception exception) { 37 | log.info("stateMachineError In StateMachineInterceptor..."); 38 | return exception; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/handler/LoginRequestHandler.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.handler; 2 | 3 | import ambitor.easy.statemachine.messaging.vo.Header; 4 | import ambitor.easy.statemachine.messaging.vo.MessageType; 5 | import ambitor.easy.statemachine.messaging.vo.NettyMessage; 6 | import io.netty.channel.ChannelHandlerAdapter; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.util.concurrent.ConcurrentHashMap; 11 | 12 | /** 13 | * @Description: 客户端握手、权限handler 14 | * Created by Ambitor on 2017/4/26. 15 | */ 16 | @Slf4j 17 | public class LoginRequestHandler extends ChannelHandlerAdapter { 18 | 19 | @Override 20 | public void channelActive(ChannelHandlerContext ctx) { 21 | NettyMessage message = new NettyMessage(); 22 | Header header = new Header(); 23 | header.setType(MessageType.ACCEPT_REQ); 24 | message.setHeader(header); 25 | message.setBody("Hello Netty Private Protocol."); 26 | ctx.writeAndFlush(message); 27 | } 28 | 29 | @Override 30 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 31 | NettyMessage request = (NettyMessage) msg; 32 | if (request != null && request.getHeader().getType() == MessageType.ACCEPT_RES) { 33 | if (request.getHeader().getSessionID() != 1) { 34 | ctx.close(); 35 | } 36 | } 37 | ctx.fireChannelRead(msg); 38 | } 39 | 40 | @Override 41 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 42 | log.error("登陆请求异常", cause); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/exception/StateMachineRetryException.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.exception; 2 | 3 | /** 4 | * 可重试的异常 5 | * Created by Ambitor on 2019/3/19 6 | */ 7 | public class StateMachineRetryException extends StateMachineException { 8 | public StateMachineRetryException(int code) { 9 | super(code); 10 | } 11 | 12 | public StateMachineRetryException(int code, String message) { 13 | super(code, message); 14 | } 15 | 16 | public StateMachineRetryException(int code, boolean notPrintStack) { 17 | super(code, notPrintStack); 18 | } 19 | 20 | public StateMachineRetryException(int code, Throwable cause) { 21 | super(code, cause); 22 | } 23 | 24 | public StateMachineRetryException(int code, Object[] args) { 25 | super(code, args); 26 | } 27 | 28 | public StateMachineRetryException(String message) { 29 | super(message); 30 | } 31 | 32 | public StateMachineRetryException(String message, Object... params) { 33 | super(message, params); 34 | } 35 | 36 | public StateMachineRetryException(Throwable cause, String message, Object... params) { 37 | super(cause, message, params); 38 | } 39 | 40 | public StateMachineRetryException(String message, Throwable cause) { 41 | super(message, cause); 42 | } 43 | 44 | public StateMachineRetryException(Throwable cause) { 45 | super(cause); 46 | } 47 | 48 | protected StateMachineRetryException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 49 | super(message, cause, enableSuppression, writableStackTrace); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/sf/enumerate/GrantConstant.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.sf.enumerate; 2 | 3 | public class GrantConstant { 4 | /** 5 | * 建档授信成功 6 | */ 7 | public static final String DOCUMENT_CREDIT_SUCCESS = "DOCUMENT_CREDIT_SUCCESS"; 8 | /** 9 | * 建档授信失败 10 | */ 11 | public static final String DOCUMENT_CREDIT_FAILED = "DOCUMENT_CREDIT_FAILED"; 12 | /** 13 | * 建档授信状态 14 | */ 15 | public static final String DOCUMENT_CREDIT_STATUS = "DOCUMENT_CREDIT_STATUS"; 16 | 17 | /** 18 | * 开二类户成功 19 | */ 20 | public static final String CREATE_CARDII_SUCCESS = "CREATE_CARDII_SUCCESS"; 21 | /** 22 | * 开二类户失败 23 | */ 24 | public static final String CREATE_CARDII_FAILED = "CREATE_CARDII_FAILED"; 25 | /** 26 | * 开二类户状态 27 | */ 28 | public static final String CREATE_CARDII_STATUS = "CREATE_CARDII_STATUS"; 29 | 30 | /** 31 | * 建档授信成功 32 | */ 33 | public static final String WAIT_DOCUMENT_CREDIT_CALLBACK = "WAIT_DOCUMENT_CREDIT_CALLBACK"; 34 | /** 35 | * 放款成功 36 | */ 37 | public static final String GRANT_SUCCESS = "GRANT_SUCCESS"; 38 | /** 39 | * 放款状态 40 | */ 41 | public static final String GRANT_STATUS = "GRANT_STATUS"; 42 | /** 43 | * 放款失败 44 | */ 45 | public static final String GRANT_FAIL = "GRANT_FAIL"; 46 | /** 47 | * 校验状态 48 | */ 49 | public static final String CHECK_STATUS = "CHECK_STATUS"; 50 | /** 51 | * 校验成功 52 | */ 53 | public static final String CHECK_SUCCESS = "CHECK_SUCCESS"; 54 | /** 55 | * 校验失败 56 | */ 57 | public static final String CHECK_FAILED = "CHECK_FAILED"; 58 | } 59 | -------------------------------------------------------------------------------- /easy-statemachine-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.ambitor.framework 8 | easy-statemachine 9 | 1.0.0 10 | 11 | easy-statemachine-demo 12 | 1.0.0 13 | 14 | easy-statemachine-demo 15 | http://www.example.com 16 | 17 | 18 | 19 | com.ambitor.framework 20 | easy-statemachine-wokflow 21 | 1.0.0 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | ${spring.boot.version} 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-test 32 | ${spring.boot.version} 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-compiler-plugin 41 | 42 | ${maven.compiler.source} 43 | ${maven.compiler.source} 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/parser/yml/StateMachineYmlConfig.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.parser.yml; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 状态机Yml配置 10 | * Created by Ambitor on 2019/4/9 11 | * @author Ambitor 12 | */ 13 | @Getter 14 | @Setter 15 | public class StateMachineYmlConfig { 16 | private String name; 17 | private List interceptor; 18 | private StateEntry states; 19 | private List events; 20 | private List transitions; 21 | 22 | @Getter 23 | @Setter 24 | public static class StateEntry { 25 | private String init; 26 | private List suspend; 27 | private List end; 28 | private List other; 29 | } 30 | 31 | @Getter 32 | @Setter 33 | public static class TransitionEntry { 34 | private String type; 35 | private String source; 36 | private String target; 37 | private String event; 38 | private String action; 39 | private String errorAction; 40 | private ChoiceTransitionVO first; 41 | private ChoiceTransitionVO then; 42 | private ChoiceTransitionVO last; 43 | } 44 | 45 | @Getter 46 | @Setter 47 | public static class ChoiceTransitionVO { 48 | private String status; 49 | private String equals; 50 | private String target; 51 | 52 | @Override 53 | public String toString() { 54 | return "ChoiceTransitionVO{" + 55 | "status='" + status + '\'' + 56 | ", equals='" + equals + '\'' + 57 | ", target='" + target + '\'' + 58 | '}'; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/coder/ChannelBufferByteInput.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.coder; 2 | 3 | 4 | import io.netty.buffer.ByteBuf; 5 | import org.jboss.marshalling.ByteInput; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * @Description: netty源码,因为引入不了所以直接复制出来 11 | * Created by Ambitor on 2017/4/26. 12 | */ 13 | class ChannelBufferByteInput implements ByteInput { 14 | private final ByteBuf buffer; 15 | 16 | ChannelBufferByteInput(ByteBuf buffer) { 17 | this.buffer = buffer; 18 | } 19 | 20 | @Override 21 | public void close() { 22 | } 23 | 24 | @Override 25 | public int available() { 26 | return this.buffer.readableBytes(); 27 | } 28 | 29 | @Override 30 | public int read() { 31 | return this.buffer.isReadable() ? this.buffer.readByte() & 255 : -1; 32 | } 33 | 34 | @Override 35 | public int read(byte[] array) throws IOException { 36 | return this.read(array, 0, array.length); 37 | } 38 | 39 | @Override 40 | public int read(byte[] dst, int dstIndex, int length) throws IOException { 41 | int available = this.available(); 42 | if (available == 0) { 43 | return -1; 44 | } else { 45 | length = Math.min(available, length); 46 | this.buffer.readBytes(dst, dstIndex, length); 47 | return length; 48 | } 49 | } 50 | 51 | @Override 52 | public long skip(long bytes) { 53 | int readable = this.buffer.readableBytes(); 54 | if ((long) readable < bytes) { 55 | bytes = (long) readable; 56 | } 57 | 58 | this.buffer.readerIndex((int) ((long) this.buffer.readerIndex() + bytes)); 59 | return bytes; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/vo/Header.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.vo; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @Description: ${todo}(描述完成的功能) 8 | * Created by Ambitor on 2017/4/26. 9 | */ 10 | public class Header { 11 | private int crcCode = 0xABEF0101; 12 | private int length; 13 | private long sessionID; 14 | private byte type; 15 | private byte priority; 16 | private Map attachment = new HashMap<>(); 17 | 18 | public Map getAttachment() { 19 | return attachment; 20 | } 21 | 22 | public void setAttachment(Map attachment) { 23 | this.attachment = attachment; 24 | } 25 | 26 | public int getCrcCode() { 27 | return crcCode; 28 | } 29 | 30 | public void setCrcCode(int crcCode) { 31 | this.crcCode = crcCode; 32 | } 33 | 34 | public int getLength() { 35 | return length; 36 | } 37 | 38 | public void setLength(int length) { 39 | this.length = length; 40 | } 41 | 42 | public byte getPriority() { 43 | return priority; 44 | } 45 | 46 | public void setPriority(byte priority) { 47 | this.priority = priority; 48 | } 49 | 50 | public long getSessionID() { 51 | return sessionID; 52 | } 53 | 54 | public void setSessionID(long sessionID) { 55 | this.sessionID = sessionID; 56 | } 57 | 58 | public byte getType() { 59 | return type; 60 | } 61 | 62 | public Header setType(byte type) { 63 | this.type = type; 64 | return this; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return new StringBuilder("crcCode:").append(crcCode).toString(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/config/ChoiceTransitionConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition.config; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.guard.Guard; 6 | 7 | /** 8 | * 选择转换器配置 9 | * Created by Ambitor on 2019/1/21 10 | * @param 状态 11 | * @param 事件 12 | */ 13 | public interface ChoiceTransitionConfigurer extends TransitionConfigurer { 14 | /** 15 | * 源状态 16 | * @param source 源状态S 17 | */ 18 | ChoiceTransitionConfigurer source(S source); 19 | 20 | /** 21 | * 触发状态扭转 Source -> Target 的事件 22 | * @param event 事件E 23 | */ 24 | ChoiceTransitionConfigurer event(E event); 25 | 26 | /** 27 | * 等同于if 28 | * @param guard 如果返回true 29 | * @param target 则状态变更成target 30 | * @return 31 | */ 32 | ChoiceTransitionConfigurer first(S target, Guard guard); 33 | 34 | /** 35 | * 等同于 else if 36 | * @param guard 如果返回true 37 | * @param target 则状态变更成target 38 | * @return 39 | */ 40 | ChoiceTransitionConfigurer then(S target, Guard guard); 41 | 42 | /** 43 | * 等同于 else 44 | * @param target 如果first then 都不使用,变更状态target 45 | */ 46 | ChoiceTransitionConfigurer last(S target); 47 | 48 | 49 | /** 50 | * action执行成功,状态扭转 source -> target 51 | * 如抛出异常则状态不会扭转 52 | * @param action 事件触发后执行的action 53 | */ 54 | ChoiceTransitionConfigurer action(Action action); 55 | 56 | /** 57 | * action执行成功状态从 source -> target 58 | * 注意,如果有error函数,当action执行失败时会调用error,且如果error执行成功无异常后 59 | * 状态也会扭转 source -> target 60 | */ 61 | ChoiceTransitionConfigurer action(Action action, Action error); 62 | } 63 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.ambitor.framework 8 | easy-statemachine 9 | 1.0.0 10 | 11 | easy-statemachine-wokflow 12 | 1.0.0 13 | 14 | easy-statemachine-wokflow 15 | http://www.example.com 16 | 17 | 18 | 19 | com.ambitor.framework 20 | easy-statemachine-core 21 | 1.0.0 22 | 23 | 24 | org.springframework 25 | spring-tx 26 | RELEASE 27 | 28 | 29 | org.apache.commons 30 | commons-lang3 31 | RELEASE 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-compiler-plugin 40 | 41 | ${maven.compiler.source} 42 | ${maven.compiler.source} 43 | 44 | 45 | 46 | 47 | 48 | 49 | nexus-releases 50 | ${deploy.name} 51 | ${deploy.url} 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/resources/script/init.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `state_machine_task` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `machine_code` varchar(48) DEFAULT NULL COMMENT '状态机唯一code', 4 | `machine_state` varchar(48) DEFAULT NULL COMMENT '状态机业务状态', 5 | `machine_type` varchar(48) DEFAULT NULL COMMENT '状态机业务类型,比如360、幼儿园等', 6 | `scan_status` varchar(48) DEFAULT NULL COMMENT '扫描状态''open'',''running'',''error'',''suspend''''close''', 7 | `transaction_id` varchar(128) DEFAULT NULL COMMENT '唯一key', 8 | `request_data` text COMMENT '请求参数', 9 | `response_data` mediumtext COMMENT 'task执行返回', 10 | `current_trytimes` int(11) DEFAULT '0' COMMENT '当前重试次数', 11 | `retry_times` int(11) DEFAULT '3' COMMENT '总重试次数', 12 | `next_run_time` datetime DEFAULT NULL COMMENT '下次执行时间', 13 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP, 14 | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 15 | PRIMARY KEY (`id`), 16 | KEY `idx_scanstatus_next_run_time` (`scan_status`,`next_run_time`), 17 | KEY `idx_transaction_id` (`transaction_id`), 18 | KEY `idx_update_time_type` (`update_time`,`machine_type`,`scan_status`) 19 | ) ENGINE=InnoDB AUTO_INCREMENT=865 DEFAULT CHARSET=utf8mb4 COMMENT='状态机'; 20 | 21 | CREATE TABLE `state_machine_log` ( 22 | `id` int(11) NOT NULL AUTO_INCREMENT, 23 | `machine_code` varchar(48) DEFAULT NULL COMMENT '状态机code', 24 | `source` varchar(48) DEFAULT NULL COMMENT '源状态', 25 | `target` varchar(48) DEFAULT NULL COMMENT '目标状态', 26 | `event` varchar(48) DEFAULT NULL COMMENT '事件', 27 | `transition_result` varchar(48) DEFAULT NULL COMMENT '状态扭转结果', 28 | `request` text COMMENT '请求参数', 29 | `response` mediumtext COMMENT '返回信息', 30 | `create_time` datetime DEFAULT CURRENT_TIMESTAMP, 31 | `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 32 | PRIMARY KEY (`id`), 33 | KEY `machine_code_idx` (machine_code) 34 | ) ENGINE=InnoDB AUTO_INCREMENT=2879 DEFAULT CHARSET=utf8mb4 -------------------------------------------------------------------------------- /easy-statemachine-core/easy-statemachine-core.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/config/DefaultStandardTransitionConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition.config; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.exception.StateMachineException; 6 | 7 | /** 8 | * 默认标准转换配置 9 | * Created by Ambitor on 2019/1/21 10 | * @param 状态 11 | * @param 事件 12 | * @author Ambitor 13 | */ 14 | public class DefaultStandardTransitionConfigurer extends BaseTransitionConfigurer 15 | implements StandardTransitionConfigurer { 16 | 17 | private S target; 18 | 19 | /** 20 | * 外部事件才会导致状态扭转 21 | * @param target 22 | */ 23 | @Override 24 | public DefaultStandardTransitionConfigurer target(S target) { 25 | setTarget(target); 26 | return this; 27 | } 28 | 29 | 30 | @Override 31 | public DefaultStandardTransitionConfigurer source(S source) { 32 | setSource(source); 33 | return this; 34 | } 35 | 36 | @Override 37 | public DefaultStandardTransitionConfigurer event(E event) { 38 | setEvent(event); 39 | return this; 40 | } 41 | 42 | @Override 43 | public DefaultStandardTransitionConfigurer action(Action action) { 44 | if (action == null) { 45 | throw new StateMachineException(getSource() + "action can not be null"); 46 | } 47 | addAction(action); 48 | return this; 49 | } 50 | 51 | @Override 52 | public DefaultStandardTransitionConfigurer action(Action action, Action error) { 53 | if (action == null) { 54 | throw new StateMachineException(getSource() + " source action can not be null"); 55 | } 56 | addAction(action, error); 57 | return this; 58 | } 59 | 60 | public S getTarget() { 61 | return target; 62 | } 63 | 64 | public void setTarget(S target) { 65 | this.target = target; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/coder/MarshallingCodeCFactory.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.coder; 2 | 3 | import io.netty.handler.codec.marshalling.DefaultMarshallerProvider; 4 | import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider; 5 | import io.netty.handler.codec.marshalling.MarshallerProvider; 6 | import io.netty.handler.codec.marshalling.UnmarshallerProvider; 7 | import org.jboss.marshalling.Marshaller; 8 | import org.jboss.marshalling.MarshallerFactory; 9 | import org.jboss.marshalling.Marshalling; 10 | import org.jboss.marshalling.MarshallingConfiguration; 11 | import org.jboss.marshalling.Unmarshaller; 12 | 13 | /** 14 | * Marshalling工厂 15 | */ 16 | public final class MarshallingCodeCFactory { 17 | 18 | public static Marshaller buildMarshaller() throws Exception { 19 | final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial"); 20 | final MarshallingConfiguration configuration = new MarshallingConfiguration(); 21 | configuration.setVersion(5); 22 | MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration); 23 | // 构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组 24 | return provider.getMarshaller(null); 25 | } 26 | 27 | public static Unmarshaller buildUnmarshaller() throws Exception { 28 | // 首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。 29 | final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial"); 30 | // 创建了MarshallingConfiguration对象,配置了版本号为5 31 | final MarshallingConfiguration configuration = new MarshallingConfiguration(); 32 | configuration.setVersion(5); 33 | // 根据marshallerFactory和configuration创建provider 34 | UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration); 35 | // 构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度 36 | return provider.getUnmarshaller(null); 37 | } 38 | } -------------------------------------------------------------------------------- /easy-statemachine-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.ambitor.framework 8 | easy-statemachine 9 | 1.0.0 10 | 11 | easy-statemachine-core 12 | 1.0.0 13 | 14 | easy-statemachine-core 15 | http://www.example.com 16 | 17 | 18 | 19 | 20 | org.projectlombok 21 | lombok 22 | ${lombok.version} 23 | 24 | 25 | 26 | org.slf4j 27 | slf4j-api 28 | 1.7.25 29 | 30 | 31 | 32 | com.alibaba 33 | fastjson 34 | ${fastjson.version} 35 | 36 | 37 | org.springframework 38 | spring-context 39 | RELEASE 40 | 41 | 42 | 43 | org.yaml 44 | snakeyaml 45 | ${snakeyaml.version} 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-compiler-plugin 55 | 56 | ${maven.compiler.source} 57 | ${maven.compiler.source} 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/action/WorkFlowAction.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.action; 2 | 3 | import ambitor.easy.statemachine.core.action.Action; 4 | import ambitor.easy.statemachine.core.context.MessageHeaders; 5 | import ambitor.easy.statemachine.core.context.StateContext; 6 | import ambitor.easy.statemachine.core.exception.StateMachineException; 7 | import ambitor.easy.statemachine.core.exception.StateMachineRetryException; 8 | import ambitor.easy.statemachine.workflow.model.StateMachineTask; 9 | 10 | import java.util.function.Consumer; 11 | 12 | import static ambitor.easy.statemachine.workflow.model.StateMachineConstant.TASK_HEADER; 13 | 14 | /** 15 | * 抽象Action,提供方法,方便Action实现类操作Task 16 | * Created by Ambitor on 2019/4/3 17 | * @author Ambitor 18 | */ 19 | public interface WorkFlowAction extends Action { 20 | /** 21 | * 从上下文中拿到StateMachineTask对象 22 | * @param context 上下文 23 | * @return task 24 | */ 25 | default StateMachineTask getStateMachineTask(StateContext context) { 26 | MessageHeaders headers = context.getMessage().getHeaders(); 27 | return (StateMachineTask) headers.getHeader(TASK_HEADER); 28 | } 29 | 30 | /** 31 | * 异常处理Action, action抛出异常后会进入errorAction,详细请看Actions.errorCallingAction实现 32 | * @param consumer 业务逻辑 33 | * @return errorAction 34 | */ 35 | default Action errorAction(Consumer> consumer) { 36 | return (s) -> { 37 | StateMachineTask task = getStateMachineTask(s); 38 | //如果是最后一次重试 39 | Exception e = s.getException(); 40 | if (e == null) { 41 | throw new StateMachineRetryException("未知异常"); 42 | } 43 | boolean isRetryException = e instanceof StateMachineRetryException; 44 | //非重试异常 45 | if (!isRetryException) { 46 | consumer.accept(s); 47 | } else { 48 | //重试异常且最后一次尝试 49 | if (task.isLastRetry()) { 50 | consumer.accept(s); 51 | } else { 52 | throw (StateMachineRetryException) e; 53 | } 54 | } 55 | }; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/coder/NettyMessageDecoder.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.coder; 2 | 3 | import ambitor.easy.statemachine.messaging.vo.Header; 4 | import ambitor.easy.statemachine.messaging.vo.NettyMessage; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.LengthFieldBasedFrameDecoder; 8 | 9 | import java.nio.charset.Charset; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | /** 14 | * @Description: 私有协议解码 15 | * Created by Ambitor on 2017/4/26. 16 | */ 17 | public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder { 18 | 19 | private MarshallingDecoder marshallingDecoder; 20 | 21 | public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) throws Exception { 22 | super(maxFrameLength, lengthFieldOffset, lengthFieldLength); 23 | marshallingDecoder = new MarshallingDecoder(); 24 | } 25 | 26 | protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { 27 | ByteBuf frame = (ByteBuf) super.decode(ctx, in); 28 | //这种情况一般是半包的时候 29 | if (frame == null) return null; 30 | 31 | NettyMessage message = new NettyMessage(); 32 | Header header = new Header(); 33 | header.setCrcCode(frame.readInt()); 34 | header.setLength(frame.readInt()); 35 | header.setSessionID(frame.readLong()); 36 | header.setType(frame.readByte()); 37 | header.setPriority(frame.readByte()); 38 | 39 | int attachmentSize = frame.readInt(); 40 | Map attachment = new HashMap<>(); 41 | for (int i = 0; i < attachmentSize; i++) { 42 | int keySize = frame.readInt(); 43 | byte[] keyByte = new byte[keySize]; 44 | frame.readBytes(keyByte); 45 | String key = new String(keyByte, Charset.forName("UTF-8")); 46 | Object value = marshallingDecoder.decode(frame); 47 | attachment.put(key, value); 48 | } 49 | header.setAttachment(attachment); 50 | message.setHeader(header); 51 | Object body = marshallingDecoder.decode(frame); 52 | message.setBody(body); 53 | return message; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/exception/StateMachineException.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.exception; 2 | 3 | import java.text.MessageFormat; 4 | 5 | /** 6 | * 状态机异常 7 | * Created by Ambitor on 2019/1/21 8 | */ 9 | public class StateMachineException extends RuntimeException { 10 | static final long serialVersionUID = -7034897190745766939L; 11 | private int code = -1; 12 | private Object[] args; 13 | private boolean notPrintStack; 14 | 15 | public StateMachineException(int code) { 16 | this.code = code; 17 | } 18 | 19 | public StateMachineException(int code, String message) { 20 | super(message); 21 | this.code = code; 22 | } 23 | 24 | public StateMachineException(int code, boolean notPrintStack) { 25 | this.notPrintStack = notPrintStack; 26 | this.code = code; 27 | } 28 | 29 | public StateMachineException(int code, Throwable cause) { 30 | super(null, cause); 31 | this.code = code; 32 | } 33 | 34 | public StateMachineException(int code, Object[] args) { 35 | this.code = code; 36 | this.args = args; 37 | } 38 | 39 | public StateMachineException(String message) { 40 | super(message); 41 | } 42 | 43 | public StateMachineException(String message, Object... params) { 44 | super(MessageFormat.format(message, params)); 45 | } 46 | 47 | public StateMachineException(Throwable cause, String message, Object... params) { 48 | super(MessageFormat.format(message, params), cause); 49 | } 50 | 51 | public StateMachineException(String message, Throwable cause) { 52 | super(message, cause); 53 | } 54 | 55 | public StateMachineException(Throwable cause) { 56 | super(cause); 57 | } 58 | 59 | protected StateMachineException(String message, Throwable cause, 60 | boolean enableSuppression, 61 | boolean writableStackTrace) { 62 | super(message, cause, enableSuppression, writableStackTrace); 63 | } 64 | 65 | public int getCode() { 66 | return code; 67 | } 68 | 69 | public Object[] getArgs() { 70 | return args; 71 | } 72 | 73 | public boolean isNotPrintStack() { 74 | return notPrintStack; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/state/DefaultState.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.state; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.context.Message; 6 | 7 | import java.util.Collection; 8 | 9 | /** 10 | * 默认的状态实体类 11 | * Created by Ambitor on 2019/1/21 12 | * @param 状态 13 | * @param 事件 14 | */ 15 | public class DefaultState implements State { 16 | 17 | private S state; 18 | private boolean initial = false; 19 | private boolean suspend = false; 20 | private boolean end = false; 21 | private Collection> entryActions; 22 | private Collection> exitActions; 23 | 24 | public DefaultState(S state, boolean initial, boolean suspend, boolean end, 25 | Collection> entryActions, 26 | Collection> exitActions) { 27 | this.state = state; 28 | this.initial = initial; 29 | this.suspend = suspend; 30 | this.end = end; 31 | this.entryActions = entryActions; 32 | this.exitActions = exitActions; 33 | } 34 | 35 | 36 | /** 37 | * 发送事件的包装类. 38 | * @param event 事件包装类 39 | * @return 如果事件被接受返回true 否则false 40 | */ 41 | @Override 42 | public boolean sendEvent(Message event) { 43 | return false; 44 | } 45 | 46 | /** 47 | * 获取状态ID 48 | */ 49 | @Override 50 | public S getId() { 51 | return state; 52 | } 53 | 54 | @Override 55 | public boolean isInitial() { 56 | return initial; 57 | } 58 | 59 | @Override 60 | public boolean isEnd() { 61 | return end; 62 | } 63 | 64 | /** 65 | * 是否为挂起状态 66 | */ 67 | @Override 68 | public boolean isSuspend() { 69 | return suspend; 70 | } 71 | 72 | /** 73 | * 进入Actions 74 | * @return actions 75 | */ 76 | @Override 77 | public Collection> getEntryActions() { 78 | return entryActions; 79 | } 80 | 81 | /** 82 | * 所有退出的actions 83 | * @return actions 84 | */ 85 | @Override 86 | public Collection> getExitActions() { 87 | return exitActions; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /easy-statemachine-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.ambitor.framework 8 | easy-statemachine 9 | 1.0.0 10 | 11 | easy-statemachine-common 12 | 1.0.0 13 | easy-statemachine-common 14 | http://www.example.com 15 | 16 | 17 | 18 | 19 | org.projectlombok 20 | lombok 21 | ${lombok.version} 22 | 23 | 24 | 25 | org.slf4j 26 | slf4j-api 27 | 1.7.25 28 | 29 | 30 | 31 | io.netty 32 | netty-all 33 | 5.0.0.Alpha2 34 | 35 | 36 | 37 | org.jboss.marshalling 38 | jboss-marshalling 39 | 1.4.11.Final 40 | 41 | 42 | 43 | org.jboss.marshalling 44 | jboss-marshalling-serial 45 | 1.4.11.Final 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-compiler-plugin 55 | 56 | ${maven.compiler.source} 57 | ${maven.compiler.source} 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/coder/NettyMessageEncoder.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.coder; 2 | 3 | import ambitor.easy.statemachine.messaging.vo.NettyMessage; 4 | import io.netty.buffer.ByteBuf; 5 | import io.netty.buffer.Unpooled; 6 | import io.netty.channel.ChannelHandlerContext; 7 | import io.netty.handler.codec.MessageToMessageEncoder; 8 | 9 | import java.nio.charset.Charset; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * @Description: 私有协议编码 15 | * Created by Ambitor on 2017/4/26. 16 | */ 17 | public class NettyMessageEncoder extends MessageToMessageEncoder { 18 | 19 | private MarshallingEncoder marshallingEncoder; 20 | 21 | public NettyMessageEncoder() throws Exception { 22 | marshallingEncoder = new MarshallingEncoder(); 23 | } 24 | 25 | @Override 26 | protected void encode(ChannelHandlerContext ctx, NettyMessage nettyMessage, List list) throws Exception { 27 | if (nettyMessage == null || nettyMessage.getHeader() == null) { 28 | throw new Exception("nettyMessage or nettyMessage.getHeader 为空"); 29 | } 30 | ByteBuf byteBuf = Unpooled.buffer(); 31 | byteBuf.writeInt(nettyMessage.getHeader().getCrcCode()); 32 | byteBuf.writeInt(nettyMessage.getHeader().getLength()); 33 | byteBuf.writeLong(nettyMessage.getHeader().getSessionID()); 34 | byteBuf.writeByte(nettyMessage.getHeader().getType()); 35 | byteBuf.writeByte(nettyMessage.getHeader().getPriority()); 36 | byteBuf.writeInt(nettyMessage.getHeader().getAttachment().size()); 37 | for (Map.Entry entry : nettyMessage.getHeader().getAttachment().entrySet()) { 38 | String key = entry.getKey(); 39 | Object attachment = entry.getValue(); 40 | byte[] keyByte = key.getBytes(Charset.forName("UTF-8")); 41 | byteBuf.writeInt(keyByte.length); 42 | byteBuf.writeBytes(keyByte); 43 | marshallingEncoder.encode(attachment, byteBuf); 44 | } 45 | if (nettyMessage.getBody() != null) { 46 | marshallingEncoder.encode(nettyMessage.getBody(), byteBuf); 47 | } else { 48 | //body长度为0 49 | byteBuf.writeInt(0); 50 | //往第5个字节中写入消息的总长度 51 | } 52 | byteBuf.setInt(4, byteBuf.readableBytes() - 8); 53 | ctx.writeAndFlush(byteBuf); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/StateMachine.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core; 2 | 3 | import ambitor.easy.statemachine.core.context.Message; 4 | import ambitor.easy.statemachine.core.context.MessageHeaders; 5 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorList; 6 | import ambitor.easy.statemachine.core.state.State; 7 | import ambitor.easy.statemachine.core.transition.Transition; 8 | 9 | import java.util.Collection; 10 | import java.util.Map; 11 | 12 | /** 13 | * 状态机接口 14 | * Created by Ambitor on 2019/1/21 15 | * @param 状态 16 | * @param 事件 17 | */ 18 | public interface StateMachine { 19 | /** 20 | * 初始化状态 21 | */ 22 | State getInitialState(); 23 | 24 | /** 25 | * 触发事件 26 | * @param event Message 27 | * @return 状态机是否接受事件 28 | */ 29 | boolean sendEvent(Message event); 30 | 31 | /** 32 | * 触发事件 33 | * @param event E 34 | * @return 状态机是否接受事件 35 | */ 36 | boolean sendEvent(E event); 37 | 38 | /** 39 | * 开始执行状态机,并自动事件驱动所有状态扭转,直到有事件不被接受或事件中发生异常 40 | */ 41 | boolean start(); 42 | 43 | /** 44 | * 开始执行状态机,并自动事件驱动所有状态扭转,直到有事件不被接受或事件中发生异常 45 | * @param headers 传入参数,可在触发事件中使用 46 | */ 47 | boolean start(MessageHeaders headers); 48 | 49 | /** 50 | * 获取状态机当前状态 51 | * @return S 52 | */ 53 | State getState(); 54 | 55 | /** 56 | * 获取当前事件 57 | */ 58 | Message getEvent(); 59 | 60 | /** 61 | * 重置状态机当前状态 62 | * @param newState S 63 | */ 64 | void resetStateMachine(S newState); 65 | 66 | /** 67 | * 获取状态机所有状态集合 68 | */ 69 | Collection> getStates(); 70 | 71 | /** 72 | * 获取状态机所有转换器 73 | */ 74 | Map>> getTransitions(); 75 | 76 | /** 77 | * 当前转换器 78 | */ 79 | Transition transition(); 80 | 81 | /** 82 | * 状态机是否完成,如果有异常不接受事件或扭转到end状态 都是完成 83 | */ 84 | boolean isComplete(); 85 | 86 | /** 87 | * 获取状态机所有拦截器 88 | * @return 89 | */ 90 | StateMachineInterceptorList interceptors(); 91 | 92 | /** 93 | * 设置状态机异常 94 | */ 95 | void setStateMachineError(Exception exception); 96 | 97 | /** 98 | * 状态机是否有异常 99 | */ 100 | Exception getStateMachineError(); 101 | 102 | } 103 | -------------------------------------------------------------------------------- /easy-statemachine-demo/README.md: -------------------------------------------------------------------------------- 1 | # 轻量级状态机工作流引擎 2 | 3 | ### Demo场景介绍 4 | 5 | 6 | #### yml配置方式 7 | 简单的配置一个yml配置文件,然后编写BuyFood.class、CookRice.class、CookFood.class、EatRice.class、WifeWash.class、 HusbandWash.class的业务逻辑实现类,为方便理解Demo状态和事件都以中文表示 8 | 9 | ``` 10 | #状态机名称 11 | name: eat 12 | 13 | #拦截器配置 14 | interceptor: 15 | - PersistStateMachineInterceptor.class 16 | 17 | #状态配置 18 | states: 19 | init: 准备食材 20 | suspend: 21 | # - 不用 22 | end: 23 | - 休息中 24 | other: 25 | - 准备米饭 26 | - 准备炒菜 27 | - 准备吃饭 28 | - 老公准备洗碗 #-----谁洗碗由EatRice.classg根据逻辑返回 29 | - 老婆准备洗碗 30 | 31 | #事件配置 32 | events: 33 | - 买菜 34 | - 煮饭 35 | - 炒菜 36 | - 吃饭 37 | - 洗碗 38 | 39 | #转换器配置 40 | transitions: 41 | - type: standard 42 | source: 准备食材 43 | target: 准备米饭 44 | event: 买菜 45 | action: BuyFood.class 46 | errorAction: 47 | 48 | - type: standard 49 | source: 准备米饭 50 | target: 准备炒菜 51 | event: 煮饭 52 | action: CookRice.class 53 | errorAction: 54 | 55 | - type: standard 56 | source: 准备炒菜 57 | target: 准备吃饭 58 | event: 炒菜 59 | action: CookFood.class 60 | errorAction: 61 | 62 | - type: choice 63 | source: 准备吃饭 64 | event: 吃饭 65 | action: EatRice.class 66 | first: {status: Eat_Status,equals: Husband,target: 老公准备洗碗} 67 | last: {target: 老婆准备洗碗} 68 | 69 | - type: standard 70 | source: 老婆准备洗碗 71 | target: 休息中 72 | event: 洗碗 73 | action: WifeWash.class 74 | 75 | - type: standard 76 | source: 老公准备洗碗 77 | target: 休息中 78 | event: 洗碗 79 | action: HusbandWash.class 80 | 81 | ``` 82 | ####代码调用 83 | ``` 84 | public class DemoTest{ 85 | 86 | @Autowired 87 | private ApplicationContext context 88 | 89 | public void runStateMachine(){ 90 | //根据状态机name获取状态机配置Bean 91 | StateMachineConfigurer configurer = context.getBean("eat", StateMachineConfigurer.class); 92 | //构建状态机实例 93 | StateMachine stateMachine = StateMachineFactory.build(configurer); 94 | //工作流上下文,整个流程中共享 95 | MessageHeaders headers = new MessageHeaders(); 96 | headers.addHeader(TASK_HEADER, task); 97 | //设置工作流上下文,可为null 98 | stateMachine.start(headers); 99 | } 100 | } 101 | ``` 102 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/handler/HeartbeatHandler.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.handler; 2 | 3 | import ambitor.easy.statemachine.messaging.vo.Header; 4 | import ambitor.easy.statemachine.messaging.vo.MessageType; 5 | import ambitor.easy.statemachine.messaging.vo.NettyMessage; 6 | import io.netty.channel.ChannelHandlerAdapter; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.util.concurrent.ScheduledFuture; 9 | 10 | import java.util.Date; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | /** 14 | * @Description: 心跳维持 15 | * Created by Ambitor on 2017/4/26. 16 | */ 17 | public class HeartbeatHandler extends ChannelHandlerAdapter { 18 | 19 | private ScheduledFuture heartbeatScheduled; 20 | 21 | @Override 22 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 23 | NettyMessage request = (NettyMessage) msg; 24 | if (request.getHeader() == null) { 25 | ctx.fireChannelRead(msg); 26 | } 27 | if (request.getHeader().getType() == MessageType.ACCEPT_RES) { 28 | heartbeatScheduled = ctx.executor().scheduleAtFixedRate(new HeartbeatTask(ctx), 0, 30, TimeUnit.SECONDS); 29 | 30 | } else if (request.getHeader().getType() == MessageType.HEARTBEAT_REQ) { 31 | NettyMessage response = new NettyMessage(); 32 | response.setHeader(new Header().setType(MessageType.HEARTBEAT_RES)); 33 | response.setBody("心跳回应信息."); 34 | ctx.writeAndFlush(response); 35 | System.out.println("收到客户端心跳包,即将返回pong消息..." + request.getBody() + new Date()); 36 | } else if (request.getHeader().getType() == MessageType.HEARTBEAT_RES) { 37 | System.out.println("心跳正常..." + new Date()); 38 | } 39 | } 40 | 41 | @Override 42 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 43 | ctx.fireExceptionCaught(cause); 44 | } 45 | 46 | class HeartbeatTask implements Runnable { 47 | 48 | private ChannelHandlerContext ctx; 49 | 50 | public HeartbeatTask(ChannelHandlerContext ctx) { 51 | this.ctx = ctx; 52 | } 53 | 54 | @Override 55 | public void run() { 56 | NettyMessage heartbeatMsg = new NettyMessage(); 57 | heartbeatMsg.setHeader(new Header().setType(MessageType.HEARTBEAT_REQ)); 58 | heartbeatMsg.setBody("客户端心跳包..."); 59 | ctx.writeAndFlush(heartbeatMsg); 60 | System.out.println("发送心跳包" + new Date()); 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/context/DefaultStateContext.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.context; 2 | 3 | 4 | import ambitor.easy.statemachine.core.state.State; 5 | import ambitor.easy.statemachine.core.transition.Transition; 6 | 7 | /** 8 | * 默认状态机上下文 9 | * Created by Ambitor on 2019-01-21 10 | * @param 状态 11 | * @param 事件 12 | */ 13 | public class DefaultStateContext implements StateContext { 14 | 15 | private final Message message; 16 | private final Transition transition; 17 | private final State source; 18 | private final State target; 19 | private Exception exception; 20 | 21 | /** 22 | * 构造函数 23 | * @param message the message 24 | * @param transition the transition 25 | * @param source the source 26 | * @param target the target 27 | */ 28 | public DefaultStateContext(Message message, Transition transition, State source, 29 | State target) { 30 | this.message = message; 31 | this.transition = transition; 32 | this.source = source; 33 | this.target = target; 34 | } 35 | 36 | public DefaultStateContext(Message message, Transition transition, State source, 37 | State target, Exception e) { 38 | this.message = message; 39 | this.transition = transition; 40 | this.source = source; 41 | this.target = target; 42 | this.exception = e; 43 | } 44 | 45 | @Override 46 | public E getEvent() { 47 | return message != null ? message.getPayload() : null; 48 | } 49 | 50 | @Override 51 | public Message getMessage() { 52 | return message; 53 | } 54 | 55 | 56 | @Override 57 | public Transition getTransition() { 58 | return transition; 59 | } 60 | 61 | @Override 62 | public State getSource() { 63 | return source != null ? source : (transition != null ? transition.getSource() : null); 64 | } 65 | 66 | @Override 67 | public State getTarget() { 68 | return target; 69 | } 70 | 71 | @Override 72 | public Exception getException() { 73 | return exception; 74 | } 75 | 76 | public void setException(Exception exception) { 77 | this.exception = exception; 78 | } 79 | 80 | @Override 81 | public String toString() { 82 | return "DefaultStateContext [ message=" + message + 83 | ", transition=" + transition + ", source=" + source + ", target=" 84 | + target + ", exception=" + exception + "]"; 85 | } 86 | } -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/ProtocolServer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging; 2 | 3 | import ambitor.easy.statemachine.messaging.coder.NettyMessageDecoder; 4 | import ambitor.easy.statemachine.messaging.coder.NettyMessageEncoder; 5 | import ambitor.easy.statemachine.messaging.handler.HeartbeatHandler; 6 | import ambitor.easy.statemachine.messaging.handler.LoginResponseHandler; 7 | import io.netty.bootstrap.ServerBootstrap; 8 | import io.netty.channel.ChannelFuture; 9 | import io.netty.channel.ChannelInitializer; 10 | import io.netty.channel.ChannelOption; 11 | import io.netty.channel.ChannelPipeline; 12 | import io.netty.channel.EventLoopGroup; 13 | import io.netty.channel.nio.NioEventLoopGroup; 14 | import io.netty.channel.socket.SocketChannel; 15 | import io.netty.channel.socket.nio.NioServerSocketChannel; 16 | 17 | /** 18 | * 服务端 19 | * @Description: Created by Ambitor on 2017/4/26. 20 | */ 21 | public class ProtocolServer { 22 | 23 | public void bind(String host, int port) { 24 | 25 | EventLoopGroup acceptGroup = new NioEventLoopGroup(); 26 | EventLoopGroup serverGroup = new NioEventLoopGroup(); 27 | 28 | try { 29 | ServerBootstrap bootstrap = new ServerBootstrap(); 30 | bootstrap.group(acceptGroup, serverGroup) 31 | .channel(NioServerSocketChannel.class) 32 | .option(ChannelOption.SO_BACKLOG, 100) 33 | .childHandler(new ChannelInitializer() { 34 | @Override 35 | protected void initChannel(SocketChannel ch) throws Exception { 36 | ChannelPipeline pipeline = ch.pipeline(); 37 | pipeline.addLast("NettyMessageDecoder", new NettyMessageDecoder(1024 * 1024 * 1, 4, 4)); 38 | pipeline.addLast("NettyMessageEncoder", new NettyMessageEncoder()); 39 | pipeline.addLast("LoginResponseHandler", new LoginResponseHandler()); 40 | pipeline.addLast("HeartbeatHandler", new HeartbeatHandler()); 41 | } 42 | }); 43 | ChannelFuture future = bootstrap.bind(host, port).sync(); 44 | System.out.println("服务器启动成功."); 45 | future.channel().closeFuture().sync(); 46 | } catch (InterruptedException e) { 47 | acceptGroup.shutdownGracefully(); 48 | serverGroup.shutdownGracefully(); 49 | System.out.println("服务器中止服务."); 50 | } finally { 51 | 52 | } 53 | } 54 | 55 | public static void main(String[] args) { 56 | new ProtocolServer().bind("127.0.0.1", 9080); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.ambitor.framework 6 | easy-statemachine 7 | 1.0.0 8 | pom 9 | 10 | easy-statemachine 11 | http://maven.apache.org 12 | 13 | 14 | easy-statemachine-core 15 | easy-statemachine-wokflow 16 | easy-statemachine-demo 17 | easy-statemachine-common 18 | 19 | 20 | 21 | UTF-8 22 | 1.8 23 | 1.8 24 | 1.5.7.RELEASE 25 | 1.2.39 26 | 1.16.16 27 | 1.24 28 | http://maven.tengsaw.cn:9081/repository/xy-maven-public/ 29 | jn-maven-public-release 30 | 31 | 32 | 33 | 34 | 35 | org.apache.maven.plugins 36 | maven-surefire-plugin 37 | 2.20 38 | 39 | true 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-javadoc-plugin 45 | 3.0.1 46 | 47 | UTF-8 48 | UTF-8 49 | false 50 | false 51 | 52 | 53 | biz-hx-api:test.hxapi.* 54 | 55 | 56 | 57 | 58 | attach-javadocs 59 | package 60 | 61 | -Xdoclint:none 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | nexus-releases 71 | ${deploy.name} 72 | ${deploy.url} 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/handler/LoginResponseHandler.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging.handler; 2 | 3 | import ambitor.easy.statemachine.messaging.vo.Header; 4 | import ambitor.easy.statemachine.messaging.vo.MessageType; 5 | import ambitor.easy.statemachine.messaging.vo.NettyMessage; 6 | import io.netty.channel.ChannelHandlerAdapter; 7 | import io.netty.channel.ChannelHandlerContext; 8 | 9 | import java.net.InetSocketAddress; 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | 14 | /** 15 | * @Description: 服务器登录返回handler 16 | * Created by Ambitor on 2017/4/27. 17 | */ 18 | public class LoginResponseHandler extends ChannelHandlerAdapter { 19 | 20 | private static ConcurrentHashMap onlineNode = new ConcurrentHashMap<>(); 21 | private static final String value = "online"; 22 | private static Set whiteHost = new HashSet() { 23 | { 24 | //加载白名单 25 | add("127.0.0.1"); 26 | } 27 | }; 28 | 29 | @Override 30 | public void channelRead(ChannelHandlerContext ctx, Object msg) { 31 | NettyMessage request = (NettyMessage) msg; 32 | if (request != null && request.getHeader().getType() == MessageType.ACCEPT_REQ) { 33 | if (request.getBody() == null || !request.getBody().equals("Hello Netty Private Protocol.")) { 34 | ctx.close(); 35 | } else { 36 | System.out.println("登录成功,进行白名单、是否重复连接校验."); 37 | String address = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress(); 38 | if (!whiteHost.contains(address)) { 39 | System.out.println("该IP禁止访问服务器,断开链接."); 40 | ctx.close(); 41 | } 42 | if (onlineNode.containsKey(address)) { 43 | System.out.println("该IP重复登录服务器,断开链接."); 44 | ctx.close(); 45 | } 46 | onlineNode.put(address, value); 47 | NettyMessage response = new NettyMessage(); 48 | Header header = new Header(); 49 | header.setType(MessageType.ACCEPT_RES); 50 | header.setSessionID(1); 51 | response.setHeader(header); 52 | response.setBody("登录成功,返回信息."); 53 | ctx.writeAndFlush(response); 54 | //TODO 待验证 这个地方不需要再调用ctx.fireChannelRead往下走的原因是此消息已经被判断为连接请求,并且已经处理返回了,所以不需要往下走了 55 | } 56 | } else { 57 | ctx.fireChannelRead(msg); 58 | } 59 | } 60 | 61 | @Override 62 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 63 | onlineNode.remove(((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress()); 64 | System.out.println("关闭链接,释放资源,删除已登录信息防止客户端登录不了."); 65 | ctx.close(); 66 | ctx.fireExceptionCaught(cause); 67 | } 68 | } -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/config/BaseTransitionConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition.config; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.action.Actions; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Collection; 9 | 10 | /** 11 | * 转换器基类 12 | * Created by Ambitor on 2019/1/21 13 | * @param 状态 14 | * @param 事件 15 | */ 16 | public class BaseTransitionConfigurer implements TransitionConfigurer { 17 | private S source; 18 | private E event; 19 | //如果true则为下一个configurer,复制给next 20 | private boolean and = true; 21 | private TransitionConfigurer next; 22 | private final Collection> actions = new ArrayList<>(); 23 | 24 | public TransitionConfigurer getNext() { 25 | return next; 26 | } 27 | 28 | public void setNext(TransitionConfigurer next) { 29 | this.next = next; 30 | } 31 | 32 | public void setAnd(boolean and) { 33 | this.and = and; 34 | } 35 | 36 | public boolean isAnd() { 37 | return and; 38 | } 39 | 40 | @Override 41 | public StandardTransitionConfigurer standardTransition() { 42 | //如果有用过and拼接,则设置成下一个config 43 | StandardTransitionConfigurer transitionConfigurer = new DefaultStandardTransitionConfigurer<>(); 44 | if (isAnd()) { 45 | setNext(transitionConfigurer); 46 | setAnd(false); 47 | } 48 | return transitionConfigurer; 49 | } 50 | 51 | public ChoiceTransitionConfigurer choiceTransition() { 52 | //如果有用过and拼接,则设置成下一个config 53 | ChoiceTransitionConfigurer transitionConfigurer = new DefaultChoiceTransitionConfigurer<>(); 54 | if (isAnd()) { 55 | setNext(transitionConfigurer); 56 | setAnd(false); 57 | } 58 | return transitionConfigurer; 59 | } 60 | 61 | /** 62 | * 拼接方法 63 | * @return 需要拼接的类 64 | */ 65 | @Override 66 | public TransitionConfigurer and() { 67 | and = true; 68 | return this; 69 | } 70 | 71 | public S getSource() { 72 | return source; 73 | } 74 | 75 | public E getEvent() { 76 | return event; 77 | } 78 | 79 | public void setSource(S source) { 80 | this.source = source; 81 | } 82 | 83 | public void setEvent(E event) { 84 | this.event = event; 85 | } 86 | 87 | public Collection> getActions() { 88 | return actions; 89 | } 90 | 91 | protected void addAction(Action action, Action error) { 92 | actions.add(error != null ? Actions.errorCallingAction(action, error) : action); 93 | } 94 | 95 | protected void addAction(Action action) { 96 | addAction(action, null); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/resources/statemachine/sf-statemachine.yml: -------------------------------------------------------------------------------- 1 | #状态机名称 2 | name: sf 3 | 4 | #拦截器配置 5 | interceptor: 6 | - PersistStateMachineInterceptor.class 7 | 8 | #状态配置 9 | states: 10 | init: WAIT_CREATE_CARDII #等待开二类户 11 | suspend: 12 | - WAIT_DOCUMENT_CREDIT_CALLBACK #等待建档授信回调 13 | - WAIT_GRANT_CHECK #放款检查 14 | end: 15 | - CREATE_CARDII_FAILED #开二类户失败 16 | - DOCUMENT_CREDIT_FAILED #建档授信失败 17 | - GRANT_FAILED #放款失败 18 | - GRANT_SUCCESS #结束流程 19 | other: 20 | - WAIT_DOCUMENT_CREDIT #建档授信 21 | - WAIT_GRANT #放款 22 | - WAIT_GRANT_CHECK #等待放款校验 23 | - GRANT_TASK_SAVE #主流程完成 24 | 25 | #事件配置 26 | events: 27 | - CREATE_CARDII #开二类户 28 | - DOCUMENT_CREDIT #建档授信 29 | - DOCUMENT_CREDIT_CALLBACK #建档授信回调 30 | - GRANTED #放款 31 | - GRANT_CHECKED #放款校验 32 | - FINISHED #结束 33 | 34 | #转换器配置 35 | transitions: 36 | - type: standard #类型: 标准转换器 37 | source: WAIT_CREATE_CARDII #源状态:等待创建二类户 38 | target: WAIT_DOCUMENT_CREDIT #目标状态:等待建档授信 39 | event: CREATE_CARDII #事件: 创建二类户 40 | action: CreateCardIIAction.class #转换操作:创建二类户业务实现类 41 | errorAction: 42 | 43 | - type: choice #类型:选择转换器 44 | source: WAIT_DOCUMENT_CREDIT #源状态:等待建档授信 45 | event: DOCUMENT_CREDIT #事件:建档授信 46 | action: DocumentCreditAction.class #转换操作:建档授信业务实现类 47 | errorAction: 48 | first: {status: DOCUMENT_CREDIT_STATUS,equals: DOCUMENT_CREDIT_SUCCESS,target: WAIT_GRANT} 49 | then: {status: DOCUMENT_CREDIT_STATUS,equals: WAIT_DOCUMENT_CREDIT_CALLBACK,target: WAIT_DOCUMENT_CREDIT_CALLBACK} 50 | last: {target: DOCUMENT_CREDIT_FAILED} 51 | 52 | - type: choice 53 | source: WAIT_DOCUMENT_CREDIT_CALLBACK #源状态:等待建档授信回调 54 | event: DOCUMENT_CREDIT_CALLBACK #事件: 建档授信回调 55 | first: {status: DOCUMENT_CREDIT_STATUS,equals: DOCUMENT_CREDIT_SUCCESS,target: WAIT_GRANT} 56 | last: {target: DOCUMENT_CREDIT_FAILED} 57 | 58 | - type: choice 59 | source: WAIT_GRANT #源状态:等待放款 60 | event: GRANTED #事件:放款 61 | action: GrantAction.class #转换操作:放款业务实现类 62 | first: {status: GRANT_STATUS,equals: GRANT_SUCCESS,target: GRANT_TASK_SAVE} 63 | last: {target: WAIT_GRANT_CHECK} 64 | 65 | - type: choice 66 | source: WAIT_GRANT_CHECK #源状态:等待放款 67 | event: GRANT_CHECKED #事件:放款 68 | action: GrantAction.class #转换操作:放款业务实现类 69 | first: {status: GRANT_STATUS,equals: GRANT_SUCCESS,target: GRANT_TASK_SAVE} 70 | last: {target: GRANT_FAILED} 71 | 72 | - type: standard 73 | source: GRANT_TASK_SAVE #源状态:放款任务保存 74 | target: GRANT_SUCCESS #目标状态:放款成功 75 | event: FINISHED #事件: 放款结束 76 | action: FinishAction.class #转换操作:放款结束保存任务业务实现类 -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/configurer/adapter/AbstractStateMachineConfigurerAdapter.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.configurer.adapter; 2 | 3 | 4 | import ambitor.easy.statemachine.core.configurer.StateMachineConfigurer; 5 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorConfigurer; 6 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorList; 7 | import ambitor.easy.statemachine.core.state.config.DefaultStateConfigurer; 8 | import ambitor.easy.statemachine.core.state.config.StateConfigurer; 9 | import ambitor.easy.statemachine.core.transition.config.BaseTransitionConfigurer; 10 | import ambitor.easy.statemachine.core.transition.config.TransitionConfigurer; 11 | 12 | /** 13 | * 抽象的状态机配置类 14 | * Created by Ambitor on 2019-01-21 15 | * @author Ambitor 16 | * @param 状态 17 | * @param 事件 18 | */ 19 | public abstract class AbstractStateMachineConfigurerAdapter implements StateMachineConfigurer { 20 | 21 | protected TransitionConfigurer transitionConfigurer; 22 | 23 | protected StateConfigurer stateConfigurer; 24 | 25 | protected StateMachineInterceptorConfigurer interceptorConfigurer; 26 | 27 | private final Object lock = new Object(); 28 | 29 | public final void init() { 30 | //初始化状态配置 31 | getStateMachineStateConfigurer(); 32 | //初始化转换器配置 33 | getStateMachineTransitionConfigurer(); 34 | //拦截器配置 35 | getStateMachineInterceptorConfigurer(); 36 | } 37 | 38 | @Override 39 | public void configure(StateConfigurer states) { 40 | } 41 | 42 | @Override 43 | public void configure(TransitionConfigurer transitions) { 44 | } 45 | 46 | @Override 47 | public void configure(StateMachineInterceptorConfigurer interceptors) { 48 | } 49 | 50 | protected final void getStateMachineTransitionConfigurer() { 51 | if (transitionConfigurer == null) { 52 | synchronized (lock) { 53 | if (transitionConfigurer == null) { 54 | transitionConfigurer = new BaseTransitionConfigurer<>(); 55 | configure(transitionConfigurer); 56 | } 57 | } 58 | } 59 | } 60 | 61 | protected final void getStateMachineStateConfigurer() { 62 | if (stateConfigurer == null) { 63 | synchronized (lock) { 64 | if (stateConfigurer == null) { 65 | stateConfigurer = new DefaultStateConfigurer<>(); 66 | configure(stateConfigurer); 67 | } 68 | } 69 | } 70 | } 71 | 72 | protected final void getStateMachineInterceptorConfigurer() { 73 | if (interceptorConfigurer == null) { 74 | synchronized (lock) { 75 | if (interceptorConfigurer == null) { 76 | interceptorConfigurer = new StateMachineInterceptorList<>(); 77 | configure(interceptorConfigurer); 78 | } 79 | } 80 | } 81 | } 82 | 83 | public TransitionConfigurer getTransitionConfigurer() { 84 | return transitionConfigurer; 85 | } 86 | 87 | public StateConfigurer getStateConfigurer() { 88 | return stateConfigurer; 89 | } 90 | 91 | public StateMachineInterceptorConfigurer getInterceptorConfigurer() { 92 | return interceptorConfigurer; 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/service/StateMachineTaskServiceImpl.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.service; 2 | 3 | import ambitor.easy.statemachine.sf.enumerate.GrantState; 4 | import ambitor.easy.statemachine.workflow.model.StateMachineTask; 5 | import ambitor.easy.statemachine.workflow.model.TaskStatus; 6 | import ambitor.easy.statemachine.workflow.service.StateMachineTaskService; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Date; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by Ambitor on 2019/4/2 16 | */ 17 | @Component 18 | @Slf4j 19 | public class StateMachineTaskServiceImpl implements StateMachineTaskService { 20 | /** 21 | * 新增状态机 22 | * @param task 状态机 23 | * @return 影响行 24 | */ 25 | @Override 26 | public int insertSelective(StateMachineTask task) { 27 | log.info("新增状态机 transactionId ->{}", task.getTransactionId()); 28 | return 1; 29 | } 30 | 31 | @Override 32 | public StateMachineTask findByCode(String code) { 33 | return new StateMachineTask(); 34 | } 35 | 36 | /** 37 | * 根据主键修改状态机任务 38 | * @param task 状态机 39 | * @return 影响行 40 | */ 41 | @Override 42 | public int updateByPrimaryKeySelective(StateMachineTask task) { 43 | log.info("修改状态机 transactionId ->{}", task.getTransactionId()); 44 | return 1; 45 | } 46 | 47 | /** 48 | * 根据机器TransactionId查询 49 | * @param transactionId 唯一编号 50 | * @return 状态机 51 | */ 52 | @Override 53 | public StateMachineTask findByTransactionId(String transactionId) { 54 | return new StateMachineTask(); 55 | } 56 | 57 | /** 58 | * 获取需要执行的状态机任务 59 | * @return 状态机任务 60 | */ 61 | @Override 62 | public List getExecuteTask() { 63 | List tasks = new ArrayList<>(); 64 | StateMachineTask task = new StateMachineTask(); 65 | task.setMachineState(GrantState.WAIT_CREATE_CARDII.name()); 66 | task.setNextRunTime(new Date()); 67 | task.setScanStatus(TaskStatus.open.name()); 68 | task.setCurrentTrytimes(0); 69 | task.setMachineType("sf"); 70 | task.setRequestData("mock模拟状态机Task"); 71 | task.setRetryTimes(3); 72 | task.setTransactionId(String.valueOf(System.currentTimeMillis())); 73 | tasks.add(task); 74 | 75 | StateMachineTask task1 = new StateMachineTask(); 76 | task1.setMachineState("准备食材"); 77 | task1.setNextRunTime(new Date()); 78 | task1.setScanStatus(TaskStatus.open.name()); 79 | task1.setCurrentTrytimes(0); 80 | task1.setMachineType("eat"); 81 | task1.setRequestData("mock模拟状态机Task"); 82 | task1.setRetryTimes(3); 83 | task1.setTransactionId(String.valueOf(System.currentTimeMillis())); 84 | tasks.add(task1); 85 | 86 | StateMachineTask task2 = new StateMachineTask(); 87 | task2.setMachineState(GrantState.WAIT_CREATE_CARDII.name()); 88 | task2.setNextRunTime(new Date()); 89 | task2.setScanStatus(TaskStatus.open.name()); 90 | task2.setCurrentTrytimes(0); 91 | task2.setMachineType("SF"); 92 | task2.setRequestData("mock模拟状态机Task"); 93 | task2.setRetryTimes(3); 94 | task2.setTransactionId(String.valueOf(System.currentTimeMillis())); 95 | tasks.add(task2); 96 | return tasks; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /easy-statemachine-common/src/main/java/ambitor/easy/statemachine/messaging/ProtocolClient.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.messaging; 2 | 3 | import ambitor.easy.statemachine.messaging.coder.NettyMessageDecoder; 4 | import ambitor.easy.statemachine.messaging.coder.NettyMessageEncoder; 5 | import ambitor.easy.statemachine.messaging.handler.HeartbeatHandler; 6 | import ambitor.easy.statemachine.messaging.handler.LoginRequestHandler; 7 | import io.netty.bootstrap.Bootstrap; 8 | import io.netty.channel.ChannelFuture; 9 | import io.netty.channel.ChannelInitializer; 10 | import io.netty.channel.ChannelOption; 11 | import io.netty.channel.ChannelPipeline; 12 | import io.netty.channel.EventLoopGroup; 13 | import io.netty.channel.nio.NioEventLoopGroup; 14 | import io.netty.channel.socket.SocketChannel; 15 | import io.netty.channel.socket.nio.NioSocketChannel; 16 | import io.netty.handler.timeout.ReadTimeoutHandler; 17 | 18 | import java.util.Date; 19 | import java.util.concurrent.LinkedBlockingQueue; 20 | import java.util.concurrent.ThreadPoolExecutor; 21 | import java.util.concurrent.TimeUnit; 22 | 23 | /** 24 | * @Description: 客户端 25 | * Created by Ambitor on 2017/4/26. 26 | */ 27 | public class ProtocolClient { 28 | 29 | private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.MINUTES, new LinkedBlockingQueue()); 30 | 31 | public void connect(String host, int port) { 32 | 33 | EventLoopGroup acceptGroup = new NioEventLoopGroup(); 34 | 35 | try { 36 | Bootstrap bootstrap = new Bootstrap(); 37 | bootstrap.group(acceptGroup) 38 | .channel(NioSocketChannel.class) 39 | .option(ChannelOption.TCP_NODELAY, true) 40 | .handler(new ChannelInitializer() { 41 | @Override 42 | protected void initChannel(SocketChannel ch) throws Exception { 43 | ChannelPipeline pipeline = ch.pipeline(); 44 | pipeline.addLast(new NettyMessageDecoder(1024 * 1024 * 1, 4, 4)); 45 | pipeline.addLast(new NettyMessageEncoder()); 46 | pipeline.addLast(new ReadTimeoutHandler(9000)); 47 | pipeline.addLast(new LoginRequestHandler()); 48 | pipeline.addLast(new HeartbeatHandler()); 49 | } 50 | }); 51 | ChannelFuture future = bootstrap.connect(host, port).sync(); 52 | future.channel().closeFuture().sync(); 53 | System.out.println("客户端链接断开."); 54 | } catch (InterruptedException e) { 55 | acceptGroup.shutdownGracefully(); 56 | System.out.println("客户端断开." + new Date()); 57 | } catch (Exception e) { 58 | e.printStackTrace(); 59 | System.out.println("客户端断开." + new Date()); 60 | } finally { 61 | try { 62 | Thread.sleep(5000); 63 | } catch (InterruptedException e) { 64 | e.printStackTrace(); 65 | } 66 | acceptGroup.shutdownGracefully(); 67 | executor.submit(new Runnable() { 68 | @Override 69 | public void run() { 70 | System.out.println("客户端重连..." + new Date()); 71 | connect("127.0.0.1", 9080); 72 | } 73 | }); 74 | } 75 | } 76 | 77 | public static void main(String[] args) { 78 | new ProtocolClient().connect("127.0.0.1", 9080); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/interceptor/StateMachineInterceptorList.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.interceptor; 2 | 3 | 4 | import ambitor.easy.statemachine.core.StateMachine; 5 | import ambitor.easy.statemachine.core.context.Message; 6 | import ambitor.easy.statemachine.core.state.State; 7 | import ambitor.easy.statemachine.core.transition.Transition; 8 | 9 | import java.util.List; 10 | import java.util.concurrent.CopyOnWriteArrayList; 11 | 12 | /** 13 | * 状态机 14 | * @param 状态 15 | * @param 事件 16 | */ 17 | public class StateMachineInterceptorList implements StateMachineInterceptorConfigurer { 18 | 19 | private final List> interceptors = new CopyOnWriteArrayList>(); 20 | 21 | public boolean set(List> interceptors) { 22 | synchronized (this.interceptors) { 23 | this.interceptors.clear(); 24 | return this.interceptors.addAll(interceptors); 25 | } 26 | } 27 | 28 | public boolean add(StateMachineInterceptor interceptor) { 29 | return interceptors.add(interceptor); 30 | } 31 | 32 | public boolean remove(StateMachineInterceptor interceptor) { 33 | return interceptors.remove(interceptor); 34 | } 35 | 36 | public Message preEvent(Message message, StateMachine stateMachine) { 37 | for (StateMachineInterceptor interceptor : interceptors) { 38 | if ((message = interceptor.preEvent(message, stateMachine)) == null) { 39 | break; 40 | } 41 | } 42 | return message; 43 | } 44 | 45 | /** 46 | * Pre state change. 47 | * @param state the state 48 | * @param message the message 49 | * @param transition the transition 50 | * @param stateMachine the state machine 51 | */ 52 | public void preStateChange(State state, Message message, Transition transition, 53 | StateMachine stateMachine) { 54 | for (StateMachineInterceptor interceptor : interceptors) { 55 | interceptor.preStateChange(state, message, transition, stateMachine); 56 | } 57 | } 58 | 59 | /** 60 | * after state change. 61 | * @param state the state 62 | * @param message the message 63 | * @param transition the transition 64 | * @param stateMachine the state machine 65 | */ 66 | public void afterStateChange(State state, Message message, Transition transition, 67 | StateMachine stateMachine) { 68 | for (StateMachineInterceptor interceptor : interceptors) { 69 | interceptor.afterStateChange(state, message, transition, stateMachine); 70 | } 71 | } 72 | 73 | /** 74 | * State machine error. 75 | * @param stateMachine the state machine 76 | * @param exception the exception 77 | * @return the exception 78 | */ 79 | public void stateMachineError(StateMachine stateMachine, Message eventMsg, Exception exception) { 80 | for (StateMachineInterceptor interceptor : interceptors) { 81 | if ((exception = interceptor.stateMachineError(stateMachine,eventMsg, exception)) == null) { 82 | break; 83 | } 84 | } 85 | } 86 | 87 | @Override 88 | public boolean register(StateMachineInterceptor interceptor) { 89 | return add(interceptor); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/AbstractTransition.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.context.StateContext; 6 | import ambitor.easy.statemachine.core.guard.Guard; 7 | import ambitor.easy.statemachine.core.state.State; 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | import java.util.Collection; 11 | 12 | /** 13 | * 转换器抽象类 14 | * Created by Ambitor on 2019/1/21 15 | * @param 状态 16 | * @param 事件 17 | */ 18 | @Slf4j 19 | public abstract class AbstractTransition implements Transition { 20 | 21 | private final State source; 22 | private final State target; 23 | private final Guard guard; 24 | private final E event; 25 | protected final Collection> actions; 26 | 27 | 28 | /** 29 | * Instantiates a new abstract transition. 30 | * @param source the source 31 | * @param event the event 32 | * @param actions ths actions 33 | */ 34 | public AbstractTransition(State source, State target, E event, Guard guard, Collection> actions) { 35 | this.source = source; 36 | this.target = target; 37 | this.event = event; 38 | this.guard = guard; 39 | this.actions = actions; 40 | } 41 | 42 | @Override 43 | public State getSource() { 44 | return source; 45 | } 46 | 47 | @Override 48 | public State getTarget() { 49 | return target; 50 | } 51 | 52 | @Override 53 | public boolean transit(StateContext context) { 54 | executeTransitionActions(context); 55 | return context.getException() == null; 56 | } 57 | 58 | @Override 59 | public final void executeTransitionActions(StateContext context) { 60 | if (actions == null) { 61 | return; 62 | } 63 | for (Action action : actions) { 64 | try { 65 | action.execute(context); 66 | } catch (Exception e) { 67 | context.setException(e); 68 | log.error("Action执行结束,发生异常 Source-->{} ,Target-->{} ,Event->{}", 69 | context.getSource().getId(), context.getTarget().getId(), context.getEvent(), e); 70 | return; 71 | } 72 | } 73 | } 74 | 75 | @Override 76 | public Guard guard() { 77 | return guard; 78 | } 79 | 80 | @Override 81 | public E getEvent() { 82 | return event; 83 | } 84 | 85 | @Override 86 | public Collection> getActions() { 87 | return actions; 88 | } 89 | 90 | @Override 91 | public int hashCode() { 92 | return getSource().hashCode() + getTarget().hashCode() + getEvent().hashCode(); 93 | } 94 | 95 | @Override 96 | public boolean equals(Object obj) { 97 | if (!(obj instanceof AbstractTransition)) { 98 | return false; 99 | } 100 | AbstractTransition o = (AbstractTransition) obj; 101 | return this.getSource().equals(o.getSource()) && 102 | this.getTarget().equals(o.getTarget()) && 103 | this.getEvent().equals(o.getEvent()); 104 | } 105 | 106 | @Override 107 | public String toString() { 108 | return "AbstractTransition [source=" + getSource() + ", target=" + target + "]"; 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/parser/loader/ParserLoader.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.parser.loader; 2 | 3 | import ambitor.easy.statemachine.core.configurer.StateMachineConfigurer; 4 | import ambitor.easy.statemachine.core.exception.StateMachineException; 5 | import ambitor.easy.statemachine.parser.StateMachineParser; 6 | import ambitor.easy.statemachine.parser.yml.StateMachineYmlConfig; 7 | import ambitor.easy.statemachine.parser.yml.StateMachineYmlParser; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.BeansException; 10 | import org.springframework.beans.factory.BeanCreationException; 11 | import org.springframework.beans.factory.config.BeanFactoryPostProcessor; 12 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.core.io.ClassPathResource; 15 | import org.springframework.core.io.Resource; 16 | import org.springframework.core.io.support.PathMatchingResourcePatternResolver; 17 | import org.springframework.util.ResourceUtils; 18 | import org.yaml.snakeyaml.Yaml; 19 | 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileNotFoundException; 23 | import java.io.IOException; 24 | 25 | /** 26 | * 添加 27 | * Created by Ambitor on 2019/4/10 28 | * @author Ambitor 29 | */ 30 | @Slf4j 31 | //@Configuration 32 | public class ParserLoader implements BeanFactoryPostProcessor { 33 | 34 | /** 35 | * Modify the application context's internal bean factory after its standard 36 | * initialization. All bean definitions will have been loaded, but no beans 37 | * will have been instantiated yet. This allows for overriding or adding 38 | * properties even to eager-initializing beans. 39 | * @param beanFactory the bean factory used by the application context 40 | * @throws BeansException in case of errors 41 | */ 42 | @Override 43 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 44 | try { 45 | StateMachineParser stateMachineParser = beanFactory.getBean(StateMachineYmlParser.class); 46 | //实例化解析器 47 | Yaml yaml = new Yaml(); 48 | //配置文件地址 49 | PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); 50 | Resource[] resources = resolver.getResources("statemachine\\*statemachine*.yml"); 51 | log.info("find StateMachineYmlConfig " + resources.length + " count"); 52 | for (Resource resource : resources) { 53 | File file = resource.getFile(); 54 | FileInputStream fileInputStream = new FileInputStream(file); 55 | StateMachineYmlConfig config = yaml.loadAs(fileInputStream, StateMachineYmlConfig.class); 56 | log.info("load StateMachineYmlConfig {}", file.getName()); 57 | StateMachineConfigurer stateMachineConfigurer = stateMachineParser.parser(config); 58 | String name = config.getName(); 59 | if (name == null || name.length() <= 0) { 60 | throw new StateMachineException("please defined name with .yml config"); 61 | } 62 | if (beanFactory.containsBean(name)) { 63 | throw new StateMachineException("StateMachine bean name '" + name + "' has conflicts with existing"); 64 | } 65 | beanFactory.registerSingleton(name, stateMachineConfigurer); 66 | } 67 | } catch (FileNotFoundException e) { 68 | log.info("No StateMachineYmlConfig Found", e); 69 | } catch (IOException e) { 70 | throw new BeanCreationException("StateMachine.yml IOException", e); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/transition/config/DefaultChoiceTransitionConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.transition.config; 2 | 3 | import ambitor.easy.statemachine.core.action.Action; 4 | import ambitor.easy.statemachine.core.exception.StateMachineException; 5 | import ambitor.easy.statemachine.core.guard.Guard; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * 默认选择转换器配置类 Created by Ambitor on 2019/1/21 11 | * 12 | * @param 状态 13 | * @param 事件 14 | */ 15 | public class DefaultChoiceTransitionConfigurer extends BaseTransitionConfigurer 16 | implements ChoiceTransitionConfigurer { 17 | 18 | private ChoiceData first; 19 | private List> thens = new ArrayList<>(); 20 | private ChoiceData last; 21 | 22 | /** 23 | * 等同于if 24 | * 25 | * @param target 则状态变更成target 26 | * @param guard 如果返回true 27 | */ 28 | @Override 29 | public ChoiceTransitionConfigurer first(S target, Guard guard) { 30 | first = new ChoiceData<>(getSource(), target, guard); 31 | return this; 32 | } 33 | 34 | /** 35 | * 等同于 else if 36 | * 37 | * @param target 则状态变更成target 38 | * @param guard 如果返回true 39 | */ 40 | @Override 41 | public ChoiceTransitionConfigurer then(S target, Guard guard) { 42 | thens.add(new ChoiceData<>(getSource(), target, guard)); 43 | return this; 44 | } 45 | 46 | /** 47 | * 等同于 else 48 | * 49 | * @param target 如果first then 都不使用,变更状态target 50 | */ 51 | @Override 52 | public ChoiceTransitionConfigurer last(S target) { 53 | last = new ChoiceData<>(getSource(), target, (s) -> true); 54 | return this; 55 | } 56 | 57 | /** 58 | * action执行成功,状态扭转 source -> target 如抛出异常则状态不会扭转 59 | * 60 | * @param action 事件触发后执行的action 61 | */ 62 | @Override 63 | public ChoiceTransitionConfigurer action(Action action) { 64 | if (action != null) { 65 | addAction(action); 66 | } 67 | return this; 68 | } 69 | 70 | /** 71 | * action执行成功状态从 source -> target 注意,如果有error函数,当action执行失败时会调用error,且如果error执行成功无异常后 状态也会扭转 source -> target 72 | */ 73 | @Override 74 | public ChoiceTransitionConfigurer action(Action action, Action error) { 75 | if (action != null) { 76 | addAction(action, error); 77 | } 78 | return this; 79 | } 80 | 81 | /** 82 | * 源状态 83 | * 84 | * @param source 源状态S 85 | */ 86 | @Override 87 | public ChoiceTransitionConfigurer source(S source) { 88 | setSource(source); 89 | return this; 90 | } 91 | 92 | /** 93 | * 触发状态扭转 Source -> Target 的事件 94 | * 95 | * @param event 事件E 96 | */ 97 | @Override 98 | public ChoiceTransitionConfigurer event(E event) { 99 | setEvent(event); 100 | return this; 101 | } 102 | 103 | public List> config() { 104 | if (first == null) { 105 | throw new StateMachineException("ChoiceTransitionConfigurer must defined first() choice"); 106 | } 107 | if (last == null) { 108 | throw new StateMachineException("ChoiceTransitionConfigurer must defined last() choice"); 109 | } 110 | List> choiceData = new ArrayList<>(); 111 | choiceData.add(first); 112 | if (thens != null) { 113 | choiceData.addAll(thens); 114 | } 115 | choiceData.add(last); 116 | if (choiceData.isEmpty()) { 117 | throw new StateMachineException("ChoiceTransitionConfigurer defined error "); 118 | } 119 | return choiceData; 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/state/config/StateConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.state.config; 2 | 3 | import ambitor.easy.statemachine.core.action.Action; 4 | import ambitor.easy.statemachine.core.builder.ConfigurerBuilder; 5 | 6 | import java.util.Collection; 7 | import java.util.Set; 8 | 9 | /** 10 | * 状态配置 11 | * Created by Ambitor on 2019/1/21 12 | * @param 状态 13 | * @param 事件 14 | */ 15 | public interface StateConfigurer extends ConfigurerBuilder> { 16 | 17 | /** 18 | * 初始化状态 19 | * @param initial 初始化状态 20 | * @return this 21 | */ 22 | StateConfigurer initial(S initial); 23 | 24 | /** 25 | * 初始化状态,以及对应的action 26 | * @param initial 初始化状态 27 | * @param action 对应的action 28 | * @return this 29 | */ 30 | StateConfigurer initial(S initial, Action action); 31 | 32 | /** 33 | * 初始化状态,以及对应的action 34 | * @param initial 初始化状态 35 | * @param initialAction 对应的action 36 | * @param initialError 出错的action 37 | * @return this 38 | */ 39 | StateConfigurer initial(S initial, Action initialAction, Action initialError); 40 | 41 | /** 42 | * 定义status 43 | * @param state 状态 44 | * @return this 45 | */ 46 | StateConfigurer state(S state); 47 | 48 | /** 49 | * 定义状态,进入以及退出action 50 | * @param state the state 51 | * @param entryActions the state entry actions 52 | * @param exitActions the state exit actions 53 | * @return this 54 | */ 55 | StateConfigurer state(S state, Collection> entryActions, 56 | Collection> exitActions); 57 | 58 | /** 59 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 60 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 61 | * @param state the state 62 | */ 63 | StateConfigurer suspend(S state); 64 | 65 | /** 66 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 67 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 68 | * @param state the state 69 | * @param entry 进入该状态触发的action 70 | */ 71 | StateConfigurer suspendEntry(S state, Action entry); 72 | 73 | /** 74 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 75 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 76 | * @param state the state 77 | * @param entry 进入该状态触发的action 78 | * @param error entry 异常就调用 79 | */ 80 | StateConfigurer suspendEntry(S state, Action entry, Action error); 81 | 82 | /** 83 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 84 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 85 | * @param state the state 86 | * @param exit 退出该状态触发的action 87 | */ 88 | StateConfigurer suspendExit(S state, Action exit); 89 | 90 | /** 91 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 92 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 93 | * @param state the state 94 | * @param exit 退出该状态触发的action 95 | * @param error exit 异常就调用 96 | */ 97 | StateConfigurer suspendExit(S state, Action exit, Action error); 98 | 99 | StateConfigurer suspend(S state, Collection> entryActions, 100 | Collection> exitActions); 101 | 102 | /** 103 | * 定义状态,并且绑定进入前action 104 | * @param state the state 105 | * @param action the state entry action 106 | * @return this 107 | */ 108 | StateConfigurer stateEntry(S state, Action action); 109 | 110 | /** 111 | * 定义状态,并且绑定进入前action 112 | * @param state the state 113 | * @param action the state entry action 114 | * @param error callback when Exception 115 | * @return this 116 | */ 117 | StateConfigurer stateEntry(S state, Action action, Action error); 118 | 119 | /** 120 | * 定义状态,并且绑定退出时action 121 | * @param state the state 122 | * @param action the state exit action 123 | * @return this 124 | */ 125 | StateConfigurer stateExit(S state, Action action); 126 | 127 | /** 128 | * 定义状态,并且绑定退出时action 129 | * @param state the state 130 | * @param action the state entry action 131 | * @param error callback when Exception 132 | * @return this 133 | */ 134 | StateConfigurer stateExit(S state, Action action, Action error); 135 | 136 | /** 137 | * 定义所有状态 138 | * @param states the states 139 | * @return this 140 | */ 141 | StateConfigurer states(Set states); 142 | 143 | /** 144 | * 结束状态,可以在任何状态时候被调用来标记结束 145 | * @param end the end state 146 | * @return this 147 | */ 148 | StateConfigurer end(S end); 149 | 150 | /** 151 | * 创建状态配置 152 | * @return this 153 | */ 154 | StateConfigurer newStatesConfigurer(); 155 | } 156 | -------------------------------------------------------------------------------- /easy-statemachine-wokflow/src/main/java/ambitor/easy/statemachine/workflow/interceptor/PersistStateMachineInterceptor.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.workflow.interceptor; 2 | 3 | import static ambitor.easy.statemachine.workflow.model.StateMachineConstant.TASK_HEADER; 4 | 5 | import ambitor.easy.statemachine.core.StateMachine; 6 | import ambitor.easy.statemachine.core.context.Message; 7 | import ambitor.easy.statemachine.core.context.MessageHeaders; 8 | import ambitor.easy.statemachine.core.interceptor.AbstractStateMachineInterceptor; 9 | import ambitor.easy.statemachine.core.state.State; 10 | import ambitor.easy.statemachine.core.transition.Transition; 11 | import ambitor.easy.statemachine.workflow.model.StateMachineConstant; 12 | import ambitor.easy.statemachine.workflow.model.StateMachineLog; 13 | import ambitor.easy.statemachine.workflow.model.StateMachineTask; 14 | import ambitor.easy.statemachine.workflow.service.StateMachineLogService; 15 | import ambitor.easy.statemachine.workflow.service.StateMachineTaskService; 16 | import com.alibaba.fastjson.JSON; 17 | import com.alibaba.fastjson.serializer.PropertyFilter; 18 | import lombok.extern.slf4j.Slf4j; 19 | import org.apache.commons.lang3.exception.ExceptionUtils; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.stereotype.Component; 22 | 23 | /** 24 | * 持久化拦截器,状态发生改变后把当前状态信息持久化 25 | * 26 | * @author Ambitor 27 | */ 28 | @Slf4j 29 | @Component 30 | public class PersistStateMachineInterceptor extends AbstractStateMachineInterceptor { 31 | 32 | //加上action唯一执行key,方便日志查看 33 | public static final String TRANSITION_UNIQUE_ID = "transition_unique_id"; 34 | public static final int MID_TEXT_LENGTH = 1677721; 35 | @Autowired 36 | private StateMachineTaskService stateMachineTaskService; 37 | @Autowired 38 | private StateMachineLogService stateMachineLogService; 39 | 40 | @Override 41 | public void afterStateChange(State target, Message message, Transition transition, 42 | StateMachine stateMachine) { 43 | log.info("状态改变持久化到数据库"); 44 | MessageHeaders headers = message.getHeaders(); 45 | StateMachineTask task = (StateMachineTask)headers.getHeaders().get(StateMachineConstant.TASK_HEADER); 46 | String tid = task.getId() + "-" + System.currentTimeMillis(); 47 | log.info("状态发生改变 tid->{}", tid); 48 | headers.addHeader(TRANSITION_UNIQUE_ID, tid); 49 | //上下文 50 | String context = getContext(headers); 51 | //保存转换日志 52 | String response = getResponse(task.getResponseData()); 53 | saveLog(task.getMachineCode(), message.getPayload().toString(), transition.getSource().getId().toString(), 54 | target.getId().toString(), Transition.SUCCESS, response, context); 55 | //修改数据库 56 | StateMachineTask update = new StateMachineTask(); 57 | update.setId(task.getId()); 58 | update.setTransactionId(task.getTransactionId()); 59 | update.setMachineState(target.getId().toString()); 60 | update.setRequestData(task.getRequestData()); 61 | update.setResponseData(response); 62 | update.setMachineContext(context); 63 | stateMachineTaskService.updateByPrimaryKeySelective(update); 64 | } 65 | 66 | @Override 67 | public Exception stateMachineError(StateMachine stateMachine, Message eventMsg, Exception e) { 68 | Transition transition = stateMachine.transition(); 69 | //保存转换日志 70 | StateMachineTask task = 71 | (StateMachineTask)stateMachine.getEvent().getHeaders().getHeaders().get(StateMachineConstant.TASK_HEADER); 72 | String errorMsg = ExceptionUtils.getStackTrace(e); 73 | String context = getContext(eventMsg.getHeaders()); 74 | saveLog(task.getMachineCode(), transition.getEvent().toString(), stateMachine.getState().getId().toString(), 75 | transition.getTarget().getId().toString(), Transition.FAILED, errorMsg, context); 76 | //修改数据库 77 | StateMachineTask update = new StateMachineTask(); 78 | update.setTransactionId(task.getTransactionId()); 79 | update.setId(task.getId()); 80 | update.setMachineContext(context); 81 | update.setResponseData(errorMsg); 82 | stateMachineTaskService.updateByPrimaryKeySelective(update); 83 | return e; 84 | } 85 | 86 | private void saveLog(String code, String event, String source, String target, String result, String response, 87 | String context) { 88 | StateMachineTask original = stateMachineTaskService.findByCode(code); 89 | //保存log 90 | StateMachineLog record = new StateMachineLog(); 91 | record.setMachineCode(code); 92 | record.setEvent(event); 93 | record.setSource(source); 94 | record.setTarget(target); 95 | record.setTransitionResult(result); 96 | record.setRequest(original.getRequestData()); 97 | record.setResponse(response); 98 | record.setMachineContext(context); 99 | stateMachineLogService.insertSelective(record); 100 | } 101 | 102 | private String getContext(MessageHeaders message) { 103 | String context = JSON.toJSONString(message, (PropertyFilter)(o, key, value) -> !TASK_HEADER.equals(key)); 104 | context = context.length() > MID_TEXT_LENGTH ? context.substring(0, MID_TEXT_LENGTH) : context; 105 | return context; 106 | } 107 | 108 | private String getResponse(String response) { 109 | return response.length() > MID_TEXT_LENGTH ? response.substring(0, MID_TEXT_LENGTH) : response; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /easy-statemachine-demo/src/main/java/ambitor/easy/statemachine/sf/statemachine/GrantStateMachineConfig.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.sf.statemachine; 2 | 3 | 4 | import ambitor.easy.statemachine.core.annotation.EnableWithStateMachine; 5 | import ambitor.easy.statemachine.core.configurer.adapter.AbstractStateMachineConfigurerAdapter; 6 | import ambitor.easy.statemachine.core.guard.DefaultGuard; 7 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorConfigurer; 8 | import ambitor.easy.statemachine.core.state.config.StateConfigurer; 9 | import ambitor.easy.statemachine.core.transition.config.TransitionConfigurer; 10 | import ambitor.easy.statemachine.sf.action.CreateCardIIAction; 11 | import ambitor.easy.statemachine.sf.action.DocumentCreditAction; 12 | import ambitor.easy.statemachine.sf.action.FinishAction; 13 | import ambitor.easy.statemachine.sf.action.GrantAction; 14 | import ambitor.easy.statemachine.sf.enumerate.GrantEvent; 15 | import ambitor.easy.statemachine.sf.enumerate.GrantState; 16 | import ambitor.easy.statemachine.workflow.interceptor.PersistStateMachineInterceptor; 17 | import lombok.extern.slf4j.Slf4j; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | 20 | import java.util.EnumSet; 21 | 22 | import static ambitor.easy.statemachine.sf.enumerate.GrantConstant.*; 23 | 24 | @Slf4j 25 | @EnableWithStateMachine 26 | public class GrantStateMachineConfig extends AbstractStateMachineConfigurerAdapter { 27 | 28 | @Override 29 | public void configure(StateConfigurer states) { 30 | states 31 | .newStatesConfigurer() 32 | // 定义初始状态 33 | .initial(GrantState.WAIT_CREATE_CARDII) 34 | // 定义挂起状态,遇到此状态后状态机自动挂起,直到再次手动触发事件(回调中),状态机继续往下执行 35 | .suspend(GrantState.WAIT_DOCUMENT_CREDIT_CALLBACK) 36 | // 放款校验状态 37 | .suspend(GrantState.WAIT_GRANT_CHECK) 38 | // 定义所有状态集合 39 | .states(EnumSet.allOf(GrantState.class)) 40 | //定义结束状态 41 | .end(GrantState.CREATE_CARDII_FAILED) 42 | .end(GrantState.DOCUMENT_CREDIT_FAILED) 43 | .end(GrantState.GRANT_FAILED) 44 | .end(GrantState.GRANT_SUCCESS); 45 | } 46 | 47 | /** 48 | * 状态扭转器配置,配置工作流通过 event(触发事件)把状态从 49 | * source status(源状态) 转到 target status (目标状态) 50 | * 可根据上一步建档授信结果转换成不同的 target status (目标状态) 51 | * 具体配置情况如下 52 | */ 53 | @Override 54 | public void configure(TransitionConfigurer transitions) { 55 | transitions 56 | /** 1、等待创建二类户 **/ 57 | //标准转换器,不具备结果判断,事件触发后只能从X状态转为Y状态 58 | .standardTransition() 59 | .source(GrantState.WAIT_CREATE_CARDII) 60 | .target(GrantState.WAIT_DOCUMENT_CREDIT) 61 | .event(GrantEvent.CREATE_CARDII) 62 | .action(createCardIIAction, createCardIIAction.errorAction(s -> log.info("创建二类户异常"))) 63 | .and() 64 | /** 2、等待建档授信步骤 **/ 65 | //具备选择结果的转换器,可根据当前事件执行结果扭转到不同状态 66 | .choiceTransition() 67 | //原状态为等待建档授信 68 | .source(GrantState.WAIT_DOCUMENT_CREDIT) 69 | //first相当于if,如果建档授信状态返回DOCUMENT_CREDIT_SUCCESS则转换成WAIT_GRANT等待放款 70 | .first(GrantState.WAIT_GRANT, DefaultGuard.condition(DOCUMENT_CREDIT_STATUS, DOCUMENT_CREDIT_SUCCESS)) 71 | //then相当于elseif,如果建档授信状态返回WAIT_DOCUMENT_CREDIT_CALLBACK则转换成等待建档授信回调 72 | .then(GrantState.WAIT_DOCUMENT_CREDIT_CALLBACK, DefaultGuard.condition(DOCUMENT_CREDIT_STATUS, WAIT_DOCUMENT_CREDIT_CALLBACK)) 73 | //last相当于else,如果都不是则返回建档授信失败 74 | .last(GrantState.DOCUMENT_CREDIT_FAILED) 75 | //触发事件 76 | .event(GrantEvent.DOCUMENT_CREDIT) 77 | //事件执行的action 78 | .action(documentCreditAction, documentCreditAction.errorAction()) 79 | .and() 80 | /** 3、等待建档授信回调步骤 **/ 81 | .choiceTransition() 82 | .source(GrantState.WAIT_DOCUMENT_CREDIT_CALLBACK) 83 | .first(GrantState.WAIT_GRANT, DefaultGuard.condition(DOCUMENT_CREDIT_STATUS, DOCUMENT_CREDIT_SUCCESS)) 84 | .last(GrantState.DOCUMENT_CREDIT_FAILED) 85 | .event(GrantEvent.DOCUMENT_CREDIT_CALLBACK) 86 | .and() 87 | /** 4、等待放款流程 **/ 88 | .choiceTransition() 89 | .source(GrantState.WAIT_GRANT) 90 | .first(GrantState.GRANT_TASK_SAVE, DefaultGuard.condition(GRANT_STATUS, GRANT_SUCCESS)) 91 | .last(GrantState.WAIT_GRANT_CHECK) 92 | .event(GrantEvent.GRANTED) 93 | .action(grantAction) 94 | .and() 95 | /** 5、放款检查流程,如果上一步操作超时 **/ 96 | .choiceTransition() 97 | .source(GrantState.WAIT_GRANT_CHECK) 98 | .first(GrantState.GRANT_TASK_SAVE, DefaultGuard.condition(GRANT_STATUS, GRANT_SUCCESS)) 99 | .last(GrantState.GRANT_FAILED) 100 | .event(GrantEvent.GRANT_CHECKED) 101 | .and() 102 | /** 6、最后完成的流程 **/ 103 | .standardTransition() 104 | .source(GrantState.GRANT_TASK_SAVE).target(GrantState.GRANT_SUCCESS) 105 | .event(GrantEvent.FINISHED) 106 | .action(finishAction); 107 | } 108 | 109 | /** 110 | * 注册拦截器 111 | */ 112 | @Override 113 | public void configure(StateMachineInterceptorConfigurer interceptors) { 114 | //状态改变持久化到数据库拦截器 115 | interceptors.register(persistStateMachineInterceptor); 116 | } 117 | 118 | /** 119 | * 状态机名称 120 | */ 121 | @Override 122 | public String getName() { 123 | return "SF"; 124 | } 125 | 126 | @Autowired 127 | private PersistStateMachineInterceptor persistStateMachineInterceptor; 128 | @Autowired 129 | private CreateCardIIAction createCardIIAction; 130 | @Autowired 131 | private DocumentCreditAction documentCreditAction; 132 | @Autowired 133 | private GrantAction grantAction; 134 | @Autowired 135 | private FinishAction finishAction; 136 | 137 | } 138 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/factory/StateMachineFactory.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.factory; 2 | 3 | import ambitor.easy.statemachine.core.DefaultStateMachine; 4 | import ambitor.easy.statemachine.core.StateMachine; 5 | import ambitor.easy.statemachine.core.action.Action; 6 | import ambitor.easy.statemachine.core.configurer.StateMachineConfigurer; 7 | import ambitor.easy.statemachine.core.configurer.adapter.AbstractStateMachineConfigurerAdapter; 8 | import ambitor.easy.statemachine.core.exception.StateMachineException; 9 | import ambitor.easy.statemachine.core.guard.Guard; 10 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorList; 11 | import ambitor.easy.statemachine.core.state.State; 12 | import ambitor.easy.statemachine.core.state.config.DefaultStateConfigurer; 13 | import ambitor.easy.statemachine.core.transition.StandardTransition; 14 | import ambitor.easy.statemachine.core.transition.Transition; 15 | import ambitor.easy.statemachine.core.transition.config.BaseTransitionConfigurer; 16 | import ambitor.easy.statemachine.core.transition.config.ChoiceData; 17 | import ambitor.easy.statemachine.core.transition.config.DefaultChoiceTransitionConfigurer; 18 | import ambitor.easy.statemachine.core.transition.config.DefaultStandardTransitionConfigurer; 19 | import ambitor.easy.statemachine.core.transition.config.TransitionConfigurer; 20 | import lombok.extern.slf4j.Slf4j; 21 | import org.springframework.util.CollectionUtils; 22 | 23 | import java.text.MessageFormat; 24 | import java.util.ArrayList; 25 | import java.util.Collection; 26 | import java.util.HashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | 30 | /** 31 | * 状态机工厂 32 | * Created by Ambitor on 2019/1/21 33 | * @author Ambitor 34 | */ 35 | @Slf4j 36 | public class StateMachineFactory { 37 | 38 | private StateMachineFactory() { 39 | } 40 | 41 | private static Map transitions = new HashMap<>(); 42 | 43 | /** 44 | * 创建状态机 45 | * @param 46 | * @param 47 | * @return 48 | */ 49 | public static StateMachine build(StateMachineConfigurer stateMachineConfigurer) { 50 | if (!(stateMachineConfigurer instanceof AbstractStateMachineConfigurerAdapter)) { 51 | throw new StateMachineException("stateMachineConfigurer must be AbstractStateMachineConfigurerAdapter instance "); 52 | } 53 | AbstractStateMachineConfigurerAdapter stateMachineConfigurerAdapter = (AbstractStateMachineConfigurerAdapter) stateMachineConfigurer; 54 | stateMachineConfigurerAdapter.init(); 55 | String stateMachineName = stateMachineConfigurerAdapter.getName(); 56 | //State 57 | DefaultStateConfigurer stateConfigurer = (DefaultStateConfigurer) stateMachineConfigurerAdapter.getStateConfigurer(); 58 | Map> stateMaps = stateConfigurer.getStateMaps(); 59 | State initialState = stateMaps.get(stateConfigurer.getInitialState()); 60 | 61 | //Transition 62 | Map>> transitions = new HashMap<>(); 63 | //第一个TransitionConfigurer是系统默认的 64 | TransitionConfigurer configurer = ((BaseTransitionConfigurer) stateMachineConfigurerAdapter.getTransitionConfigurer()).getNext(); 65 | 66 | while (configurer != null) { 67 | if (configurer instanceof DefaultStandardTransitionConfigurer) { 68 | //StandardTransitionConfigurer 69 | DefaultStandardTransitionConfigurer standard = (DefaultStandardTransitionConfigurer) configurer; 70 | State target = stateMaps.get(standard.getTarget()); 71 | State source = stateMaps.get(standard.getSource()); 72 | Collection> collection = transitions.computeIfAbsent(source.getId(), k -> new ArrayList<>()); 73 | Transition t = getTransition(stateMachineName, source, target, standard.getEvent(), (s) -> true, standard.getActions()); 74 | collection.add(t); 75 | configurer = standard.getNext(); 76 | } else if (configurer instanceof DefaultChoiceTransitionConfigurer) { 77 | //ChoiceTransitionConfigurer 78 | DefaultChoiceTransitionConfigurer choice = (DefaultChoiceTransitionConfigurer) configurer; 79 | State source = stateMaps.get(choice.getSource()); 80 | Collection> collection = transitions.computeIfAbsent(source.getId(), k -> new ArrayList<>()); 81 | List> choiceData = choice.config(); 82 | for (ChoiceData data : choiceData) { 83 | State target = stateMaps.get(data.getTarget()); 84 | if (target == null) { 85 | throw new StateMachineException("please defined " + data.getTarget() + " state in " + stateMachineName + " machine"); 86 | } 87 | Transition t = getTransition(stateMachineName, source, target, choice.getEvent(), data.getGuard(), choice.getActions()); 88 | collection.add(t); 89 | } 90 | configurer = choice.getNext(); 91 | } else { 92 | String msg = MessageFormat.format("TransitionConfigurer -> {0} not support", configurer.getClass().getName()); 93 | throw new StateMachineException(msg); 94 | } 95 | } 96 | //valid Transition and Status 97 | checkStatus2Transition(stateMaps, transitions); 98 | 99 | //interceptors of the StateMachine 100 | StateMachineInterceptorList interceptors = (StateMachineInterceptorList) stateMachineConfigurerAdapter.getInterceptorConfigurer(); 101 | 102 | //StateMachine 103 | return new DefaultStateMachine<>(stateMaps, transitions, initialState, initialState, null, interceptors); 104 | } 105 | 106 | /** 107 | * 校验状态和转换器 108 | */ 109 | private static void checkStatus2Transition(Map> stateMaps, Map>> transitions) { 110 | if (stateMaps == null || stateMaps.isEmpty()) { 111 | throw new StateMachineException("Please defined State By StateMachine StateConfigurer"); 112 | } 113 | if (transitions == null || transitions.isEmpty()) { 114 | throw new StateMachineException("Please defined Transition By StateMachine TransitionConfigurer"); 115 | } 116 | for (S s : stateMaps.keySet()) { 117 | State state = stateMaps.get(s); 118 | if (CollectionUtils.isEmpty(transitions.get(s)) && !state.isEnd()) { 119 | //非结束状态必须定义转换器 120 | String msg = MessageFormat.format("The non ending state must define the Transition by TransitionConfigurer, State -> {0}", s); 121 | throw new StateMachineException(msg); 122 | } 123 | } 124 | } 125 | 126 | 127 | /** 128 | * 确保Transition不能重复添加 129 | */ 130 | private static Transition getTransition(String stateMachineName, State source, State target, E event, Guard guard, Collection> actions) { 131 | String remark = stateMachineName + " machine " + source.getId() + " source"; 132 | if (source == null) { 133 | throw new StateMachineException(remark + " transition source state can not be null."); 134 | } 135 | if (target == null) { 136 | throw new StateMachineException(remark + " transition target state can not be null"); 137 | } 138 | if (event == null) { 139 | throw new StateMachineException(remark + " transition event can not be null"); 140 | } 141 | String key = stateMachineName + "_" + source.getId() + "_" + target.getId() + "_" + event; 142 | Transition transition = transitions.get(key); 143 | if (transition == null) { 144 | transition = new StandardTransition<>(source, target, event, guard, actions); 145 | transitions.put(key, transition); 146 | } 147 | return transition; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/state/config/DefaultStateConfigurer.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core.state.config; 2 | 3 | 4 | import ambitor.easy.statemachine.core.action.Action; 5 | import ambitor.easy.statemachine.core.action.Actions; 6 | import ambitor.easy.statemachine.core.exception.StateMachineException; 7 | import ambitor.easy.statemachine.core.state.DefaultState; 8 | import ambitor.easy.statemachine.core.state.State; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.TreeMap; 15 | 16 | /** 17 | * 状态机状态配置类 18 | * Created by Ambitor on 2019/1/21 19 | * @author Ambitor 20 | * @param 状态 21 | * @param 事件 22 | */ 23 | public class DefaultStateConfigurer implements StateConfigurer { 24 | 25 | S initialState; 26 | final Collection ends = new ArrayList<>(); 27 | final Map> stateMaps = new TreeMap<>(); 28 | 29 | /** 30 | * 创建状态配置 31 | * @return this 32 | */ 33 | @Override 34 | public StateConfigurer newStatesConfigurer() { 35 | return this; 36 | } 37 | 38 | /** 39 | * 初始化状态 40 | * @param initialState 初始化状态 41 | * @return this 42 | */ 43 | @Override 44 | public StateConfigurer initial(S initialState) { 45 | return initial(initialState, null, null); 46 | } 47 | 48 | /** 49 | * 初始化状态,以及对应的action 50 | * @param initial 初始化状态 51 | * @param action 对应的action 52 | * @return this 53 | */ 54 | @Override 55 | public StateConfigurer initial(S initial, Action action) { 56 | return initial(initial, action, null); 57 | } 58 | 59 | /** 60 | * 初始化状态,以及对应的action 61 | * @param initial 初始化状态 62 | * @param initialAction 对应的action 63 | * @return this 64 | */ 65 | @Override 66 | public StateConfigurer initial(S initial, Action initialAction, Action initialError) { 67 | Collection> exitActions = null; 68 | if (initialAction != null) { 69 | exitActions = new ArrayList<>(1); 70 | exitActions.add(initialError != null ? Actions.errorCallingAction(initialAction, initialError) : initialAction); 71 | } 72 | addState(initial, true, false, false, null, exitActions); 73 | this.initialState = initial; 74 | return this; 75 | } 76 | 77 | /** 78 | * 定义status 79 | * @param state 状态 80 | * @return this 81 | */ 82 | @Override 83 | public StateConfigurer state(S state) { 84 | addState(state, false, false, false, null, null); 85 | return this; 86 | } 87 | 88 | /** 89 | * 定义状态,进入以及退出action 90 | * @param state the state 91 | * @param entryActions the state entry actions 92 | * @param exitActions the state exit actions 93 | * @return this 94 | */ 95 | @Override 96 | public StateConfigurer state(S state, Collection> entryActions, Collection> exitActions) { 97 | addState(state, false, false, false, entryActions, exitActions); 98 | return this; 99 | } 100 | 101 | /** 102 | * 定义状态,并且绑定进入前action 103 | * @param state the state 104 | * @param entry the state entry action 105 | * @return this 106 | */ 107 | @Override 108 | public StateConfigurer stateEntry(S state, Action entry) { 109 | stateEntry(state, entry, null); 110 | return this; 111 | } 112 | 113 | /** 114 | * 定义状态,并且绑定进入前action 115 | * @param state the state 116 | * @param entry the state entry action 117 | * @param error callback when Exception 118 | * @return this 119 | */ 120 | @Override 121 | public StateConfigurer stateEntry(S state, Action entry, Action error) { 122 | Collection> entryActions = null; 123 | if (entry != null) { 124 | entryActions = new ArrayList<>(1); 125 | entryActions.add(error != null ? Actions.errorCallingAction(entry, error) : entry); 126 | } 127 | addState(state, false, false, false, entryActions, null); 128 | return this; 129 | } 130 | 131 | /** 132 | * 定义状态,并且绑定退出时action 133 | * @param state the state 134 | * @param exit the state exit action 135 | * @return this 136 | */ 137 | @Override 138 | public StateConfigurer stateExit(S state, Action exit) { 139 | stateExit(state, exit, null); 140 | return this; 141 | } 142 | 143 | /** 144 | * 定义状态,并且绑定退出时action 145 | * @param state the state 146 | * @param exit the state exit action 147 | * @param error callback when Exception 148 | * @return this 149 | */ 150 | @Override 151 | public StateConfigurer stateExit(S state, Action exit, Action error) { 152 | Collection> exitActions = null; 153 | if (exit != null) { 154 | exitActions = new ArrayList<>(1); 155 | exitActions.add(error != null ? Actions.errorCallingAction(exit, error) : exit); 156 | } 157 | return state(state, null, exitActions); 158 | } 159 | 160 | /** 161 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 162 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 163 | * @param state the state 164 | */ 165 | @Override 166 | public StateConfigurer suspend(S state) { 167 | addState(state, false, true, false, null, null); 168 | return this; 169 | } 170 | 171 | /** 172 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 173 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 174 | * @param state the state 175 | * @param entry 进入该状态触发的action 176 | */ 177 | @Override 178 | public StateConfigurer suspendEntry(S state, Action entry) { 179 | suspendEntry(state, entry, null); 180 | return this; 181 | } 182 | 183 | /** 184 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 185 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 186 | * @param state the state 187 | * @param entry 进入该状态触发的action 188 | * @param error entry 异常就调用 189 | */ 190 | @Override 191 | public StateConfigurer suspendEntry(S state, Action entry, Action error) { 192 | Collection> entryActions = null; 193 | if (entry != null) { 194 | entryActions = new ArrayList<>(1); 195 | entryActions.add(error != null ? Actions.errorCallingAction(entry, error) : entry); 196 | } 197 | addState(state, false, true, false, entryActions, null); 198 | return this; 199 | } 200 | 201 | /** 202 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 203 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 204 | * @param state the state 205 | * @param exit 退出该状态触发的action 206 | */ 207 | @Override 208 | public StateConfigurer suspendExit(S state, Action exit) { 209 | suspendExit(state, exit, null); 210 | return this; 211 | } 212 | 213 | /** 214 | * 挂起状态,此状态执行完Transition后会挂起状态机(请注意状态机只有恢复后才会继续执行,使用此状态请记得恢复状态机) 215 | * 使用场景:触发事件扭转状态后,需要回调才能继续往下走流程 216 | * @param state the state 217 | * @param exit 退出该状态触发的action 218 | * @param error exit 异常就调用 219 | */ 220 | @Override 221 | public StateConfigurer suspendExit(S state, Action exit, Action error) { 222 | Collection> exitActions = null; 223 | if (exit != null) { 224 | exitActions = new ArrayList<>(1); 225 | exitActions.add(error != null ? Actions.errorCallingAction(exit, error) : exit); 226 | } 227 | return state(state, null, exitActions); 228 | } 229 | 230 | @Override 231 | public StateConfigurer suspend(S state, Collection> entryActions, Collection> exitActions) { 232 | addState(state, false, true, false, entryActions, exitActions); 233 | return null; 234 | } 235 | 236 | /** 237 | * 定义所有状态 238 | * @param states the states 239 | * @return this 240 | */ 241 | @Override 242 | public StateConfigurer states(Set states) { 243 | for (S state : states) { 244 | State exist = stateMaps.get(state); 245 | if (exist == null) { 246 | state(state); 247 | } 248 | } 249 | return this; 250 | } 251 | 252 | /** 253 | * 拼接方法 254 | * @return 需要拼接的类 255 | */ 256 | @Override 257 | public StateConfigurer and() { 258 | throw new StateMachineException("暂时不支持"); 259 | } 260 | 261 | /** 262 | * 结束状态,可以在任何状态时候被调用来标记结束 263 | * @param end the end state 264 | * @return this 265 | */ 266 | @Override 267 | public StateConfigurer end(S end) { 268 | ends.add(end); 269 | addState(end, false, false, true, null, null); 270 | return this; 271 | } 272 | 273 | private void addState(S state, boolean initial, boolean suspend, boolean end, 274 | Collection> entryActions, 275 | Collection> exitActions) { 276 | State stateData = new DefaultState<>(state, initial, suspend, end, entryActions, exitActions); 277 | stateMaps.put(state, stateData); 278 | } 279 | 280 | public Collection getEnds() { 281 | return ends; 282 | } 283 | 284 | public Map> getStateMaps() { 285 | return stateMaps; 286 | } 287 | 288 | public S getInitialState() { 289 | return initialState; 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/parser/yml/StateMachineYmlParser.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.parser.yml; 2 | 3 | import ambitor.easy.statemachine.core.action.Action; 4 | import ambitor.easy.statemachine.core.configurer.StateMachineConfigurer; 5 | import ambitor.easy.statemachine.core.configurer.adapter.AbstractStateMachineConfigurerAdapter; 6 | import ambitor.easy.statemachine.core.enumerate.TransitionType; 7 | import ambitor.easy.statemachine.core.exception.StateMachineException; 8 | import ambitor.easy.statemachine.core.guard.DefaultGuard; 9 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptor; 10 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorConfigurer; 11 | import ambitor.easy.statemachine.core.state.config.StateConfigurer; 12 | import ambitor.easy.statemachine.core.transition.config.ChoiceTransitionConfigurer; 13 | import ambitor.easy.statemachine.core.transition.config.StandardTransitionConfigurer; 14 | import ambitor.easy.statemachine.core.transition.config.TransitionConfigurer; 15 | import ambitor.easy.statemachine.parser.StateMachineParser; 16 | import lombok.extern.slf4j.Slf4j; 17 | import org.springframework.beans.BeansException; 18 | import org.springframework.context.ApplicationContext; 19 | import org.springframework.context.ApplicationContextAware; 20 | import org.springframework.stereotype.Component; 21 | import org.springframework.util.ReflectionUtils; 22 | import org.springframework.util.StringUtils; 23 | 24 | import java.lang.reflect.Method; 25 | import java.util.HashSet; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | /** 30 | * yml解析器 31 | * Created by Ambitor on 2019/4/9 32 | * @author Ambitor 33 | */ 34 | @Slf4j 35 | @Component 36 | public class StateMachineYmlParser implements ApplicationContextAware, StateMachineParser { 37 | 38 | public static final String POINT = "."; 39 | 40 | /** 41 | * 状态机YML配置解析方法 42 | * @param config 43 | * @return 44 | */ 45 | @Override 46 | public StateMachineConfigurer parser(StateMachineYmlConfig config) { 47 | return new AbstractStateMachineConfigurerAdapter() { 48 | @Override 49 | public void configure(StateConfigurer states) { 50 | stateParser(states, config); 51 | } 52 | 53 | @Override 54 | public void configure(TransitionConfigurer transitions) { 55 | transitionParser(transitions, config); 56 | } 57 | 58 | @Override 59 | public void configure(StateMachineInterceptorConfigurer interceptors) { 60 | interceptorParser(interceptors, config); 61 | } 62 | 63 | @Override 64 | public String getName() { 65 | return config.getName(); 66 | } 67 | }; 68 | } 69 | 70 | /** 71 | * 拦截器解析 72 | * @param interceptors 拦截器配置 73 | * @param config yml配置文件 74 | */ 75 | private void interceptorParser(StateMachineInterceptorConfigurer interceptors, StateMachineYmlConfig config) { 76 | List interceptor = config.getInterceptor(); 77 | if (interceptor == null || interceptor.size() == 0) { 78 | log.info("No StateMachineInterceptorConfigurer Found"); 79 | return; 80 | } 81 | for (String interceptorName : interceptor) { 82 | StateMachineInterceptor expect = getBeanByClassName(StateMachineInterceptor.class, interceptorName); 83 | if (expect != null) { 84 | interceptors.register(expect); 85 | } 86 | } 87 | } 88 | 89 | /** 90 | * 转换器配置 91 | * @param transitions 转换器配置 92 | * @param config yml配置文件 93 | */ 94 | @SuppressWarnings("unchecked") 95 | private void transitionParser(TransitionConfigurer transitions, StateMachineYmlConfig config) { 96 | List configTransitions = config.getTransitions(); 97 | if (configTransitions != null && configTransitions.size() > 0) { 98 | for (int i = 0; i < configTransitions.size(); i++) { 99 | if (i > 0) { 100 | transitions.and(); 101 | } 102 | StateMachineYmlConfig.TransitionEntry entry = configTransitions.get(i); 103 | String type = entry.getType(); 104 | String source = entry.getSource(); 105 | Action action = getBeanByClassName(Action.class, entry.getAction()); 106 | String errorActionName = entry.getErrorAction(); 107 | Action errorAction = null; 108 | if (!StringUtils.isEmpty(errorActionName) && errorActionName.contains(POINT)) { 109 | //支持ClassName.method() 通过方法返回匿名类,只支持无参方法 110 | String[] array = errorActionName.split("\\" + POINT); 111 | String className = array[0]; 112 | Action error = getBeanByClassName(Action.class, className); 113 | if (error == null) { 114 | throw new StateMachineException("can not found error class " + className); 115 | } 116 | String methodName = array[1]; 117 | Method method = ReflectionUtils.findMethod(error.getClass(), methodName); 118 | if (method == null) { 119 | throw new StateMachineException("can not found method " + methodName + " in class " + className); 120 | } 121 | Object object = ReflectionUtils.invokeMethod(method, error); 122 | if (!(object instanceof Action)) { 123 | throw new StateMachineException(config.getName() + " machine " + errorActionName + " errorAction return value is not Action instance"); 124 | } 125 | errorAction = (Action) object; 126 | } else { 127 | errorAction = getBeanByClassName(Action.class, errorActionName); 128 | } 129 | String event = entry.getEvent(); 130 | if (TransitionType.standard.name().equals(type)) { 131 | String target = entry.getTarget(); 132 | StandardTransitionConfigurer standard = transitions.standardTransition(); 133 | standard.source(source).target(target).event(event).action(action, errorAction); 134 | transitions = standard; 135 | } else if (TransitionType.choice.name().equals(type)) { 136 | StateMachineYmlConfig.ChoiceTransitionVO first = entry.getFirst(); 137 | StateMachineYmlConfig.ChoiceTransitionVO then = entry.getThen(); 138 | StateMachineYmlConfig.ChoiceTransitionVO last = entry.getLast(); 139 | ChoiceTransitionConfigurer choice = transitions.choiceTransition(); 140 | choice.source(source); 141 | if (first != null) { 142 | choice.first(first.getTarget(), DefaultGuard.condition(first.getStatus(), first.getEquals())); 143 | } 144 | if (then != null) { 145 | choice.then(then.getTarget(), DefaultGuard.condition(then.getStatus(), then.getEquals())); 146 | } 147 | if (last != null) { 148 | choice.last(last.getTarget()); 149 | } 150 | choice.event(event).action(action, errorAction); 151 | transitions = choice; 152 | } else { 153 | throw new StateMachineException(type + " type not support"); 154 | } 155 | } 156 | } else { 157 | throw new StateMachineException("please defined transitions with .yml config"); 158 | } 159 | } 160 | 161 | /** 162 | * 状态解析 163 | * @param states 状态配置 164 | * @param config yml配置文件 165 | */ 166 | private void stateParser(StateConfigurer states, StateMachineYmlConfig config) { 167 | StateMachineYmlConfig.StateEntry stateEntry = config.getStates(); 168 | if (stateEntry == null) { 169 | throw new StateMachineException("please defined states with .yml config"); 170 | } 171 | StateConfigurer stateConfig = states.newStatesConfigurer(); 172 | // 定义初始状态 173 | stateConfig.initial(stateEntry.getInit()); 174 | // 定义挂起状态,遇到此状态后状态机自动挂起,直到再次手动触发事件(回调中),状态机继续往下执行 175 | List suspend = stateEntry.getSuspend(); 176 | if (suspend != null && suspend.size() > 0) { 177 | for (String s : suspend) { 178 | stateConfig.suspend(s); 179 | } 180 | } 181 | // 定义其它状态集合 182 | stateConfig.states(new HashSet<>(stateEntry.getOther())); 183 | // 定义结束状态 184 | List end = stateEntry.getEnd(); 185 | if (end != null && end.size() > 0) { 186 | for (String s : end) { 187 | stateConfig.end(s); 188 | } 189 | } 190 | } 191 | 192 | private T getBeanByClassName(Class clazz, String expect) { 193 | if (expect != null && expect.length() > 0) { 194 | Map interceptorMap = applicationContext.getBeansOfType(clazz); 195 | for (String key : interceptorMap.keySet()) { 196 | if (key.toLowerCase().contains(expect.toLowerCase()) || expect.toLowerCase().contains(key.toLowerCase())) { 197 | return interceptorMap.get(key); 198 | } 199 | } 200 | } 201 | return null; 202 | } 203 | 204 | @Override 205 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 206 | this.applicationContext = applicationContext; 207 | } 208 | 209 | private ApplicationContext applicationContext; 210 | 211 | } 212 | -------------------------------------------------------------------------------- /easy-statemachine-core/src/main/java/ambitor/easy/statemachine/core/AbstractStateMachine.java: -------------------------------------------------------------------------------- 1 | package ambitor.easy.statemachine.core; 2 | 3 | import ambitor.easy.statemachine.core.action.Action; 4 | import ambitor.easy.statemachine.core.context.DefaultMessage; 5 | import ambitor.easy.statemachine.core.context.DefaultStateContext; 6 | import ambitor.easy.statemachine.core.context.Message; 7 | import ambitor.easy.statemachine.core.context.MessageHeaders; 8 | import ambitor.easy.statemachine.core.context.StateContext; 9 | import ambitor.easy.statemachine.core.exception.StateMachineException; 10 | import ambitor.easy.statemachine.core.interceptor.StateMachineInterceptorList; 11 | import ambitor.easy.statemachine.core.state.State; 12 | import ambitor.easy.statemachine.core.transition.Transition; 13 | import lombok.extern.slf4j.Slf4j; 14 | import org.springframework.util.CollectionUtils; 15 | 16 | import java.text.MessageFormat; 17 | import java.util.Collection; 18 | import java.util.Map; 19 | 20 | /** 21 | * 状态机抽象类 22 | * Created by Ambitor on 2019/1/21 23 | * @param 状态 24 | * @param 事件 25 | */ 26 | @Slf4j 27 | public class AbstractStateMachine implements StateMachine { 28 | //所有状态 29 | private final Map> states; 30 | //所有转换器 31 | private final Map>> transitions; 32 | //初始化状态 33 | private final State initialState; 34 | //当前转换器 35 | private volatile Transition currentTransition; 36 | //当前状态 37 | private volatile State currentState; 38 | //当前事件 39 | private volatile Message currentEvent; 40 | //当前发生的异常 41 | private volatile Exception currentError; 42 | //拦截器 43 | private final StateMachineInterceptorList interceptors; 44 | 45 | 46 | public AbstractStateMachine(Map> states, Map>> transitions, 47 | State initialState, State currentState, Exception currentError, 48 | StateMachineInterceptorList interceptors) { 49 | this.states = states; 50 | this.transitions = transitions; 51 | this.initialState = initialState; 52 | this.currentState = currentState; 53 | this.currentError = currentError; 54 | this.interceptors = interceptors; 55 | } 56 | 57 | @Override 58 | public State getInitialState() { 59 | return initialState; 60 | } 61 | 62 | @Override 63 | public void setStateMachineError(Exception exception) { 64 | this.currentError = exception; 65 | } 66 | 67 | @Override 68 | public Exception getStateMachineError() { 69 | return currentError; 70 | } 71 | 72 | @Override 73 | public boolean sendEvent(Message event) { 74 | return sendEvent(event, false); 75 | } 76 | 77 | @Override 78 | public boolean sendEvent(E event) { 79 | return sendEvent(new DefaultMessage<>(event, null)); 80 | } 81 | 82 | @Override 83 | public boolean start() { 84 | return start(null); 85 | } 86 | 87 | @Override 88 | public boolean start(MessageHeaders headers) { 89 | E event = getCurrentEvent(); 90 | boolean accepted = sendEvent(new DefaultMessage<>(event, headers), true); 91 | return accepted && (currentState.isEnd() || currentState.isSuspend()); 92 | } 93 | 94 | @Override 95 | public State getState() { 96 | return currentState; 97 | } 98 | 99 | /** 100 | * 获取当前事件 101 | */ 102 | @Override 103 | public Message getEvent() { 104 | return currentEvent; 105 | } 106 | 107 | @Override 108 | public void resetStateMachine(S newState) { 109 | if(newState==null) throw new StateMachineException("状态不能为空"); 110 | this.currentState = states.get(newState); 111 | } 112 | 113 | @Override 114 | public Collection> getStates() { 115 | if (states == null) { 116 | return null; 117 | } 118 | return states.values(); 119 | } 120 | 121 | @Override 122 | public Map>> getTransitions() { 123 | return transitions; 124 | } 125 | 126 | /** 127 | * 当前转换器 128 | */ 129 | @Override 130 | public Transition transition() { 131 | return currentTransition; 132 | } 133 | 134 | @Override 135 | public boolean isComplete() { 136 | return currentState.isEnd() || currentState != null; 137 | } 138 | 139 | @Override 140 | public StateMachineInterceptorList interceptors() { 141 | return interceptors; 142 | } 143 | 144 | /** 145 | * 发送事件 146 | * @param event 事件 147 | * @param autoDrive 是否自动触发事件扭转状态 148 | * @return 是否接受事件 149 | */ 150 | private boolean sendEvent(Message event, boolean autoDrive) { 151 | boolean accepted = sendEventInternal(event); 152 | if (accepted && !currentState.isEnd()) { 153 | //自动驱动流程 154 | if (autoDrive) { 155 | //如果不是最终状态、不是挂起状态、并且状态机接受了上一事件 156 | while (!currentState.isEnd() && accepted && !currentState.isSuspend()) { 157 | E nextEvent = null; 158 | for (Transition transition : transitions.get(currentState.getId())) { 159 | if (transition.getSource().getId().equals(currentState.getId())) { 160 | nextEvent = transition.getEvent(); 161 | break; 162 | } 163 | } 164 | Message newMessage = new DefaultMessage<>(nextEvent, event.getHeaders()); 165 | accepted = sendEventInternal(newMessage); 166 | } 167 | } 168 | } 169 | return accepted; 170 | } 171 | 172 | 173 | /** 174 | * 发送事件 175 | * @param event 176 | * @return 是否接受事件 177 | */ 178 | private boolean sendEventInternal(Message event) { 179 | try { 180 | currentEvent = event; 181 | event = interceptors.preEvent(event, this); 182 | //找出当前状态所有的转换器 183 | Collection> trans = transitions.get(currentState.getId()); 184 | if (!CollectionUtils.isEmpty(trans)) { 185 | //事件的action是否已经执行 186 | boolean transitExecuted = false; 187 | for (Transition transition : trans) { 188 | //如果事件不是状态关心的 189 | if (!transition.getEvent().equals(event.getPayload())) { 190 | continue; 191 | } 192 | //设置当前转换器 193 | currentTransition = transition; 194 | /* 进入以下流程表示已经找到Transition */ 195 | StateContext stateContext = new DefaultStateContext<>(event, transition, transition.getSource(), transition.getTarget()); 196 | //如果transition.transit()没有执行过 197 | if (!transitExecuted) { 198 | //转换改变前拦截器 199 | interceptors.preStateChange(currentState, event, transition, this); 200 | //状态扭转前执行action, action执行失败表示不接受事件,返回false 201 | boolean accept = transition.transit(stateContext); 202 | //标记已执行 203 | transitExecuted = true; 204 | if (!accept) { 205 | setStateMachineError(stateContext.getException()); 206 | log.info("状态扭转失败,source {} -> target {} Event {}", currentState.getId(), transition.getTarget().getId(), event.getPayload()); 207 | //状态机发生异常 208 | interceptors.stateMachineError(this,currentEvent, stateContext.getException()); 209 | return false; 210 | } 211 | } 212 | //已经执行过transition.transit(),不用再执行,只需要判断guard() 213 | //StandardTransition只有一个默认的guard,返回true 214 | //ChoiceTransition 每个if/elseif分支有一个guard,但configurer已经确保必须有一个else分支使用guard返回true 215 | if (transition.guard().evaluate(stateContext)) { 216 | //转换成功 217 | return transitionSuccess(transition, stateContext, event); 218 | } 219 | } 220 | } 221 | String msg = MessageFormat.format("没有找到Transition, 状态{0},事件{1}", currentState.getId(), event.getPayload()); 222 | log.info(msg); 223 | setStateMachineError(new StateMachineException(msg)); 224 | return false; 225 | } catch (Exception e) { 226 | setStateMachineError(e); 227 | currentError = e; 228 | log.error("发送事件异常,未接受该事件" + event, e); 229 | if (interceptors != null) { 230 | interceptors.stateMachineError(this,currentEvent, currentError); 231 | } 232 | return false; 233 | } 234 | } 235 | 236 | /** 237 | * 转换成功 238 | */ 239 | private boolean transitionSuccess(Transition transition, StateContext stateContext, Message event) { 240 | log.info("状态扭转成功,source {} -> target {}", currentState.getId(), transition.getTarget().getId()); 241 | //触发state退出绑定的action 242 | fireStateAction(currentState.getExitActions(), stateContext); 243 | //扭转状态 244 | currentState = transition.getTarget(); 245 | //触发state进入绑定的action 246 | fireStateAction(currentState.getEntryActions(), stateContext); 247 | //状态改变后 248 | interceptors.afterStateChange(currentState, event, transition, this); 249 | return true; 250 | } 251 | 252 | /** 253 | * 触发状态绑定的action 254 | */ 255 | private void fireStateAction(Collection> actions, StateContext context) { 256 | if (!CollectionUtils.isEmpty(actions)) { 257 | for (Action action : actions) { 258 | action.execute(context); 259 | } 260 | } 261 | } 262 | 263 | private E getCurrentEvent() { 264 | Collection> trans = transitions.get(currentState.getId()); 265 | if (!CollectionUtils.isEmpty(trans)) { 266 | for (Transition transition : trans) { 267 | if (transition.getSource().getId().equals(getState().getId())) { 268 | return transition.getEvent(); 269 | } 270 | } 271 | } 272 | return null; 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /easy-statemachine-core/README.md: -------------------------------------------------------------------------------- 1 | # 轻量级状态机工作流引擎 2 | 3 | #### 有限状态机定义 4 | 5 | 有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。有限状态机体现了两点:首先是离散的,然后是有限的。以下是对状态机抽象定义 6 | 7 | State(状态):构成状态机的基本单位。 状态机在任何特定时间都可处于某一状态。从生命周期来看有`Initial State、End State、Suspend State(挂起状态)` 8 | 9 | Event(事件):导致转换发生的事件活动 10 | 11 | Transitions(转换器):两个状态之间的定向转换关系,状态机对发生的特定类型事件响应后当前状态由A转换到B。`标准转换、选择转、子流程转换`多种抽象实现 12 | 13 | Actions(转换操作):在执行某个转换时执行的具体操作。 14 | 15 | Guards(检测器):检测器出现的原因是为了转换操作执行后检测结果是否满足特定条件从一个状态切换到某一个状态 16 | 17 | Interceptor(拦截器):对当前状态改变前、后进行监听拦截。如:每个状态变更后插入日志等 18 | 19 | ![](https://oscimg.oschina.net/oscnet/9724f02b886173ed28f7a802e1f20e1f40c.jpg) 20 | 21 |                                  状态机扭转图 22 | 23 | ### 状态机实现 待补 24 | 状态机`代码解耦、高效率开发/维护、节省开发成本`等一系列好处.... 25 | ### 状态机使用 26 | 目前支持以下两种方式配置状态机: 27 | - yml配置文件(推荐):只需要编写少量的业务实现类和yml配置文件即可完成 28 | - 代码方式配置:稍微复杂,但可以更加直观的了解状态机的配置 29 | - web界面拖拽式配置(未来版本) 30 | 31 | #### yml配置方式 32 | ``` 33 | #状态机名称 34 | name: sf 35 | 36 | #状态配置 37 | states: 38 | init: WAIT_CREATE_CARDII #等待开二类户 39 | suspend: 40 | - WAIT_DOCUMENT_CREDIT_CALLBACK #等待建档授信回调 41 | - WAIT_GRANT_CHECK #放款检查 42 | end: 43 | - CREATE_CARDII_FAILED #开二类户失败 44 | - DOCUMENT_CREDIT_FAILED #建档授信失败 45 | - GRANT_FAILED #放款失败 46 | - GRANT_SUCCESS #结束流程 47 | other: 48 | - WAIT_DOCUMENT_CREDIT #建档授信 49 | - WAIT_GRANT #放款 50 | - WAIT_GRANT_CHECK #等待放款校验 51 | - GRANT_TASK_SAVE #主流程完成 52 | 53 | #事件配置 54 | events: 55 | - CREATE_CARDII #开二类户 56 | - DOCUMENT_CREDIT #建档授信 57 | - DOCUMENT_CREDIT_CALLBACK #建档授信回调 58 | - GRANTED #放款 59 | - GRANT_CHECKED #放款校验 60 | - FINISHED #结束 61 | 62 | #转换器配置 63 | transitions: 64 | - type: standard #类型: 标准转换器 65 | source: WAIT_CREATE_CARDII #源状态:等待创建二类户 66 | target: WAIT_DOCUMENT_CREDIT #目标状态:等待建档授信 67 | event: CREATE_CARDII #事件: 创建二类户 68 | action: SFCreateCardIIAction.class #转换操作:创建二类户业务实现类 69 | errorAction: 70 | 71 | - type: choice #类型:选择转换器 72 | source: WAIT_DOCUMENT_CREDIT #源状态:等待建档授信 73 | event: DOCUMENT_CREDIT #事件:建档授信 74 | action: SFDocumentCreditAction.class #转换操作:建档授信业务实现类 75 | errorAction: 76 | if: {status: DOCUMENT_CREDIT_STATUS,equals: DOCUMENT_CREDIT_SUCCESS,target: WAIT_GRANT} 77 | elseif: {status: DOCUMENT_CREDIT_STATUS,equals: WAIT_DOCUMENT_CREDIT_CALLBACK,target: WAIT_DOCUMENT_CREDIT_CALLBACK} 78 | else: {target: DOCUMENT_CREDIT_FAILED} 79 | 80 | - type: choice 81 | source: WAIT_DOCUMENT_CREDIT_CALLBACK #源状态:等待建档授信回调 82 | event: DOCUMENT_CREDIT_CALLBACK #事件: 建档授信回调 83 | if: {key: DOCUMENT_CREDIT_STATUS,equals: DOCUMENT_CREDIT_SUCCESS,target: WAIT_GRANT} 84 | else: {target: DOCUMENT_CREDIT_FAILED} 85 | 86 | - type: choice 87 | source: WAIT_GRANT #源状态:等待放款 88 | event: GRANTED #事件:放款 89 | action: SFGrantAction.class #转换操作:放款业务实现类 90 | if: {status: GRANT_STATUS,equals: GRANT_SUCCESS,target: GRANT_TASK_SAVE} 91 | else: {target: WAIT_GRANT_CHECK} 92 | 93 | - type: choice 94 | source: WAIT_GRANT_CHECK #源状态:等待放款 95 | event: GRANT_CHECKED #事件:放款 96 | action: SFGrantAction.class #转换操作:放款业务实现类 97 | if: {status: GRANT_STATUS,equals: GRANT_SUCCESS,target: GRANT_TASK_SAVE} 98 | else: {target: GRANT_FAILED} 99 | 100 | - type: standard 101 | source: GRANT_TASK_SAVE #源状态:放款任务保存 102 | target: GRANT_SUCCESS #目标状态:放款成功 103 | event: FINISHED #事件: 放款结束 104 | action: SFFinishAction.class #转换操作:放款结束保存任务业务实现类 105 | 106 | ``` 107 | 108 | #### 代码方式配置: 109 | 110 | ``` 111 | /** 112 | * 定义状态枚举 113 | */ 114 | public enum SFGrantState { 115 | //等待开二类户 116 | WAIT_CREATE_CARDII, 117 | //开二类户失败 118 | CREATE_CARDII_FAILED, 119 | //建档授信 120 | WAIT_DOCUMENT_CREDIT, 121 | //等待建档授信回调 122 | WAIT_DOCUMENT_CREDIT_CALLBACK, 123 | //建档授信失败 124 | DOCUMENT_CREDIT_FAILED, 125 | //放款 126 | WAIT_GRANT, 127 | //放款失败 128 | GRANT_FAILED, 129 | //等待放款校验 130 | WAIT_GRANT_CHECK, 131 | //主流程完成 132 | GRANT_TASK_SAVE, 133 | //结束流程 134 | GRANT_SUCCESS 135 | } 136 | 137 | /** 138 | * 定义事件枚举 139 | */ 140 | public enum SFGrantEvent { 141 | //开二类户 142 | CREATE_CARDII, 143 | //建档授信 144 | DOCUMENT_CREDIT, 145 | //建档授信回调 146 | DOCUMENT_CREDIT_CALLBACK, 147 | //放款 148 | GRANTED, 149 | //放款校验 150 | GRANT_CHECKED, 151 | //结束 152 | FINISHED 153 | } 154 | 155 | /** 156 | * 放款工作流配置 157 | */ 158 | @Slf4j 159 | @EnableWithStateMachine //集成Spring IOC 的注解 160 | public class SFGrantStateMachineConfig extends StateMachineConfigurerAdapter { 161 | 162 | /** 163 | * 工作流所有节点状态配置 164 | */ 165 | @Override 166 | public void configure(StateConfigurer states) { 167 | states 168 | .newStatesConfigurer() 169 | // 定义初始状态 170 | .initial(SFGrantState.WAIT_CREATE_CARDII) 171 | // 定义挂起状态,遇到此状态后状态机自动挂起,直到再次手动触发事件(回调中),状态机继续往下执行 172 | .suspend(SFGrantState.WAIT_DOCUMENT_CREDIT_CALLBACK) 173 | // 放款校验状态 174 | .suspend(SFGrantState.WAIT_GRANT_CHECK) 175 | // 定义其它所有状态集合 176 | .states(EnumSet.allOf(SFGrantState.class)) 177 | //定义结束状态,开二类户失败、建档授信失败、放款失败、放款成功都是结束状态 178 | .end(SFGrantState.CREATE_CARDII_FAILED) 179 | .end(SFGrantState.DOCUMENT_CREDIT_FAILED) 180 | .end(SFGrantState.GRANT_FAILED) 181 | .end(SFGrantState.GRANT_SUCCESS); 182 | } 183 | 184 | /** 185 | * 状态扭转器配置,配置工作流通过 event(触发事件)把状态从 186 | * source status(源状态) 转到 target status (目标状态) 187 | * 可根据上一步建档授信结果转换成不同的 target status (目标状态) 188 | * 代码完全解耦,把之前在Task中创建下一节点的IF/ELSE抽象成配置信息 189 | * 具体配置情况如下,细看 2、等待建档授信步骤 190 | */ 191 | @Override 192 | public void configure(TransitionConfigurer transitions) { 193 | transitions 194 | /** 1、等待创建二类户 **/ 195 | .standardTransition()//标准转换器,不具备结果判断,事件触发后只能从X状态转为Y状态 196 | .source(SFGrantState.WAIT_CREATE_CARDII) 197 | .target(SFGrantState.WAIT_DOCUMENT_CREDIT) 198 | .event(SFGrantEvent.CREATE_CARDII) 199 | .action(sfCreateCardIIAction, sfCreateCardIIAction.errorAction()) 200 | .and() 201 | 202 | /** 2、等待建档授信步骤 **/ 203 | .choiceTransition()//具备选择结果的转换器,可根据当前事件执行结果扭转到不同状态 204 | 205 | //原状态为等待建档授信 206 | .source(SFGrantState.WAIT_DOCUMENT_CREDIT) 207 | 208 | //first相当于if,如果建档授信状态返回DOCUMENT_CREDIT_SUCCESS则转换成WAIT_GRANT等待放款 209 | .first(SFGrantState.WAIT_GRANT, GrantGuard.condition(DOCUMENT_CREDIT_STATUS, DOCUMENT_CREDIT_SUCCESS)) 210 | 211 | //then相当于elseif,如果建档授信状态返回WAIT_DOCUMENT_CREDIT_CALLBACK则转换成等待建档授信回调 212 | .then(SFGrantState.WAIT_DOCUMENT_CREDIT_CALLBACK, GrantGuard.condition(DOCUMENT_CREDIT_STATUS, WAIT_DOCUMENT_CREDIT_CALLBACK)) 213 | 214 | //last相当于else,如果都不是则返回建档授信失败 215 | .last(SFGrantState.DOCUMENT_CREDIT_FAILED) 216 | 217 | //触发事件 218 | .event(SFGrantEvent.DOCUMENT_CREDIT) 219 | 220 | //转换器执行的转换action 221 | .action(sfDocumentCreditAction, sfDocumentCreditAction.errorAction()) 222 | 223 | //不同节点转换器的拼接,类似StringBuulder.append() 224 | .and() 225 | 226 | /** 3、等待建档授信回调步骤 **/ 227 | .choiceTransition() 228 | .source(SFGrantState.WAIT_DOCUMENT_CREDIT_CALLBACK) 229 | .first(SFGrantState.WAIT_GRANT, GrantGuard.condition(DOCUMENT_CREDIT_STATUS, DOCUMENT_CREDIT_SUCCESS)) 230 | .last(SFGrantState.DOCUMENT_CREDIT_FAILED) 231 | .event(SFGrantEvent.DOCUMENT_CREDIT_CALLBACK) 232 | .and() 233 | 234 | /** 4、等待放款流程 **/ 235 | .choiceTransition() 236 | .source(SFGrantState.WAIT_GRANT) 237 | .first(SFGrantState.GRANT_TASK_SAVE, GrantGuard.condition(GRANT_STATUS, GRANT_SUCCESS)) 238 | .last(SFGrantState.WAIT_GRANT_CHECK) 239 | .event(SFGrantEvent.GRANTED) 240 | .action(sfGrantAction) 241 | .and() 242 | 243 | /** 5、放款检查流程,如果上一步操作超时 **/ 244 | .choiceTransition() 245 | .source(SFGrantState.WAIT_GRANT_CHECK) 246 | .first(SFGrantState.GRANT_TASK_SAVE, GrantGuard.condition(GRANT_STATUS, GRANT_SUCCESS)) 247 | .last(SFGrantState.GRANT_FAILED) 248 | .event(SFGrantEvent.GRANT_CHECKED) 249 | .and() 250 | 251 | /** 6、最后完成的流程 **/ 252 | .standardTransition() 253 | .source(SFGrantState.GRANT_TASK_SAVE).target(SFGrantState.GRANT_SUCCESS) 254 | .event(SFGrantEvent.FINISHED) 255 | .action(sfFinishAction); 256 | /** 257 | * 注册拦截器 258 | */ 259 | @Override 260 | public void configure(StateMachineInterceptorConfigurer interceptors) { 261 | //状态改变持久化到数据库拦截器 262 | interceptors.register(persistStateMachineInterceptor); 263 | } 264 | } 265 | 266 | ``` 267 | 268 | 放款工作流调用代码 269 | 270 | ``` 271 | //根据工作流名称获取配置信息 272 | StateMachineConfigurer configurer = getByName(task.getMachineType()); 273 | //根据工作流配置创建一个工作流实例 274 | StateMachine stateMachine = StateMachineFactory.build(configurer); 275 | //开始运行,可传工作流需要参数 276 | stateMachine.start(params); 277 | 278 | ``` 279 | 280 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easy-statemachine 2 | 轻量级状态机工作流框架 3 | 4 | ### 项目介绍 5 | 项目目前主要份为3大块,未来会随着功能的丰富拆分更多的项目 6 | 7 | - easy-statemachine-core 状态机核心项目,是对有限状态机核心原理实现 8 | 9 | - easy-statemachine-wokflow 状态机工作流项目是基于easy-statemachine-core 实现的轻量级状态机工作流框架 10 | 11 | - easy-statemachine-demo 可运行Demo 12 | 13 | - easy-statemachine-common 分布式中的公共组件,如:通信,选举等(待开发) 14 | 15 | 16 | #### 有限状态机定义 17 | 18 | 有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。有限状态机体现了两点:首先是离散的,然后是有限的。以下是对状态机抽象定义 19 | 20 | State(状态):构成状态机的基本单位。 状态机在任何特定时间都可处于某一状态。从生命周期来看有`Initial State、End State、Suspend State(挂起状态)` 21 | 22 | Event(事件):导致转换发生的事件活动 23 | 24 | Transitions(转换器):两个状态之间的定向转换关系,状态机对发生的特定类型事件响应后当前状态由A转换到B。`标准转换、选择转、子流程转换`多种抽象实现 25 | 26 | Actions(转换操作):在执行某个转换时执行的具体操作。 27 | 28 | Guards(检测器):检测器出现的原因是为了转换操作执行后检测结果是否满足特定条件从一个状态切换到某一个状态 29 | 30 | Interceptor(拦截器):对当前状态改变前、后进行监听拦截。如:每个状态变更后插入日志等 31 | 32 | ![](https://oscimg.oschina.net/oscnet/9724f02b886173ed28f7a802e1f20e1f40c.jpg) 33 | 34 |                                  状态机扭转图 35 | 36 | #### 状态机实现 待补 37 | 状态机`代码解耦、高效率开发/维护、节省开发成本`等一系列好处.... 38 | #### 状态机使用 39 | 目前支持以下两种方式配置状态机: 40 | - yml配置文件(推荐):只需要编写少量的业务实现类和yml配置文件即可完成 41 | - 代码方式配置:稍微复杂,但可以更加直观的了解状态机的配置 42 | - web界面拖拽式配置(未来版本) 43 | 44 | #### yml配置方式 45 | ``` 46 | #状态机名称 47 | name: hxd 48 | 49 | #拦截器配置 50 | interceptor: 51 | - PersistStateMachineInterceptor.class 52 | 53 | #状态配置 54 | states: 55 | init: WAIT_CREATE_CARDII #等待开二类户 56 | suspend: 57 | end: 58 | - GRANT_FAILED #放款失败 59 | - GRANT_SUCCESS #结束流程 60 | other: 61 | - WAIT_PRE_VALID #放款前校验 62 | - WAIT_PRE_VALID_CHECK #放款前校验检查 63 | - WAIT_SIGN_CONTRACT #等待签署合同 64 | - WAIT_GRANT #放款 65 | - WAIT_GRANT_CHECK #等待放款校验 66 | - GRANT_FAILED_HANDLER #放款失败处理 67 | - GRANT_SUCCESS_HANDLER #放款成功处理 68 | 69 | #事件配置 70 | events: 71 | - CREATE_CARDII #开二类户 72 | - GRANT_VALID #放款前落地校验 73 | - GRANT_VALID_CHECK #放款前落地校验Check 74 | - SIGN_CONTRACT #签署合同 75 | - GRANTED #放款 76 | - GRANT_CHECKED #放款校验 77 | - GRANTED_RESULT_HANDLE #放款结果处理 78 | 79 | #转换器配置 80 | transitions: 81 | - type: choice #类型: 选择转换器 82 | source: WAIT_CREATE_CARDII #源状态:等待创建二类户 83 | event: CREATE_CARDII #事件: 创建二类户 84 | first: {status: GRANT_STATUS,equals: CREATE_CARDII_SUCCESS,target: WAIT_PRE_VALID} 85 | last: {target: GRANT_FAILED_HANDLER} 86 | action: BaseCreateCardIIAction.class #转换操作:创建二类户业务实现类 87 | errorAction: BaseCreateCardIIAction.errorAction 88 | 89 | - type: choice #类型: 选择转换器 90 | source: WAIT_PRE_VALID #源状态:等待放款前落地校验 91 | event: GRANT_VALID #事件: 放款前落地校验 92 | first: {status: GRANT_STATUS,equals: GRANT_PRE_VALID_SUCCESS,target: WAIT_SIGN_CONTRACT} 93 | last: {target: WAIT_PRE_VALID_CHECK} 94 | action: BasePreValidAction.class #转换操作:放款前落地校验 95 | errorAction: 96 | 97 | - type: choice #类型: 选择转换器 98 | source: WAIT_PRE_VALID_CHECK #源状态:等待放款前落地校验Check 99 | event: GRANT_VALID_CHECK #事件: 放款前落地校验Check 100 | first: {status: GRANT_STATUS,equals: GRANT_PRE_VALID_SUCCESS,target: WAIT_SIGN_CONTRACT} 101 | last: {target: GRANT_FAILED_HANDLER} 102 | action: BasePreValidCheckAction.class #转换操作:放款前落地校验 103 | errorAction: BasePreValidCheckAction.errorAction 104 | 105 | - type: choice #类型: 选择转换器 106 | source: WAIT_SIGN_CONTRACT #源状态:等待签署合同 107 | event: SIGN_CONTRACT #事件: 签署合同 108 | first: {status: GRANT_STATUS,equals: SIGN_CONTRACT_SUCCESS,target: WAIT_GRANT} 109 | last: {target: GRANT_FAILED_HANDLER} 110 | action: BaseContractAction.class #转换操作:签合同Action 111 | errorAction: BaseContractAction.errorAction 112 | 113 | - type: choice #类型:选择转换器 114 | source: WAIT_GRANT #源状态:等待放款 115 | event: GRANTED #事件:放款 116 | action: BaseGrantAction.class #转换操作:放款实现类 117 | errorAction: 118 | first: {status: GRANT_STATUS,equals: GRANT_SUCCESS,target: GRANT_SUCCESS_HANDLER} 119 | last: {target: WAIT_GRANT_CHECK} 120 | 121 | - type: choice 122 | source: WAIT_GRANT_CHECK #源状态:等待放款校验 123 | event: GRANT_CHECKED #事件: 放款校验 124 | errorAction: BaseGrantCheckAction.errorAction 125 | first: {status: GRANT_STATUS,equals: GRANT_SUCCESS,target: GRANT_SUCCESS_HANDLER} 126 | last: {target: GRANT_FAILED_HANDLER} 127 | 128 | - type: standard #类型: 标准转换器 129 | source: GRANT_SUCCESS_HANDLER #源状态: 放款成功处理 130 | target: GRANT_SUCCESS #目标状态:放款成功 131 | event: GRANTED_RESULT_HANDLE #事件: 放款结果处理 132 | action: BaseGrantSuccessAction.class #转换操作: 众安放款成功业务实现类 133 | 134 | - type: standard #类型: 标准转换器 135 | source: GRANT_FAILED_HANDLER #源状态: 放款失败处理 136 | target: GRANT_FAILED #目标状态:放款失败 137 | event: GRANTED_RESULT_HANDLE #事件: 放款结果处理 138 | action: BaseGrantFailedAction.class 139 | 140 | ``` 141 | 142 | #### 代码方式配置: 143 | 144 | ``` 145 | /** 146 | * 定义状态枚举 147 | */ 148 | public enum SFGrantState { 149 | //等待开二类户 150 | WAIT_CREATE_CARDII, 151 | //开二类户失败 152 | CREATE_CARDII_FAILED, 153 | //建档授信 154 | WAIT_DOCUMENT_CREDIT, 155 | //等待建档授信回调 156 | WAIT_DOCUMENT_CREDIT_CALLBACK, 157 | //建档授信失败 158 | DOCUMENT_CREDIT_FAILED, 159 | //放款 160 | WAIT_GRANT, 161 | //放款失败 162 | GRANT_FAILED, 163 | //等待放款校验 164 | WAIT_GRANT_CHECK, 165 | //主流程完成 166 | GRANT_TASK_SAVE, 167 | //结束流程 168 | GRANT_SUCCESS 169 | } 170 | 171 | /** 172 | * 定义事件枚举 173 | */ 174 | public enum SFGrantEvent { 175 | //开二类户 176 | CREATE_CARDII, 177 | //建档授信 178 | DOCUMENT_CREDIT, 179 | //建档授信回调 180 | DOCUMENT_CREDIT_CALLBACK, 181 | //放款 182 | GRANTED, 183 | //放款校验 184 | GRANT_CHECKED, 185 | //结束 186 | FINISHED 187 | } 188 | 189 | /** 190 | * 放款工作流配置 191 | */ 192 | @Slf4j 193 | @EnableWithStateMachine //集成Spring IOC 的注解 194 | public class SFGrantStateMachineConfig extends StateMachineConfigurerAdapter { 195 | 196 | /** 197 | * 工作流所有节点状态配置 198 | */ 199 | @Override 200 | public void configure(StateConfigurer states) { 201 | states 202 | .newStatesConfigurer() 203 | // 定义初始状态 204 | .initial(SFGrantState.WAIT_CREATE_CARDII) 205 | // 定义挂起状态,遇到此状态后状态机自动挂起,直到再次手动触发事件(回调中),状态机继续往下执行 206 | .suspend(SFGrantState.WAIT_DOCUMENT_CREDIT_CALLBACK) 207 | // 放款校验状态 208 | .suspend(SFGrantState.WAIT_GRANT_CHECK) 209 | // 定义其它所有状态集合 210 | .states(EnumSet.allOf(SFGrantState.class)) 211 | //定义结束状态,开二类户失败、建档授信失败、放款失败、放款成功都是结束状态 212 | .end(SFGrantState.CREATE_CARDII_FAILED) 213 | .end(SFGrantState.DOCUMENT_CREDIT_FAILED) 214 | .end(SFGrantState.GRANT_FAILED) 215 | .end(SFGrantState.GRANT_SUCCESS); 216 | } 217 | 218 | /** 219 | * 状态扭转器配置,配置工作流通过 event(触发事件)把状态从 220 | * source status(源状态) 转到 target status (目标状态) 221 | * 可根据上一步建档授信结果转换成不同的 target status (目标状态) 222 | * 代码完全解耦,把之前在Task中创建下一节点的IF/ELSE抽象成配置信息 223 | * 具体配置情况如下,细看 2、等待建档授信步骤 224 | */ 225 | @Override 226 | public void configure(TransitionConfigurer transitions) { 227 | transitions 228 | /** 1、等待创建二类户 **/ 229 | .standardTransition()//标准转换器,不具备结果判断,事件触发后只能从X状态转为Y状态 230 | .source(SFGrantState.WAIT_CREATE_CARDII) 231 | .target(SFGrantState.WAIT_DOCUMENT_CREDIT) 232 | .event(SFGrantEvent.CREATE_CARDII) 233 | .action(sfCreateCardIIAction, sfCreateCardIIAction.errorAction()) 234 | .and() 235 | 236 | /** 2、等待建档授信步骤 **/ 237 | .choiceTransition()//具备选择结果的转换器,可根据当前事件执行结果扭转到不同状态 238 | 239 | //原状态为等待建档授信 240 | .source(SFGrantState.WAIT_DOCUMENT_CREDIT) 241 | 242 | //first相当于if,如果建档授信状态返回DOCUMENT_CREDIT_SUCCESS则转换成WAIT_GRANT等待放款 243 | .first(SFGrantState.WAIT_GRANT, GrantGuard.condition(DOCUMENT_CREDIT_STATUS, DOCUMENT_CREDIT_SUCCESS)) 244 | 245 | //then相当于elseif,如果建档授信状态返回WAIT_DOCUMENT_CREDIT_CALLBACK则转换成等待建档授信回调 246 | .then(SFGrantState.WAIT_DOCUMENT_CREDIT_CALLBACK, GrantGuard.condition(DOCUMENT_CREDIT_STATUS, WAIT_DOCUMENT_CREDIT_CALLBACK)) 247 | 248 | //last相当于else,如果都不是则返回建档授信失败 249 | .last(SFGrantState.DOCUMENT_CREDIT_FAILED) 250 | 251 | //触发事件 252 | .event(SFGrantEvent.DOCUMENT_CREDIT) 253 | 254 | //转换器执行的转换action 255 | .action(sfDocumentCreditAction, sfDocumentCreditAction.errorAction()) 256 | 257 | //不同节点转换器的拼接,类似StringBuulder.append() 258 | .and() 259 | 260 | /** 3、等待建档授信回调步骤 **/ 261 | .choiceTransition() 262 | .source(SFGrantState.WAIT_DOCUMENT_CREDIT_CALLBACK) 263 | .first(SFGrantState.WAIT_GRANT, GrantGuard.condition(DOCUMENT_CREDIT_STATUS, DOCUMENT_CREDIT_SUCCESS)) 264 | .last(SFGrantState.DOCUMENT_CREDIT_FAILED) 265 | .event(SFGrantEvent.DOCUMENT_CREDIT_CALLBACK) 266 | .and() 267 | 268 | /** 4、等待放款流程 **/ 269 | .choiceTransition() 270 | .source(SFGrantState.WAIT_GRANT) 271 | .first(SFGrantState.GRANT_TASK_SAVE, GrantGuard.condition(GRANT_STATUS, GRANT_SUCCESS)) 272 | .last(SFGrantState.WAIT_GRANT_CHECK) 273 | .event(SFGrantEvent.GRANTED) 274 | .action(sfGrantAction) 275 | .and() 276 | 277 | /** 5、放款检查流程,如果上一步操作超时 **/ 278 | .choiceTransition() 279 | .source(SFGrantState.WAIT_GRANT_CHECK) 280 | .first(SFGrantState.GRANT_TASK_SAVE, GrantGuard.condition(GRANT_STATUS, GRANT_SUCCESS)) 281 | .last(SFGrantState.GRANT_FAILED) 282 | .event(SFGrantEvent.GRANT_CHECKED) 283 | .and() 284 | 285 | /** 6、最后完成的流程 **/ 286 | .standardTransition() 287 | .source(SFGrantState.GRANT_TASK_SAVE).target(SFGrantState.GRANT_SUCCESS) 288 | .event(SFGrantEvent.FINISHED) 289 | .action(sfFinishAction); 290 | /** 291 | * 注册拦截器 292 | */ 293 | @Override 294 | public void configure(StateMachineInterceptorConfigurer interceptors) { 295 | //状态改变持久化到数据库拦截器 296 | interceptors.register(persistStateMachineInterceptor); 297 | } 298 | } 299 | 300 | ``` 301 | 302 | 放款工作流调用代码 303 | 304 | ``` 305 | //根据工作流名称获取配置信息 306 | StateMachineConfigurer configurer = getByName(task.getMachineType()); 307 | //根据工作流配置创建一个工作流实例 308 | StateMachine stateMachine = StateMachineFactory.build(configurer); 309 | //开始运行,可传工作流需要参数 310 | stateMachine.start(params); 311 | 312 | ``` 313 | 314 | --------------------------------------------------------------------------------