diff --git a/pom.xml b/pom.xml
index d3f7602..74311d5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,6 +32,18 @@
4.2.1
+
+ org.springframework.statemachine
+ spring-statemachine-core
+ 3.2.0
+
+
+
+
+ org.springframework
+ spring-context
+
+
org.apache.commons
@@ -45,6 +57,24 @@
3.8.1
test
+
+ org.testng
+ testng
+ 7.4.0
+ compile
+
+
+ org.testng
+ testng
+ 7.4.0
+ compile
+
+
+ org.testng
+ testng
+ 7.4.0
+ compile
+
diff --git a/src/main/java/com/tuoheng/status/airport/action/airport/OfflineAction.java b/src/main/java/com/tuoheng/status/airport/action/airport/OfflineAction.java
new file mode 100644
index 0000000..21be67f
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/airport/OfflineAction.java
@@ -0,0 +1,20 @@
+package com.tuoheng.status.airport.action.airport;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 机巢离线动作
+ */
+@Component
+public class OfflineAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("执行机巢离线操作");
+ // TODO: 实现机巢离线的具体逻辑
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/airport/OnlineAction.java b/src/main/java/com/tuoheng/status/airport/action/airport/OnlineAction.java
new file mode 100644
index 0000000..2348773
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/airport/OnlineAction.java
@@ -0,0 +1,18 @@
+package com.tuoheng.status.airport.action.airport;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+@Component
+public class OnlineAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+
+ }
+
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/cover/CloseCoverAction.java b/src/main/java/com/tuoheng/status/airport/action/cover/CloseCoverAction.java
new file mode 100644
index 0000000..9d3a0c7
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/cover/CloseCoverAction.java
@@ -0,0 +1,20 @@
+package com.tuoheng.status.airport.action.cover;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 关舱动作
+ */
+@Component
+public class CloseCoverAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("执行关舱操作");
+ // TODO: 发送 cover_close 指令
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/cover/CoverClosedAction.java b/src/main/java/com/tuoheng/status/airport/action/cover/CoverClosedAction.java
new file mode 100644
index 0000000..fb9d647
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/cover/CoverClosedAction.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.action.cover;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 舱门关闭完成动作
+ * 处理舱门完全关闭后的逻辑
+ */
+@Component
+public class CoverClosedAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("舱门已完全关闭,执行后续处理");
+ // TODO: 舱门关闭完成后的处理逻辑(例如:记录日志、自动关闭调试模式等)
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/cover/CoverOpenedAction.java b/src/main/java/com/tuoheng/status/airport/action/cover/CoverOpenedAction.java
new file mode 100644
index 0000000..7b948a9
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/cover/CoverOpenedAction.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.action.cover;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 舱门打开完成动作
+ * 处理舱门完全打开后的逻辑
+ */
+@Component
+public class CoverOpenedAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("舱门已完全打开,执行后续处理");
+ // TODO: 舱门打开完成后的处理逻辑(例如:记录日志、通知相关模块等)
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/cover/OpenCoverAction.java b/src/main/java/com/tuoheng/status/airport/action/cover/OpenCoverAction.java
new file mode 100644
index 0000000..77370f7
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/cover/OpenCoverAction.java
@@ -0,0 +1,20 @@
+package com.tuoheng.status.airport.action.cover;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 开舱动作
+ */
+@Component
+public class OpenCoverAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("执行开舱操作");
+ // TODO: 发送 cover_open 指令
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/debug/CloseDebugModeAction.java b/src/main/java/com/tuoheng/status/airport/action/debug/CloseDebugModeAction.java
new file mode 100644
index 0000000..ab7ecf4
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/debug/CloseDebugModeAction.java
@@ -0,0 +1,20 @@
+package com.tuoheng.status.airport.action.debug;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 关闭调试模式动作
+ */
+@Component
+public class CloseDebugModeAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("执行关闭调试模式操作");
+ // TODO: 发送 debug_mode_close 指令
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/debug/OpenDebugModeAction.java b/src/main/java/com/tuoheng/status/airport/action/debug/OpenDebugModeAction.java
new file mode 100644
index 0000000..5a6fa70
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/debug/OpenDebugModeAction.java
@@ -0,0 +1,20 @@
+package com.tuoheng.status.airport.action.debug;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 开启调试模式动作
+ */
+@Component
+public class OpenDebugModeAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("执行开启调试模式操作");
+ // TODO: 发送 debug_mode_open 指令
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/reboot/RebootAction.java b/src/main/java/com/tuoheng/status/airport/action/reboot/RebootAction.java
new file mode 100644
index 0000000..8a41802
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/reboot/RebootAction.java
@@ -0,0 +1,20 @@
+package com.tuoheng.status.airport.action.reboot;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 机巢重启动作
+ */
+@Component
+public class RebootAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("执行机巢重启操作");
+ // TODO: 发送 device_reboot 指令
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/action/reboot/RebootCompletedAction.java b/src/main/java/com/tuoheng/status/airport/action/reboot/RebootCompletedAction.java
new file mode 100644
index 0000000..67553a1
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/action/reboot/RebootCompletedAction.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.action.reboot;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+import org.springframework.stereotype.Component;
+
+/**
+ * 机巢重启完成动作
+ * 处理机巢重启完成后的逻辑
+ */
+@Component
+public class RebootCompletedAction implements Action {
+
+ @Override
+ public void execute(StateContext stateContext) {
+ System.out.println("机巢重启完成,执行初始化处理");
+ // TODO: 机巢重启完成后的处理逻辑(例如:重新初始化、恢复状态等)
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/config/AirportMachineConfig.java b/src/main/java/com/tuoheng/status/airport/config/AirportMachineConfig.java
new file mode 100644
index 0000000..e18070a
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/config/AirportMachineConfig.java
@@ -0,0 +1,187 @@
+package com.tuoheng.status.airport.config;
+
+import com.tuoheng.status.airport.action.airport.OfflineAction;
+import com.tuoheng.status.airport.action.airport.OnlineAction;
+import com.tuoheng.status.airport.action.debug.CloseDebugModeAction;
+import com.tuoheng.status.airport.action.debug.OpenDebugModeAction;
+import com.tuoheng.status.airport.action.reboot.RebootAction;
+import com.tuoheng.status.airport.action.reboot.RebootCompletedAction;
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.guard.airport.CanOfflineGuard;
+import com.tuoheng.status.airport.guard.airport.CanOnlineGuard;
+import com.tuoheng.status.airport.guard.debug.CanCloseDebugModeGuard;
+import com.tuoheng.status.airport.guard.debug.IsDebugModeGuard;
+import com.tuoheng.status.airport.guard.debug.IsNotDebugModeGuard;
+import com.tuoheng.status.airport.guard.reboot.IsRebootCompletedGuard;
+import com.tuoheng.status.airport.listener.AirPortStatusListener;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.config.StateMachineBuilder;
+import org.springframework.statemachine.config.StateMachineFactory;
+
+import java.util.EnumSet;
+import java.util.UUID;
+
+/**
+ * 机巢状态机配置(简化版 - 舱门状态已分离)
+ */
+@Configuration
+public class AirportMachineConfig {
+
+ @Autowired
+ AirPortStatusListener airPortStatusListener;
+
+ // ==================== Actions ====================
+ @Autowired
+ OnlineAction onlineAction;
+
+ @Autowired
+ OfflineAction offlineAction;
+
+ @Autowired
+ OpenDebugModeAction openDebugModeAction;
+
+ @Autowired
+ CloseDebugModeAction closeDebugModeAction;
+
+ @Autowired
+ RebootAction rebootAction;
+
+ @Autowired
+ RebootCompletedAction rebootCompletedAction;
+
+ // ==================== Guards ====================
+ @Autowired
+ CanOnlineGuard canOnlineGuard;
+
+ @Autowired
+ CanOfflineGuard canOfflineGuard;
+
+ @Autowired
+ IsDebugModeGuard isDebugModeGuard;
+
+ @Autowired
+ IsNotDebugModeGuard isNotDebugModeGuard;
+
+ @Autowired
+ CanCloseDebugModeGuard canCloseDebugModeGuard;
+
+ @Autowired
+ IsRebootCompletedGuard isRebootCompletedGuard;
+
+ @Bean(name = "airportStateMachineFactory")
+ public StateMachineFactory stateMachineFactory() throws Exception {
+ return new StateMachineFactory() {
+ @Override
+ public StateMachine getStateMachine() {
+ return null;
+ }
+
+ @Override
+ public StateMachine getStateMachine(String machineId) {
+ try {
+ StateMachineBuilder.Builder builder = StateMachineBuilder.builder();
+ configureStateMachine(builder);
+ configureStates(builder);
+ configureTransitions(builder);
+
+ StateMachine stateMachine = builder.build();
+ stateMachine.getExtendedState().getVariables().put("machineId", machineId);
+ return stateMachine;
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create state machine", e);
+ }
+ }
+
+ @Override
+ public StateMachine getStateMachine(UUID uuid) {
+ return null;
+ }
+ };
+ }
+
+ private void configureStateMachine(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureConfiguration()
+ .withConfiguration()
+ .autoStartup(true)
+ .listener(airPortStatusListener);
+ }
+
+ private void configureStates(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureStates()
+ .withStates()
+ .initial(AirportState.OFFLINE)
+ .states(EnumSet.of(
+ AirportState.OFFLINE,
+ AirportState.ONLINE,
+ AirportState.REBOOTING
+ ))
+ .and()
+ .withStates()
+ .parent(AirportState.ONLINE)
+ .initial(AirportState.STANDBY)
+ .states(EnumSet.of(
+ AirportState.STANDBY,
+ AirportState.DEBUG_MODE
+ ));
+ }
+
+ private void configureTransitions(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureTransitions()
+ // OFFLINE -> ONLINE(STANDBY)
+ .withExternal()
+ .source(AirportState.OFFLINE)
+ .target(AirportState.ONLINE)
+ .event(AirportEvent.AIRPORT_ONLINE)
+ .action(onlineAction)
+ .guard(canOnlineGuard)
+ .and()
+
+ // ONLINE -> OFFLINE
+ .withExternal()
+ .source(AirportState.ONLINE)
+ .target(AirportState.OFFLINE)
+ .event(AirportEvent.AIRPORT_OFFLINE)
+ .action(offlineAction)
+ .guard(canOfflineGuard)
+ .and()
+
+ // STANDBY -> DEBUG_MODE
+ .withExternal()
+ .source(AirportState.STANDBY)
+ .target(AirportState.DEBUG_MODE)
+ .event(AirportEvent.DEBUG_MODE_OPEN)
+ .action(openDebugModeAction)
+ .guard(isNotDebugModeGuard)
+ .and()
+
+ // DEBUG_MODE -> STANDBY
+ .withExternal()
+ .source(AirportState.DEBUG_MODE)
+ .target(AirportState.STANDBY)
+ .event(AirportEvent.DEBUG_MODE_CLOSE)
+ .action(closeDebugModeAction)
+ .guard(canCloseDebugModeGuard)
+ .and()
+
+ // DEBUG_MODE -> REBOOTING
+ .withExternal()
+ .source(AirportState.DEBUG_MODE)
+ .target(AirportState.REBOOTING)
+ .event(AirportEvent.AIRPORT_REBOOT)
+ .action(rebootAction)
+ .guard(isDebugModeGuard)
+ .and()
+
+ // REBOOTING -> ONLINE(STANDBY)
+ .withExternal()
+ .source(AirportState.REBOOTING)
+ .target(AirportState.ONLINE)
+ .event(AirportEvent.REBOOT_COMPLETED)
+ .action(rebootCompletedAction)
+ .guard(isRebootCompletedGuard);
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/config/CoverMachineConfig.java b/src/main/java/com/tuoheng/status/airport/config/CoverMachineConfig.java
new file mode 100644
index 0000000..b0d86a1
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/config/CoverMachineConfig.java
@@ -0,0 +1,113 @@
+package com.tuoheng.status.airport.config;
+
+import com.tuoheng.status.airport.events.CoverEvent;
+import com.tuoheng.status.airport.status.CoverState;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.config.StateMachineBuilder;
+import org.springframework.statemachine.config.StateMachineFactory;
+
+import java.util.EnumSet;
+import java.util.UUID;
+
+/**
+ * 舱门状态机配置
+ */
+@Configuration
+public class CoverMachineConfig {
+
+ @Bean
+ public StateMachineFactory coverStateMachineFactory() throws Exception {
+ return new StateMachineFactory() {
+ @Override
+ public StateMachine getStateMachine() {
+ return null;
+ }
+
+ @Override
+ public StateMachine getStateMachine(String machineId) {
+ try {
+ StateMachineBuilder.Builder builder = StateMachineBuilder.builder();
+ configureCoverStateMachine(builder);
+ configureCoverStates(builder);
+ configureCoverTransitions(builder);
+
+ StateMachine stateMachine = builder.build();
+ stateMachine.getExtendedState().getVariables().put("machineId", machineId);
+ return stateMachine;
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create cover state machine", e);
+ }
+ }
+
+ @Override
+ public StateMachine getStateMachine(UUID uuid) {
+ return null;
+ }
+ };
+ }
+
+ private void configureCoverStateMachine(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureConfiguration()
+ .withConfiguration()
+ .autoStartup(true);
+ }
+
+ private void configureCoverStates(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureStates()
+ .withStates()
+ .initial(CoverState.CLOSED)
+ .states(EnumSet.allOf(CoverState.class));
+ }
+
+ private void configureCoverTransitions(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureTransitions()
+ // CLOSED -> OPENING
+ .withExternal()
+ .source(CoverState.CLOSED)
+ .target(CoverState.OPENING)
+ .event(CoverEvent.OPEN)
+ .and()
+
+ // OPENING -> OPENED
+ .withExternal()
+ .source(CoverState.OPENING)
+ .target(CoverState.OPENED)
+ .event(CoverEvent.OPENED)
+ .and()
+
+ // OPENED -> CLOSING
+ .withExternal()
+ .source(CoverState.OPENED)
+ .target(CoverState.CLOSING)
+ .event(CoverEvent.CLOSE)
+ .and()
+
+ // CLOSING -> CLOSED
+ .withExternal()
+ .source(CoverState.CLOSING)
+ .target(CoverState.CLOSED)
+ .event(CoverEvent.CLOSED)
+ .and()
+
+ // ERROR handling
+ .withExternal()
+ .source(CoverState.OPENING)
+ .target(CoverState.ERROR)
+ .event(CoverEvent.ERROR)
+ .and()
+
+ .withExternal()
+ .source(CoverState.CLOSING)
+ .target(CoverState.ERROR)
+ .event(CoverEvent.ERROR)
+ .and()
+
+ // RESET from ERROR
+ .withExternal()
+ .source(CoverState.ERROR)
+ .target(CoverState.CLOSED)
+ .event(CoverEvent.RESET);
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/demo/AirportSystemDemo.java b/src/main/java/com/tuoheng/status/airport/demo/AirportSystemDemo.java
new file mode 100644
index 0000000..09d0a58
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/demo/AirportSystemDemo.java
@@ -0,0 +1,128 @@
+package com.tuoheng.status.airport.demo;
+
+import com.tuoheng.status.airport.manager.AirportSystemManager;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/**
+ * 机巢系统演示(组合状态机方案)
+ */
+public class AirportSystemDemo {
+
+ public static void main(String[] args) throws InterruptedException {
+
+ // 创建 Spring 上下文
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+
+ // 扫描配置类、服务类和监听器类
+ context.scan("com.tuoheng.status.airport");
+ context.refresh();
+
+ // 获取 AirportSystemManager
+ AirportSystemManager systemManager = context.getBean(AirportSystemManager.class);
+
+ System.out.println("\n========== 机巢系统演示开始(组合状态机方案) ==========\n");
+
+ String airportSn = "airport-001";
+
+ // ==================== 场景1: 机巢上线 ====================
+ System.out.println("【场景1】机巢上线");
+ System.out.println("----------------------------------------");
+ systemManager.airportOnline(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ // ==================== 场景2: 开舱流程 ====================
+ System.out.println("【场景2】开舱流程");
+ System.out.println("----------------------------------------");
+
+ System.out.println("1. 开启调试模式");
+ systemManager.openDebugMode(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ System.out.println("2. 开舱");
+ systemManager.openCover(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ System.out.println("3. 舱门打开完成");
+ systemManager.coverOpened(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ // ==================== 场景3: 关舱流程 ====================
+ System.out.println("【场景3】关舱流程");
+ System.out.println("----------------------------------------");
+
+ System.out.println("4. 关舱");
+ systemManager.closeCover(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ System.out.println("5. 舱门关闭完成");
+ systemManager.coverClosed(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ System.out.println("6. 关闭调试模式");
+ systemManager.closeDebugMode(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ // ==================== 场景4: 状态查询 ====================
+ System.out.println("【场景4】状态查询");
+ System.out.println("----------------------------------------");
+ System.out.println("机巢状态: " + systemManager.getAirportState(airportSn));
+ System.out.println("舱门状态: " + systemManager.getCoverState(airportSn));
+ System.out.println("完整状态: " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+
+ // ==================== 场景5: 机巢重启 ====================
+ System.out.println("【场景5】机巢重启");
+ System.out.println("----------------------------------------");
+
+ System.out.println("7. 开启调试模式");
+ systemManager.openDebugMode(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ System.out.println("8. 机巢重启");
+ systemManager.rebootAirport(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+ Thread.sleep(500);
+
+ System.out.println("9. 重启完成");
+ systemManager.rebootCompleted(airportSn);
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+
+ // ==================== 场景6: 错误处理 ====================
+ System.out.println("【场景6】错误处理(Guard拦截)");
+ System.out.println("----------------------------------------");
+
+ System.out.println("10. 尝试在待机状态下开舱(应该失败)");
+ boolean result = systemManager.openCover(airportSn);
+ System.out.println(" 操作结果: " + (result ? "成功" : "失败"));
+ System.out.println(" " + systemManager.getFullStatus(airportSn));
+ System.out.println();
+
+ System.out.println("\n========== 机巢系统演示结束 ==========\n");
+
+ System.out.println("✅ 组合状态机方案优势:");
+ System.out.println(" 1. 机巢状态和舱门状态独立管理");
+ System.out.println(" 2. 状态层级扁平,易于理解");
+ System.out.println(" 3. 易于扩展(可添加推杆、无人机等状态机)");
+ System.out.println(" 4. 职责清晰,维护方便");
+
+ context.close();
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/events/AirportEvent.java b/src/main/java/com/tuoheng/status/airport/events/AirportEvent.java
new file mode 100644
index 0000000..77108f0
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/events/AirportEvent.java
@@ -0,0 +1,47 @@
+package com.tuoheng.status.airport.events;
+
+/**
+ * 机巢事件枚举
+ * 定义机巢状态机中的所有事件
+ */
+public enum AirportEvent {
+
+ // ==================== 机巢基本事件 ====================
+ /**
+ * 机巢上线事件
+ * 触发源: OSD心跳
+ */
+ AIRPORT_ONLINE,
+
+ /**
+ * 机巢离线事件
+ * 触发源: OSD心跳
+ */
+ AIRPORT_OFFLINE,
+
+ // ==================== 调试模式事件 ====================
+ /**
+ * 开启调试模式
+ * 触发源: 用户指令
+ */
+ DEBUG_MODE_OPEN,
+
+ /**
+ * 关闭调试模式
+ * 触发源: 用户指令/自动
+ */
+ DEBUG_MODE_CLOSE,
+
+ // ==================== 机巢重启事件 ====================
+ /**
+ * 机巢重启指令
+ * 触发源: 用户指令
+ */
+ AIRPORT_REBOOT,
+
+ /**
+ * 重启完成
+ * 触发源: Events事件
+ */
+ REBOOT_COMPLETED
+}
diff --git a/src/main/java/com/tuoheng/status/airport/events/CoverEvent.java b/src/main/java/com/tuoheng/status/airport/events/CoverEvent.java
new file mode 100644
index 0000000..f06ae13
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/events/CoverEvent.java
@@ -0,0 +1,36 @@
+package com.tuoheng.status.airport.events;
+
+/**
+ * 舱门事件枚举
+ */
+public enum CoverEvent {
+ /**
+ * 开舱指令
+ */
+ OPEN,
+
+ /**
+ * 开舱完成
+ */
+ OPENED,
+
+ /**
+ * 关舱指令
+ */
+ CLOSE,
+
+ /**
+ * 关舱完成
+ */
+ CLOSED,
+
+ /**
+ * 舱门异常
+ */
+ ERROR,
+
+ /**
+ * 重置舱门状态
+ */
+ RESET
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/airport/CanOfflineGuard.java b/src/main/java/com/tuoheng/status/airport/guard/airport/CanOfflineGuard.java
new file mode 100644
index 0000000..bbb0ef8
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/airport/CanOfflineGuard.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.guard.airport;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查机巢是否可以离线
+ */
+@Component
+public class CanOfflineGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查机巢是否可以安全离线(例如:无人机已降落、舱门已关闭等)
+ System.out.println("检查机巢是否可以离线");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/airport/CanOnlineGuard.java b/src/main/java/com/tuoheng/status/airport/guard/airport/CanOnlineGuard.java
new file mode 100644
index 0000000..2c35ac9
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/airport/CanOnlineGuard.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.guard.airport;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查机巢是否可以上线
+ */
+@Component
+public class CanOnlineGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查机巢是否可以上线(例如:设备状态正常、网络连接正常等)
+ System.out.println("检查机巢是否可以上线");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/airport/IsAirportOnlineGuard.java b/src/main/java/com/tuoheng/status/airport/guard/airport/IsAirportOnlineGuard.java
new file mode 100644
index 0000000..fd260f5
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/airport/IsAirportOnlineGuard.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.guard.airport;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查机巢是否在线
+ */
+@Component
+public class IsAirportOnlineGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查机巢是否在线
+ System.out.println("检查机巢是否在线");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/cover/CanCloseCoverGuard.java b/src/main/java/com/tuoheng/status/airport/guard/cover/CanCloseCoverGuard.java
new file mode 100644
index 0000000..43d200b
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/cover/CanCloseCoverGuard.java
@@ -0,0 +1,22 @@
+package com.tuoheng.status.airport.guard.cover;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查是否可以关舱
+ * 条件:舱门状态不是已关闭
+ */
+@Component
+public class CanCloseCoverGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查当前舱门状态不是已关闭
+ System.out.println("检查是否可以关舱");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/cover/CanOpenCoverGuard.java b/src/main/java/com/tuoheng/status/airport/guard/cover/CanOpenCoverGuard.java
new file mode 100644
index 0000000..0245630
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/cover/CanOpenCoverGuard.java
@@ -0,0 +1,22 @@
+package com.tuoheng.status.airport.guard.cover;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查是否可以开舱
+ * 条件:舱门状态不是已打开
+ */
+@Component
+public class CanOpenCoverGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查当前舱门状态不是已打开
+ System.out.println("检查是否可以开舱");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/cover/IsCoverClosedGuard.java b/src/main/java/com/tuoheng/status/airport/guard/cover/IsCoverClosedGuard.java
new file mode 100644
index 0000000..4d32dbb
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/cover/IsCoverClosedGuard.java
@@ -0,0 +1,22 @@
+package com.tuoheng.status.airport.guard.cover;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查舱门是否已完全关闭
+ * 用于验证 COVER_CLOSED 事件的有效性
+ */
+@Component
+public class IsCoverClosedGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查舱门是否已完全关闭(通过OSD状态确认)
+ System.out.println("检查舱门是否已完全关闭");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/cover/IsCoverOpenedGuard.java b/src/main/java/com/tuoheng/status/airport/guard/cover/IsCoverOpenedGuard.java
new file mode 100644
index 0000000..ddac6f2
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/cover/IsCoverOpenedGuard.java
@@ -0,0 +1,22 @@
+package com.tuoheng.status.airport.guard.cover;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查舱门是否已完全打开
+ * 用于验证 COVER_OPENED 事件的有效性
+ */
+@Component
+public class IsCoverOpenedGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查舱门是否已完全打开(通过OSD状态确认)
+ System.out.println("检查舱门是否已完全打开");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/debug/CanCloseDebugModeGuard.java b/src/main/java/com/tuoheng/status/airport/guard/debug/CanCloseDebugModeGuard.java
new file mode 100644
index 0000000..ff0faa2
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/debug/CanCloseDebugModeGuard.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.guard.debug;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查是否可以关闭调试模式
+ */
+@Component
+public class CanCloseDebugModeGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查是否可以关闭调试模式(例如:没有正在进行的操作)
+ System.out.println("检查是否可以关闭调试模式");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/debug/IsDebugModeGuard.java b/src/main/java/com/tuoheng/status/airport/guard/debug/IsDebugModeGuard.java
new file mode 100644
index 0000000..7b897c3
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/debug/IsDebugModeGuard.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.guard.debug;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查是否处于调试模式
+ */
+@Component
+public class IsDebugModeGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查当前是否处于调试模式
+ System.out.println("检查是否处于调试模式");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/debug/IsNotDebugModeGuard.java b/src/main/java/com/tuoheng/status/airport/guard/debug/IsNotDebugModeGuard.java
new file mode 100644
index 0000000..74426c7
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/debug/IsNotDebugModeGuard.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.guard.debug;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查是否不处于调试模式
+ */
+@Component
+public class IsNotDebugModeGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查当前不处于调试模式
+ System.out.println("检查是否不处于调试模式");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/guard/reboot/IsRebootCompletedGuard.java b/src/main/java/com/tuoheng/status/airport/guard/reboot/IsRebootCompletedGuard.java
new file mode 100644
index 0000000..2e5c414
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/guard/reboot/IsRebootCompletedGuard.java
@@ -0,0 +1,21 @@
+package com.tuoheng.status.airport.guard.reboot;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+import org.springframework.stereotype.Component;
+
+/**
+ * 检查机巢重启是否完成
+ */
+@Component
+public class IsRebootCompletedGuard implements Guard {
+
+ @Override
+ public boolean evaluate(StateContext stateContext) {
+ // TODO: 检查机巢重启是否完成(通过Events事件确认)
+ System.out.println("检查机巢重启是否完成");
+ return true;
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/listener/AirPortStatusListener.java b/src/main/java/com/tuoheng/status/airport/listener/AirPortStatusListener.java
new file mode 100644
index 0000000..4cff578
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/listener/AirPortStatusListener.java
@@ -0,0 +1,77 @@
+package com.tuoheng.status.airport.listener;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import com.tuoheng.status.statemachine.events.Event;
+import com.tuoheng.status.statemachine.status.Status;
+import org.springframework.messaging.Message;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.listener.StateMachineListener;
+import org.springframework.statemachine.state.State;
+import org.springframework.statemachine.transition.Transition;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AirPortStatusListener implements StateMachineListener {
+
+ @Override
+ public void stateChanged(State state, State state1) {
+
+ }
+
+ @Override
+ public void stateEntered(State state) {
+
+ }
+
+ @Override
+ public void stateExited(State state) {
+
+ }
+
+ @Override
+ public void eventNotAccepted(Message message) {
+
+ }
+
+ @Override
+ public void transition(Transition transition) {
+
+ }
+
+ @Override
+ public void transitionStarted(Transition transition) {
+
+ }
+
+ @Override
+ public void transitionEnded(Transition transition) {
+
+ }
+
+ @Override
+ public void stateMachineStarted(StateMachine stateMachine) {
+
+ }
+
+ @Override
+ public void stateMachineStopped(StateMachine stateMachine) {
+
+ }
+
+ @Override
+ public void stateMachineError(StateMachine stateMachine, Exception e) {
+
+ }
+
+ @Override
+ public void extendedStateChanged(Object o, Object o1) {
+
+ }
+
+ @Override
+ public void stateContext(StateContext stateContext) {
+
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/manager/AirportSystemManager.java b/src/main/java/com/tuoheng/status/airport/manager/AirportSystemManager.java
new file mode 100644
index 0000000..07cd1a1
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/manager/AirportSystemManager.java
@@ -0,0 +1,154 @@
+package com.tuoheng.status.airport.manager;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.events.CoverEvent;
+import com.tuoheng.status.airport.service.AirportMachineService;
+import com.tuoheng.status.airport.service.CoverMachineService;
+import com.tuoheng.status.airport.status.AirportState;
+import com.tuoheng.status.airport.status.CoverState;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 机巢系统管理器
+ * 协调机巢状态机和舱门状态机
+ */
+@Component
+public class AirportSystemManager {
+
+ @Autowired
+ private AirportMachineService airportManager;
+
+ @Autowired
+ private CoverMachineService coverManager;
+
+ /**
+ * 机巢上线
+ */
+ public boolean airportOnline(String airportSn) {
+ return airportManager.sendEvent(airportSn, AirportEvent.AIRPORT_ONLINE);
+ }
+
+ /**
+ * 机巢离线
+ */
+ public boolean airportOffline(String airportSn) {
+ return airportManager.sendEvent(airportSn, AirportEvent.AIRPORT_OFFLINE);
+ }
+
+ /**
+ * 开启调试模式
+ */
+ public boolean openDebugMode(String airportSn) {
+ if (!airportManager.isInState(airportSn, AirportState.ONLINE)) {
+ System.out.println("机巢未在线,无法开启调试模式");
+ return false;
+ }
+ return airportManager.sendEvent(airportSn, AirportEvent.DEBUG_MODE_OPEN);
+ }
+
+ /**
+ * 关闭调试模式
+ */
+ public boolean closeDebugMode(String airportSn) {
+ return airportManager.sendEvent(airportSn, AirportEvent.DEBUG_MODE_CLOSE);
+ }
+
+ /**
+ * 开舱
+ */
+ public boolean openCover(String airportSn) {
+ // 检查机巢是否在调试模式
+ if (!airportManager.isInState(airportSn, AirportState.DEBUG_MODE)) {
+ System.out.println("必须在调试模式下才能开舱");
+ return false;
+ }
+
+ // 检查舱门是否已经打开
+ if (coverManager.isInState(airportSn, CoverState.OPENED)) {
+ System.out.println("舱门已经打开");
+ return false;
+ }
+
+ // 发送开舱指令到舱门状态机
+ return coverManager.sendEvent(airportSn, CoverEvent.OPEN);
+ }
+
+ /**
+ * 舱门打开完成
+ */
+ public boolean coverOpened(String airportSn) {
+ return coverManager.sendEvent(airportSn, CoverEvent.OPENED);
+ }
+
+ /**
+ * 关舱
+ */
+ public boolean closeCover(String airportSn) {
+ // 检查机巢是否在调试模式
+ if (!airportManager.isInState(airportSn, AirportState.DEBUG_MODE)) {
+ System.out.println("必须在调试模式下才能关舱");
+ return false;
+ }
+
+ // 检查舱门是否已经关闭
+ if (coverManager.isInState(airportSn, CoverState.CLOSED)) {
+ System.out.println("舱门已经关闭");
+ return false;
+ }
+
+ // 发送关舱指令到舱门状态机
+ return coverManager.sendEvent(airportSn, CoverEvent.CLOSE);
+ }
+
+ /**
+ * 舱门关闭完成
+ */
+ public boolean coverClosed(String airportSn) {
+ return coverManager.sendEvent(airportSn, CoverEvent.CLOSED);
+ }
+
+ /**
+ * 机巢重启
+ */
+ public boolean rebootAirport(String airportSn) {
+ if (!airportManager.isInState(airportSn, AirportState.DEBUG_MODE)) {
+ System.out.println("必须在调试模式下才能重启");
+ return false;
+ }
+ return airportManager.sendEvent(airportSn, AirportEvent.AIRPORT_REBOOT);
+ }
+
+ /**
+ * 重启完成
+ */
+ public boolean rebootCompleted(String airportSn) {
+ return airportManager.sendEvent(airportSn, AirportEvent.REBOOT_COMPLETED);
+ }
+
+ /**
+ * 获取机巢状态
+ */
+ public AirportState getAirportState(String airportSn) {
+ return airportManager.getCurrentState(airportSn);
+ }
+
+ /**
+ * 获取舱门状态
+ */
+ public CoverState getCoverState(String airportSn) {
+ return coverManager.getCurrentState(airportSn);
+ }
+
+ /**
+ * 获取完整状态信息
+ */
+ public String getFullStatus(String airportSn) {
+ AirportState airportState = getAirportState(airportSn);
+ CoverState coverState = getCoverState(airportSn);
+
+ return String.format("机巢状态: %s, 舱门状态: %s",
+ airportState != null ? airportState : "未知",
+ coverState != null ? coverState : "未知");
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/service/AirportMachineService.java b/src/main/java/com/tuoheng/status/airport/service/AirportMachineService.java
new file mode 100644
index 0000000..68bc908
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/service/AirportMachineService.java
@@ -0,0 +1,182 @@
+package com.tuoheng.status.airport.service;
+
+import com.tuoheng.status.airport.events.AirportEvent;
+import com.tuoheng.status.airport.status.AirportState;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.config.StateMachineFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 机巢状态机管理器
+ * 负责管理多个机巢的状态机实例
+ */
+@Component
+public class AirportMachineService {
+
+ @Autowired
+ StateMachineFactory stateMachineFactory;
+
+ /**
+ * 存储所有机巢的状态机实例
+ * Key: 机巢ID (airportSn)
+ * Value: 状态机实例
+ */
+ private final Map> stateMachineMap = new ConcurrentHashMap<>();
+
+ /**
+ * 获取或创建状态机
+ * 如果状态机不存在,则创建新的状态机实例
+ *
+ * @param airportSn 机巢序列号
+ * @return 状态机实例
+ */
+ public StateMachine getOrCreateStateMachine(String airportSn) {
+ return stateMachineMap.computeIfAbsent(airportSn, id -> {
+ StateMachine stateMachine = stateMachineFactory.getStateMachine(id);
+ stateMachine.start();
+ System.out.println("创建并启动状态机: " + id);
+ return stateMachine;
+ });
+ }
+
+ /**
+ * 获取状态机(不创建)
+ *
+ * @param airportSn 机巢序列号
+ * @return 状态机实例,如果不存在返回null
+ */
+ public StateMachine getStateMachine(String airportSn) {
+ return stateMachineMap.get(airportSn);
+ }
+
+ /**
+ * 获取状态机的当前状态
+ *
+ * @param airportSn 机巢序列号
+ * @return 当前状态,如果状态机不存在返回null
+ */
+ public AirportState getCurrentState(String airportSn) {
+ StateMachine stateMachine = stateMachineMap.get(airportSn);
+ if (stateMachine == null) {
+ System.out.println("状态机不存在: " + airportSn);
+ return null;
+ }
+ return stateMachine.getState().getId();
+ }
+
+ /**
+ * 获取状态机的所有当前状态(包括子状态)
+ *
+ * @param airportSn 机巢序列号
+ * @return 当前状态集合的字符串表示
+ */
+ public String getCurrentStates(String airportSn) {
+ StateMachine stateMachine = stateMachineMap.get(airportSn);
+ if (stateMachine == null) {
+ return "状态机不存在";
+ }
+
+ StringBuilder states = new StringBuilder();
+ stateMachine.getState().getIds().forEach(state -> {
+ if (states.length() > 0) {
+ states.append(" -> ");
+ }
+ states.append(state);
+ });
+
+ return states.toString();
+ }
+
+ /**
+ * 发送事件到状态机
+ *
+ * @param airportSn 机巢序列号
+ * @param event 事件
+ * @return 是否发送成功
+ */
+ public boolean sendEvent(String airportSn, AirportEvent event) {
+ StateMachine stateMachine = getOrCreateStateMachine(airportSn);
+ boolean result = stateMachine.sendEvent(event);
+
+ if (result) {
+ System.out.println(String.format("事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
+ airportSn, event, getCurrentStates(airportSn)));
+ } else {
+ System.out.println(String.format("事件发送失败 - 机巢: %s, 事件: %s, 当前状态: %s",
+ airportSn, event, getCurrentStates(airportSn)));
+ }
+
+ return result;
+ }
+
+ /**
+ * 移除状态机
+ *
+ * @param airportSn 机巢序列号
+ */
+ public void removeStateMachine(String airportSn) {
+ StateMachine stateMachine = stateMachineMap.remove(airportSn);
+ if (stateMachine != null) {
+ stateMachine.stop();
+ System.out.println("停止并移除状态机: " + airportSn);
+ }
+ }
+
+ /**
+ * 检查状态机是否存在
+ *
+ * @param airportSn 机巢序列号
+ * @return 是否存在
+ */
+ public boolean hasStateMachine(String airportSn) {
+ return stateMachineMap.containsKey(airportSn);
+ }
+
+ /**
+ * 获取所有状态机的数量
+ *
+ * @return 状态机数量
+ */
+ public int getStateMachineCount() {
+ return stateMachineMap.size();
+ }
+
+ /**
+ * 获取所有机巢ID
+ *
+ * @return 机巢ID集合
+ */
+ public java.util.Set getAllAirportIds() {
+ return stateMachineMap.keySet();
+ }
+
+ /**
+ * 检查状态机是否处于指定状态
+ *
+ * @param airportSn 机巢序列号
+ * @param state 要检查的状态
+ * @return 是否处于指定状态
+ */
+ public boolean isInState(String airportSn, AirportState state) {
+ StateMachine stateMachine = stateMachineMap.get(airportSn);
+ if (stateMachine == null) {
+ return false;
+ }
+ return stateMachine.getState().getIds().contains(state);
+ }
+
+ /**
+ * 重启状态机
+ *
+ * @param airportSn 机巢序列号
+ */
+ public void restartStateMachine(String airportSn) {
+ removeStateMachine(airportSn);
+ getOrCreateStateMachine(airportSn);
+ System.out.println("重启状态机: " + airportSn);
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/service/CoverMachineService.java b/src/main/java/com/tuoheng/status/airport/service/CoverMachineService.java
new file mode 100644
index 0000000..bc11166
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/service/CoverMachineService.java
@@ -0,0 +1,71 @@
+package com.tuoheng.status.airport.service;
+
+import com.tuoheng.status.airport.events.CoverEvent;
+import com.tuoheng.status.airport.status.CoverState;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.config.StateMachineFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 舱门状态机管理器
+ */
+@Component
+public class CoverMachineService {
+
+ @Autowired
+ StateMachineFactory coverStateMachineFactory;
+
+ private final Map> stateMachineMap = new ConcurrentHashMap<>();
+
+ public StateMachine getOrCreateStateMachine(String airportSn) {
+ return stateMachineMap.computeIfAbsent(airportSn, id -> {
+ StateMachine stateMachine = coverStateMachineFactory.getStateMachine(id);
+ stateMachine.start();
+ System.out.println("创建并启动舱门状态机: " + id);
+ return stateMachine;
+ });
+ }
+
+ public CoverState getCurrentState(String airportSn) {
+ StateMachine stateMachine = stateMachineMap.get(airportSn);
+ if (stateMachine == null) {
+ return null;
+ }
+ return stateMachine.getState().getId();
+ }
+
+ public boolean sendEvent(String airportSn, CoverEvent event) {
+ StateMachine stateMachine = getOrCreateStateMachine(airportSn);
+ boolean result = stateMachine.sendEvent(event);
+
+ if (result) {
+ System.out.println(String.format("舱门事件发送成功 - 机巢: %s, 事件: %s, 当前状态: %s",
+ airportSn, event, getCurrentState(airportSn)));
+ } else {
+ System.out.println(String.format("舱门事件发送失败 - 机巢: %s, 事件: %s, 当前状态: %s",
+ airportSn, event, getCurrentState(airportSn)));
+ }
+
+ return result;
+ }
+
+ public boolean isInState(String airportSn, CoverState state) {
+ StateMachine stateMachine = stateMachineMap.get(airportSn);
+ if (stateMachine == null) {
+ return false;
+ }
+ return stateMachine.getState().getId() == state;
+ }
+
+ public void removeStateMachine(String airportSn) {
+ StateMachine stateMachine = stateMachineMap.remove(airportSn);
+ if (stateMachine != null) {
+ stateMachine.stop();
+ System.out.println("停止并移除舱门状态机: " + airportSn);
+ }
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/airport/status/AirportState.java b/src/main/java/com/tuoheng/status/airport/status/AirportState.java
new file mode 100644
index 0000000..4b5b868
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/status/AirportState.java
@@ -0,0 +1,31 @@
+package com.tuoheng.status.airport.status;
+
+/**
+ * 机巢状态枚举(简化版 - 舱门状态已分离)
+ */
+public enum AirportState {
+ /**
+ * 离线
+ */
+ OFFLINE,
+
+ /**
+ * 在线(父状态)
+ */
+ ONLINE,
+
+ /**
+ * 待机
+ */
+ STANDBY,
+
+ /**
+ * 调试模式
+ */
+ DEBUG_MODE,
+
+ /**
+ * 重启中
+ */
+ REBOOTING
+}
diff --git a/src/main/java/com/tuoheng/status/airport/status/CoverState.java b/src/main/java/com/tuoheng/status/airport/status/CoverState.java
new file mode 100644
index 0000000..1050513
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/status/CoverState.java
@@ -0,0 +1,36 @@
+package com.tuoheng.status.airport.status;
+
+/**
+ * 舱门状态枚举
+ */
+public enum CoverState {
+ /**
+ * 舱门已关闭
+ */
+ CLOSED,
+
+ /**
+ * 舱门打开中
+ */
+ OPENING,
+
+ /**
+ * 舱门已打开
+ */
+ OPENED,
+
+ /**
+ * 舱门关闭中
+ */
+ CLOSING,
+
+ /**
+ * 舱门半开
+ */
+ HALF_OPEN,
+
+ /**
+ * 舱门状态异常
+ */
+ ERROR
+}
diff --git a/src/main/java/com/tuoheng/status/airport/status/DroneState.java b/src/main/java/com/tuoheng/status/airport/status/DroneState.java
new file mode 100644
index 0000000..a357e9d
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/airport/status/DroneState.java
@@ -0,0 +1,4 @@
+package com.tuoheng.status.airport.status;
+
+public enum DroneState {
+}
diff --git a/src/main/java/com/tuoheng/status/statemachine/README.md b/src/main/java/com/tuoheng/status/statemachine/README.md
new file mode 100644
index 0000000..1f8838a
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/README.md
@@ -0,0 +1,506 @@
+# Spring StateMachine 使用文档
+
+本文档详细介绍了如何在项目中使用 Spring StateMachine Core 来构建状态机,包括子状态、Action、Guard、事件监听器等核心功能。
+
+## 目录
+
+- [项目结构](#项目结构)
+- [核心概念](#核心概念)
+- [状态机配置](#状态机配置)
+- [使用示例](#使用示例)
+- [运行演示](#运行演示)
+- [常见问题](#常见问题)
+
+## 项目结构
+
+```
+com.tuoheng.status.statemachine/
+├── status/
+│ └── Status.java # 状态枚举(包含主状态和子状态)
+├── events/
+│ └── Event.java # 事件枚举
+├── action/
+│ └── TaskAction.java # Action 实现类
+├── guard/
+│ └── TaskGuard.java # Guard 实现类
+├── listener/
+│ └── TaskStateMachineListener.java # 状态机事件监听器
+├── config/
+│ └── TaskStateMachineConfig.java # 状态机配置类
+├── service/
+│ └── TaskStateMachineService.java # 服务类(使用示例)
+└── demo/
+ └── StateMachineDemo.java # 演示主类
+```
+
+## 核心概念
+
+### 1. 状态(State)
+
+状态定义了状态机可能处于的各种情况。本示例包含:
+
+**主状态:**
+- `IDLE` - 空闲状态(初始状态)
+- `PROCESSING` - 处理中(父状态,包含子状态)
+- `COMPLETED` - 已完成
+- `FAILED` - 失败
+
+**子状态(PROCESSING 的子状态):**
+- `PREPARING` - 准备中
+- `EXECUTING` - 执行中
+- `VALIDATING` - 验证中
+
+### 2. 事件(Event)
+
+事件是触发状态转换的触发器:
+
+- `START` - 开始处理
+- `PREPARE` - 准备就绪
+- `EXECUTE` - 执行
+- `VALIDATE` - 验证
+- `COMPLETE` - 完成
+- `FAIL` - 失败
+- `RETRY` - 重试
+- `RESET` - 重置
+
+### 3. Action(动作)
+
+Action 在状态转换时执行,用于执行业务逻辑。Action 在转换**过程中**执行。
+
+**示例:**
+```java
+public static class StartAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ // 执行业务逻辑
+ logger.info("开始处理任务");
+ Object taskId = context.getExtendedState().getVariables().get("taskId");
+ // ...
+ }
+}
+```
+
+### 4. Guard(守卫)
+
+Guard 用于条件判断,决定是否允许状态转换。Guard 在转换**之前**执行,如果返回 `false`,转换将被阻止。
+
+**示例:**
+```java
+public static class CanStartGuard implements Guard {
+ @Override
+ public boolean evaluate(StateContext context) {
+ // 检查条件
+ Object taskId = context.getExtendedState().getVariables().get("taskId");
+ return taskId != null; // 只有 taskId 存在时才允许转换
+ }
+}
+```
+
+### 5. 事件监听器(Listener)
+
+监听器用于监听状态机的各种事件,如状态变化、转换等。
+
+**监听的事件类型:**
+- 状态机启动/停止
+- 状态进入/退出
+- 状态改变
+- 转换开始/结束
+- 事件未接受
+- 扩展状态改变
+
+## 状态机配置
+
+### 状态转换流程
+
+```
+IDLE
+ └─[START]─> PREPARING (子状态)
+ └─[PREPARE]─> EXECUTING (子状态)
+ └─[EXECUTE]─> VALIDATING (子状态)
+ └─[VALIDATE]─> COMPLETED
+ └─[RESET]─> IDLE
+
+PREPARING/EXECUTING/VALIDATING
+ └─[FAIL]─> FAILED
+ └─[RETRY]─> PREPARING (重试)
+ └─[RESET]─> IDLE (重置)
+```
+
+### 配置类说明
+
+`TaskStateMachineConfig` 类负责配置状态机:
+
+1. **配置状态和子状态:**
+```java
+builder.configureStates()
+ .withStates()
+ .initial(Status.IDLE) // 初始状态
+ .states(EnumSet.of(Status.IDLE, Status.COMPLETED, Status.FAILED))
+ .and()
+ .withStates()
+ .parent(Status.PROCESSING) // 设置父状态
+ .initial(Status.PREPARING) // 子状态的初始状态
+ .states(EnumSet.of(Status.PREPARING, Status.EXECUTING, Status.VALIDATING));
+```
+
+2. **配置转换:**
+```java
+builder.configureTransitions()
+ .withExternal()
+ .source(Status.IDLE)
+ .target(Status.PREPARING)
+ .event(Event.START)
+ .action(new TaskAction.StartAction()) // 添加 Action
+ .guard(new TaskGuard.CanStartGuard()) // 添加 Guard
+ .and()
+ // ... 更多转换配置
+```
+
+## 状态机管理器
+
+当需要管理多个状态机实例时,可以使用 `StateMachineManager` 来统一管理。
+
+### 核心功能
+
+- **通过ID获取状态机**:`getStateMachine(machineId)`
+- **查询当前状态**:`getCurrentStatus(machineId)`
+- **获取详细信息**:`getStateMachineInfo(machineId)`
+- **创建/获取状态机**:`getOrCreateStateMachine(machineId)`
+- **移除状态机**:`removeStateMachine(machineId)`
+
+### 使用示例
+
+```java
+@Autowired
+private StateMachineManager stateMachineManager;
+
+// 1. 创建或获取状态机
+StateMachine stateMachine =
+ stateMachineManager.getOrCreateStateMachine("machine-001");
+
+// 2. 通过ID获取状态机
+StateMachine sm =
+ stateMachineManager.getStateMachine("machine-001");
+
+// 3. 查询当前状态
+Status currentStatus = stateMachineManager.getCurrentStatus("machine-001");
+System.out.println("当前状态: " + currentStatus);
+
+// 4. 获取详细信息
+StateMachineManager.StateMachineInfo info =
+ stateMachineManager.getStateMachineInfo("machine-001");
+System.out.println("状态机ID: " + info.getMachineId());
+System.out.println("当前状态: " + info.getCurrentStatus());
+System.out.println("是否运行中: " + info.isRunning());
+System.out.println("扩展状态: " + info.getExtendedState());
+
+// 5. 发送事件
+stateMachine.sendEvent(Event.START);
+
+// 6. 移除状态机
+stateMachineManager.removeStateMachine("machine-001");
+```
+
+### 使用服务类(推荐)
+
+`StateMachineManagerService` 提供了更便捷的方法:
+
+```java
+@Autowired
+private StateMachineManagerService stateMachineManagerService;
+
+// 启动任务(自动创建状态机)
+stateMachineManagerService.startTask("machine-001", "task-001");
+
+// 发送事件
+stateMachineManagerService.sendEvent("machine-001", Event.PREPARE);
+
+// 查询状态
+Status status = stateMachineManagerService.getCurrentStatus("machine-001");
+
+// 获取详细信息
+StateMachineManager.StateMachineInfo info =
+ stateMachineManagerService.getStateMachineInfo("machine-001");
+
+// 打印状态机信息(调试用)
+stateMachineManagerService.printStateMachineInfo("machine-001");
+
+// 获取所有状态机ID
+Set allIds = stateMachineManagerService.getAllMachineIds();
+```
+
+### 管理多个状态机
+
+```java
+// 创建多个状态机
+stateMachineManager.getOrCreateStateMachine("machine-001");
+stateMachineManager.getOrCreateStateMachine("machine-002");
+stateMachineManager.getOrCreateStateMachine("machine-003");
+
+// 获取所有状态机ID
+Set allIds = stateMachineManager.getAllMachineIds();
+System.out.println("管理的状态机数量: " + allIds.size());
+
+// 为不同状态机设置不同状态
+stateMachineManager.getStateMachine("machine-001")
+ .getExtendedState().getVariables().put("taskId", "task-001");
+stateMachineManagerService.sendEvent("machine-001", Event.START);
+
+// 查询不同状态机的状态
+Status status1 = stateMachineManager.getCurrentStatus("machine-001");
+Status status2 = stateMachineManager.getCurrentStatus("machine-002");
+```
+
+## 使用示例
+
+### 1. 基本使用
+
+```java
+@Autowired
+private StateMachineFactory stateMachineFactory;
+
+public void processTask(String taskId) {
+ // 创建状态机实例
+ StateMachine stateMachine = stateMachineFactory.getStateMachine();
+
+ try {
+ // 设置扩展状态变量
+ stateMachine.getExtendedState().getVariables().put("taskId", taskId);
+
+ // 启动状态机
+ stateMachine.start();
+
+ // 发送事件触发转换
+ stateMachine.sendEvent(Event.START);
+
+ // 继续处理...
+ stateMachine.getExtendedState().getVariables().put("prepared", true);
+ stateMachine.sendEvent(Event.PREPARE);
+
+ } finally {
+ // 停止状态机
+ stateMachine.stop();
+ }
+}
+```
+
+### 2. 使用 Guard 控制转换
+
+```java
+// Guard 会在转换前检查条件
+public static class CanStartGuard implements Guard {
+ @Override
+ public boolean evaluate(StateContext context) {
+ Object taskId = context.getExtendedState().getVariables().get("taskId");
+ return taskId != null; // 只有满足条件才允许转换
+ }
+}
+
+// 使用:如果 taskId 不存在,START 事件将被拒绝
+stateMachine.sendEvent(Event.START); // Guard 检查失败,转换不会发生
+```
+
+### 3. 使用 Action 执行业务逻辑
+
+```java
+// Action 在转换时执行
+public static class ExecuteAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ // 执行业务逻辑
+ logger.info("执行任务");
+ // 可以访问和修改扩展状态
+ context.getExtendedState().getVariables().put("executionResult", "success");
+ }
+}
+```
+
+### 4. 监听状态机事件
+
+监听器会自动监听所有状态机事件:
+
+```java
+// 监听器会自动记录:
+// - 状态机启动/停止
+// - 状态进入/退出
+// - 状态改变
+// - 转换开始/结束
+// - 事件未接受
+```
+
+## 运行演示
+
+### 方式一:运行演示主类
+
+直接运行 `StateMachineDemo` 类的 `main` 方法:
+
+```bash
+# 在 IDE 中运行
+com.tuoheng.status.statemachine.demo.StateMachineDemo
+
+# 或使用 Maven
+mvn exec:java -Dexec.mainClass="com.tuoheng.status.statemachine.demo.StateMachineDemo"
+```
+
+演示内容包括:
+1. **正常流程**:IDLE → PREPARING → EXECUTING → VALIDATING → COMPLETED
+2. **失败和重试**:演示失败后的重试机制
+3. **Guard 条件不满足**:演示 Guard 如何阻止转换
+4. **重置流程**:演示如何重置状态机
+
+### 方式二:运行测试类
+
+运行 `TaskStateMachineTest` 测试类:
+
+```bash
+mvn test -Dtest=TaskStateMachineTest
+```
+
+### 方式三:在服务中使用
+
+```java
+@Service
+public class YourService {
+ @Autowired
+ private TaskStateMachineService taskStateMachineService;
+
+ public void handleTask(String taskId) {
+ // 使用预定义的服务方法
+ taskStateMachineService.startTask(taskId);
+ }
+}
+```
+
+## 扩展状态(Extended State)
+
+扩展状态用于存储状态机相关的变量,可以在 Action、Guard 和监听器中访问:
+
+```java
+// 设置变量
+stateMachine.getExtendedState().getVariables().put("taskId", "task-001");
+stateMachine.getExtendedState().getVariables().put("retryCount", 0);
+
+// 在 Action 或 Guard 中访问
+Object taskId = context.getExtendedState().getVariables().get("taskId");
+Integer retryCount = (Integer) context.getExtendedState().getVariables().getOrDefault("retryCount", 0);
+```
+
+## 常见问题
+
+### 1. Guard 返回 false,转换不执行
+
+**原因:** Guard 在转换前检查条件,如果返回 `false`,转换将被阻止。
+
+**解决:** 确保在发送事件前,扩展状态中的变量已正确设置。
+
+```java
+// 错误示例:Guard 检查失败
+stateMachine.sendEvent(Event.START); // taskId 未设置,Guard 返回 false
+
+// 正确示例:先设置变量
+stateMachine.getExtendedState().getVariables().put("taskId", "task-001");
+stateMachine.sendEvent(Event.START); // Guard 检查通过
+```
+
+### 2. 事件未接受
+
+**原因:** 当前状态不支持该事件,或没有匹配的转换配置。
+
+**解决:**
+- 检查状态机当前状态
+- 确认配置中是否有对应的转换
+- 检查 Guard 条件是否满足
+
+```java
+// 检查事件是否被接受
+boolean accepted = stateMachine.sendEvent(Event.START);
+if (!accepted) {
+ logger.warning("事件未被接受,当前状态: " + stateMachine.getState().getId());
+}
+```
+
+### 3. 子状态的使用
+
+子状态是父状态的内部状态,当进入父状态时,会自动进入子状态的初始状态。
+
+```java
+// 从 IDLE 转换到 PREPARING(PROCESSING 的子状态)
+stateMachine.sendEvent(Event.START);
+// 状态机现在处于 PROCESSING 父状态,具体是 PREPARING 子状态
+```
+
+### 4. 状态机实例管理
+
+每次调用 `getStateMachine()` 都会创建新的状态机实例。如果需要复用实例,需要自己管理:
+
+```java
+// 方式一:每次创建新实例(推荐用于独立任务)
+StateMachine sm1 = stateMachineFactory.getStateMachine();
+StateMachine sm2 = stateMachineFactory.getStateMachine();
+
+// 方式二:复用实例(需要自己管理生命周期)
+StateMachine sm = stateMachineFactory.getStateMachine();
+// ... 使用 sm
+sm.stop(); // 使用完后停止
+```
+
+### 5. 监听器中的空指针异常
+
+某些转换(如初始转换)可能没有 trigger,需要检查 null:
+
+```java
+@Override
+public void transitionStarted(Transition transition) {
+ if (transition.getTrigger() != null) {
+ logger.info("事件: " + transition.getTrigger().getEvent());
+ } else {
+ logger.info("事件: null (可能是初始转换)");
+ }
+}
+```
+
+### 6. 通过ID获取状态机
+
+使用 `StateMachineManager` 可以方便地管理多个状态机:
+
+```java
+@Autowired
+private StateMachineManager stateMachineManager;
+
+// 获取状态机
+StateMachine sm = stateMachineManager.getStateMachine("machine-id");
+
+// 如果不存在,创建新实例
+StateMachine sm = stateMachineManager.getOrCreateStateMachine("machine-id");
+
+// 查询当前状态
+Status status = stateMachineManager.getCurrentStatus("machine-id");
+
+// 获取详细信息
+StateMachineManager.StateMachineInfo info =
+ stateMachineManager.getStateMachineInfo("machine-id");
+```
+
+## 最佳实践
+
+1. **使用 Guard 进行条件检查**:在转换前验证条件,避免无效转换
+2. **使用 Action 执行业务逻辑**:将业务逻辑放在 Action 中,保持代码清晰
+3. **合理使用扩展状态**:存储任务相关的变量,便于在 Action 和 Guard 中使用
+4. **添加监听器**:监听状态机事件,便于调试和监控
+5. **管理状态机生命周期**:及时启动和停止状态机,避免资源泄漏
+6. **处理异常情况**:在 Guard 中检查条件,在 Action 中处理异常
+
+## 参考资源
+
+- [Spring StateMachine 官方文档](https://docs.spring.io/spring-statemachine/docs/current/reference/)
+- [Spring StateMachine API 文档](https://docs.spring.io/spring-statemachine/docs/current/api/)
+
+## 示例代码位置
+
+- **配置类**:`com.tuoheng.status.statemachine.config.TaskStateMachineConfig`
+- **状态机管理器**:`com.tuoheng.status.statemachine.manager.StateMachineManager`
+- **管理器服务类**:`com.tuoheng.status.statemachine.service.StateMachineManagerService`
+- **服务类**:`com.tuoheng.status.statemachine.service.TaskStateMachineService`
+- **演示类**:`com.tuoheng.status.statemachine.demo.StateMachineDemo`
+- **测试类**:`com.tuoheng.status.statemachine.TaskStateMachineTest`
+- **管理器测试类**:`com.tuoheng.status.statemachine.StateMachineManagerTest`
diff --git a/src/main/java/com/tuoheng/status/statemachine/action/TaskAction.java b/src/main/java/com/tuoheng/status/statemachine/action/TaskAction.java
new file mode 100644
index 0000000..15e21ed
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/action/TaskAction.java
@@ -0,0 +1,142 @@
+package com.tuoheng.status.statemachine.action;
+
+import com.tuoheng.status.statemachine.events.Event;
+import com.tuoheng.status.statemachine.status.Status;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.action.Action;
+
+import java.util.logging.Logger;
+
+/**
+ * 任务 Action 示例
+ * Action 在状态转换时执行,用于执行业务逻辑
+ */
+public class TaskAction {
+
+ private static final Logger logger = Logger.getLogger(TaskAction.class.getName());
+
+ /**
+ * 开始处理 Action
+ */
+ public static class StartAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ logger.info("=== Action: 开始处理任务 ===");
+ logger.info("从状态: " + context.getSource().getId());
+ logger.info("到状态: " + context.getTarget().getId());
+ logger.info("事件: " + context.getEvent());
+
+ // 可以在这里执行业务逻辑,比如初始化任务数据
+ Object taskId = context.getExtendedState().getVariables().get("taskId");
+ logger.info("任务ID: " + taskId);
+ }
+ }
+
+ /**
+ * 准备 Action
+ */
+ public static class PrepareAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ logger.info("=== Action: 准备任务资源 ===");
+ logger.info("当前状态: " + context.getStateMachine().getState().getId());
+
+ // 模拟准备资源
+ try {
+ Thread.sleep(100); // 模拟耗时操作
+ logger.info("资源准备完成");
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * 执行 Action
+ */
+ public static class ExecuteAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ logger.info("=== Action: 执行任务 ===");
+ logger.info("当前状态: " + context.getStateMachine().getState().getId());
+
+ // 模拟执行任务
+ try {
+ Thread.sleep(200); // 模拟耗时操作
+ logger.info("任务执行完成");
+
+ // 可以在扩展状态中存储执行结果
+ context.getExtendedState().getVariables().put("executionResult", "success");
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /**
+ * 验证 Action
+ */
+ public static class ValidateAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ logger.info("=== Action: 验证任务结果 ===");
+ logger.info("当前状态: " + context.getStateMachine().getState().getId());
+
+ // 获取执行结果
+ Object result = context.getExtendedState().getVariables().get("executionResult");
+ logger.info("执行结果: " + result);
+
+ // 模拟验证
+ boolean isValid = result != null && "success".equals(result);
+ context.getExtendedState().getVariables().put("validationResult", isValid);
+ logger.info("验证结果: " + isValid);
+ }
+ }
+
+ /**
+ * 完成 Action
+ */
+ public static class CompleteAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ logger.info("=== Action: 任务完成 ===");
+ logger.info("从状态: " + context.getSource().getId());
+ logger.info("到状态: " + context.getTarget().getId());
+
+ // 清理资源或执行完成后的操作
+ logger.info("清理资源...");
+ }
+ }
+
+ /**
+ * 失败 Action
+ */
+ public static class FailAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ logger.warning("=== Action: 任务失败 ===");
+ logger.warning("从状态: " + context.getSource().getId());
+ logger.warning("到状态: " + context.getTarget().getId());
+
+ // 记录失败信息
+ Exception exception = context.getException();
+ if (exception != null) {
+ logger.warning("失败原因: " + exception.getMessage());
+ }
+ }
+ }
+
+ /**
+ * 重置 Action
+ */
+ public static class ResetAction implements Action {
+ @Override
+ public void execute(StateContext context) {
+ logger.info("=== Action: 重置状态机 ===");
+
+ // 清理扩展状态中的变量
+ context.getExtendedState().getVariables().clear();
+ logger.info("状态机已重置");
+ }
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/statemachine/config/TaskStateMachineConfig.java b/src/main/java/com/tuoheng/status/statemachine/config/TaskStateMachineConfig.java
new file mode 100644
index 0000000..7e63342
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/config/TaskStateMachineConfig.java
@@ -0,0 +1,182 @@
+package com.tuoheng.status.statemachine.config;
+
+import com.tuoheng.status.statemachine.action.TaskAction;
+import com.tuoheng.status.statemachine.events.Event;
+import com.tuoheng.status.statemachine.guard.TaskGuard;
+import com.tuoheng.status.statemachine.listener.TaskStateMachineListener;
+import com.tuoheng.status.statemachine.status.Status;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.config.StateMachineBuilder;
+import org.springframework.statemachine.config.StateMachineFactory;
+import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
+import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
+import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
+
+import java.util.EnumSet;
+import java.util.UUID;
+import java.util.logging.Logger;
+
+/**
+ * 状态机配置类
+ * 配置状态、子状态、转换、Action、Guard 等
+ */
+@Configuration
+public class TaskStateMachineConfig {
+
+ private static final Logger logger = Logger.getLogger(TaskStateMachineConfig.class.getName());
+
+ /**
+ * 构建状态机工厂
+ * 使用工厂模式可以为每个任务创建独立的状态机实例
+ */
+ @Bean
+ public StateMachineFactory stateMachineFactory() throws Exception {
+
+ // 返回一个简单的 StateMachineFactory 实现
+ return new StateMachineFactory() {
+ @Override
+ public StateMachine getStateMachine() {
+ return getStateMachine(UUID.randomUUID().toString());
+ }
+
+ @Override
+ public StateMachine getStateMachine(String machineId) {
+ try {
+ // 为每个请求创建新的状态机实例
+ StateMachineBuilder.Builder newBuilder = StateMachineBuilder.builder();
+ configureStateMachine(newBuilder);
+ configureStates(newBuilder);
+ configureTransitions(newBuilder);
+ StateMachine stateMachine = newBuilder.build();
+ stateMachine.getExtendedState().getVariables().put("machineId", machineId);
+ return stateMachine;
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create state machine", e);
+ }
+ }
+
+ @Override
+ public StateMachine getStateMachine(UUID uuid) {
+ return getStateMachine(uuid.toString());
+ }
+ };
+ }
+
+ /**
+ * 配置状态机基本设置
+ */
+ private void configureStateMachine(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureConfiguration()
+ .withConfiguration()
+ .autoStartup(true) // 自动启动
+ .listener(new TaskStateMachineListener()); // 添加监听器
+ }
+
+ /**
+ * 配置状态和子状态
+ */
+ private void configureStates(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureStates()
+ .withStates()
+ // 初始状态
+ .initial(Status.IDLE)
+
+ // 定义主状态
+ .states(EnumSet.of(Status.IDLE, Status.COMPLETED, Status.FAILED))
+
+ // PROCESSING 是父状态,包含子状态
+ .and()
+ .withStates()
+ .parent(Status.PROCESSING) // 设置 PROCESSING 为父状态
+ .initial(Status.PREPARING) // 子状态的初始状态是 PREPARING
+ .states(EnumSet.of(Status.PREPARING, Status.EXECUTING, Status.VALIDATING));
+ }
+
+ /**
+ * 配置状态转换
+ */
+ private void configureTransitions(StateMachineBuilder.Builder builder) throws Exception {
+ builder.configureTransitions()
+ // IDLE -> PROCESSING (PREPARING)
+ .withExternal()
+ .source(Status.IDLE)
+ .target(Status.PREPARING)
+ .event(Event.START)
+ .action(new TaskAction.StartAction())
+ .guard(new TaskGuard.CanStartGuard())
+ .and()
+
+ // PREPARING -> EXECUTING
+ .withExternal()
+ .source(Status.PREPARING)
+ .target(Status.EXECUTING)
+ .event(Event.PREPARE)
+ .action(new TaskAction.PrepareAction())
+ .guard(new TaskGuard.PrepareCompleteGuard())
+ .and()
+
+ // EXECUTING -> VALIDATING
+ .withExternal()
+ .source(Status.EXECUTING)
+ .target(Status.VALIDATING)
+ .event(Event.EXECUTE)
+ .action(new TaskAction.ExecuteAction())
+ .guard(new TaskGuard.ExecutionSuccessGuard())
+ .and()
+
+ // VALIDATING -> COMPLETED (从子状态退出到主状态)
+ .withExternal()
+ .source(Status.VALIDATING)
+ .target(Status.COMPLETED)
+ .event(Event.VALIDATE)
+ .action(new TaskAction.ValidateAction())
+ .guard(new TaskGuard.ValidationPassGuard())
+ .and()
+
+ // 任何 PROCESSING 子状态 -> FAILED
+ .withExternal()
+ .source(Status.PREPARING)
+ .target(Status.FAILED)
+ .event(Event.FAIL)
+ .action(new TaskAction.FailAction())
+ .and()
+ .withExternal()
+ .source(Status.EXECUTING)
+ .target(Status.FAILED)
+ .event(Event.FAIL)
+ .action(new TaskAction.FailAction())
+ .and()
+ .withExternal()
+ .source(Status.VALIDATING)
+ .target(Status.FAILED)
+ .event(Event.FAIL)
+ .action(new TaskAction.FailAction())
+ .and()
+
+ // FAILED -> PREPARING (重试)
+ .withExternal()
+ .source(Status.FAILED)
+ .target(Status.PREPARING)
+ .event(Event.RETRY)
+ .action(new TaskAction.StartAction())
+ .guard(new TaskGuard.CanRetryGuard())
+ .and()
+
+ // COMPLETED -> IDLE (重置)
+ .withExternal()
+ .source(Status.COMPLETED)
+ .target(Status.IDLE)
+ .event(Event.RESET)
+ .action(new TaskAction.ResetAction())
+ .and()
+
+ // FAILED -> IDLE (重置)
+ .withExternal()
+ .source(Status.FAILED)
+ .target(Status.IDLE)
+ .event(Event.RESET)
+ .action(new TaskAction.ResetAction());
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/statemachine/demo/StateMachineDemo.java b/src/main/java/com/tuoheng/status/statemachine/demo/StateMachineDemo.java
new file mode 100644
index 0000000..c5ebc39
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/demo/StateMachineDemo.java
@@ -0,0 +1,94 @@
+package com.tuoheng.status.statemachine.demo;
+
+import com.tuoheng.status.statemachine.service.TaskStateMachineService;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+/**
+ * 状态机演示主类
+ * 可以直接运行此类的 main 方法来查看状态机的运行效果
+ */
+public class StateMachineDemo {
+
+ public static void main(String[] args) {
+ // 创建 Spring 上下文
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+
+ // 扫描配置类和服务类
+ context.scan(
+ "com.tuoheng.status.statemachine.config",
+ "com.tuoheng.status.statemachine.service"
+ );
+ context.refresh();
+
+ // 获取服务实例
+ TaskStateMachineService service = context.getBean(TaskStateMachineService.class);
+
+ System.out.println("\n\n");
+ System.out.println("╔════════════════════════════════════════════════════════════╗");
+ System.out.println("║ Spring StateMachine 完整示例演示 ║");
+ System.out.println("║ 包含:子状态、Action、Guard、事件监听器 ║");
+ System.out.println("╚════════════════════════════════════════════════════════════╝");
+ System.out.println("\n");
+
+ // 演示1: 正常流程
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示1: 正常流程");
+ System.out.println("流程: IDLE -> PREPARING -> EXECUTING -> VALIDATING -> COMPLETED");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ service.startTask("demo-task-001");
+
+ // 等待一下
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ // 演示2: 失败和重试
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示2: 失败和重试流程");
+ System.out.println("流程: IDLE -> PREPARING -> FAILED -> PREPARING (重试)");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ service.demonstrateFailureAndRetry("demo-task-002");
+
+ // 等待一下
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ // 演示3: Guard 条件不满足
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示3: Guard 条件不满足");
+ System.out.println("说明: 不设置 taskId,Guard 会阻止状态转换");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ service.demonstrateGuardFailure("demo-task-003");
+
+ // 等待一下
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ // 演示4: 重置流程
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示4: 重置流程");
+ System.out.println("流程: COMPLETED -> IDLE (重置)");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ service.demonstrateReset("demo-task-004");
+
+ System.out.println("\n\n");
+ System.out.println("╔════════════════════════════════════════════════════════════╗");
+ System.out.println("║ 所有演示完成! ║");
+ System.out.println("╚════════════════════════════════════════════════════════════╝");
+ System.out.println("\n");
+
+ // 关闭上下文
+ context.close();
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/statemachine/demo/StateMachineManagerDemo.java b/src/main/java/com/tuoheng/status/statemachine/demo/StateMachineManagerDemo.java
new file mode 100644
index 0000000..619d4f9
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/demo/StateMachineManagerDemo.java
@@ -0,0 +1,161 @@
+package com.tuoheng.status.statemachine.demo;
+
+import com.tuoheng.status.statemachine.events.Event;
+import com.tuoheng.status.statemachine.manager.StateMachineManager;
+import com.tuoheng.status.statemachine.service.StateMachineManagerService;
+import com.tuoheng.status.statemachine.status.Status;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+
+import java.util.Set;
+
+/**
+ * 状态机管理器演示类
+ * 演示如何通过ID管理多个状态机实例
+ */
+public class StateMachineManagerDemo {
+
+ public static void main(String[] args) {
+ // 创建 Spring 上下文
+ AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+ context.scan(
+ "com.tuoheng.status.statemachine.config",
+ "com.tuoheng.status.statemachine.manager",
+ "com.tuoheng.status.statemachine.service"
+ );
+ context.refresh();
+
+ // 获取服务实例
+ StateMachineManagerService service = context.getBean(StateMachineManagerService.class);
+ StateMachineManager manager = context.getBean(StateMachineManager.class);
+
+ System.out.println("\n\n");
+ System.out.println("╔════════════════════════════════════════════════════════════╗");
+ System.out.println("║ Spring StateMachine 管理器演示 ║");
+ System.out.println("║ 演示如何通过ID管理多个状态机实例 ║");
+ System.out.println("╚════════════════════════════════════════════════════════════╝");
+ System.out.println("\n");
+
+ // 演示1: 创建多个状态机
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示1: 创建多个状态机实例");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+
+ String machineId1 = "machine-001";
+ String machineId2 = "machine-002";
+ String machineId3 = "machine-003";
+
+ // 创建状态机
+ service.startTask(machineId1, "task-001");
+ service.startTask(machineId2, "task-002");
+ service.startTask(machineId3, "task-003");
+
+ System.out.println("创建了3个状态机:");
+ System.out.println(" - " + machineId1);
+ System.out.println(" - " + machineId2);
+ System.out.println(" - " + machineId3);
+ System.out.println("管理的状态机数量: " + manager.getStateMachineCount());
+
+ // 等待状态转换
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ // 演示2: 查询不同状态机的状态
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示2: 查询不同状态机的当前状态");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+
+ Status status1 = service.getCurrentStatus(machineId1);
+ Status status2 = service.getCurrentStatus(machineId2);
+ Status status3 = service.getCurrentStatus(machineId3);
+
+ System.out.println("状态机 " + machineId1 + " 当前状态: " + status1);
+ System.out.println("状态机 " + machineId2 + " 当前状态: " + status2);
+ System.out.println("状态机 " + machineId3 + " 当前状态: " + status3);
+
+ // 演示3: 获取状态机详细信息
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示3: 获取状态机详细信息");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+
+ StateMachineManager.StateMachineInfo info1 = service.getStateMachineInfo(machineId1);
+ System.out.println("状态机1详细信息:");
+ System.out.println(" ID: " + info1.getMachineId());
+ System.out.println(" 当前状态: " + info1.getCurrentStatus());
+ System.out.println(" 是否子状态: " + info1.isSubState());
+ System.out.println(" 是否运行中: " + info1.isRunning());
+ System.out.println(" 扩展状态: " + info1.getExtendedState());
+
+ // 演示4: 为不同状态机发送不同事件
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示4: 为不同状态机发送不同事件");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+
+ // 为 machine-001 继续处理
+ manager.getStateMachine(machineId1)
+ .getExtendedState().getVariables().put("prepared", true);
+ service.sendEvent(machineId1, Event.PREPARE);
+
+ // 为 machine-002 发送失败事件
+ service.sendEvent(machineId2, Event.FAIL);
+
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ System.out.println("machine-001 发送 PREPARE 事件后状态: " + service.getCurrentStatus(machineId1));
+ System.out.println("machine-002 发送 FAIL 事件后状态: " + service.getCurrentStatus(machineId2));
+ System.out.println("machine-003 状态保持不变: " + service.getCurrentStatus(machineId3));
+
+ // 演示5: 获取所有状态机ID
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示5: 获取所有状态机ID");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+
+ Set allIds = service.getAllMachineIds();
+ System.out.println("所有状态机ID: " + allIds);
+ System.out.println("状态机数量: " + allIds.size());
+
+ // 演示6: 通过ID直接获取状态机并操作
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示6: 通过ID直接获取状态机并操作");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+
+ var stateMachine = manager.getStateMachine(machineId1);
+ if (stateMachine != null) {
+ System.out.println("成功获取状态机: " + machineId1);
+ System.out.println("当前状态: " + stateMachine.getState().getId());
+ System.out.println("扩展状态中的 taskId: " +
+ stateMachine.getExtendedState().getVariables().get("taskId"));
+ }
+
+ // 演示7: 移除状态机
+ System.out.println("\n");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+ System.out.println("演示7: 移除状态机");
+ System.out.println("═══════════════════════════════════════════════════════════════");
+
+ boolean removed = service.removeStateMachine(machineId3);
+ System.out.println("移除状态机 " + machineId3 + ": " + (removed ? "成功" : "失败"));
+ System.out.println("移除后管理的状态机数量: " + manager.getStateMachineCount());
+ System.out.println("状态机 " + machineId3 + " 是否存在: " + service.exists(machineId3));
+
+ System.out.println("\n\n");
+ System.out.println("╔════════════════════════════════════════════════════════════╗");
+ System.out.println("║ 所有演示完成! ║");
+ System.out.println("╚════════════════════════════════════════════════════════════╝");
+ System.out.println("\n");
+
+ // 关闭上下文
+ context.close();
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/statemachine/events/Event.java b/src/main/java/com/tuoheng/status/statemachine/events/Event.java
new file mode 100644
index 0000000..80a4925
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/events/Event.java
@@ -0,0 +1,15 @@
+package com.tuoheng.status.statemachine.events;
+
+/**
+ * 事件枚举
+ */
+public enum Event {
+ START, // 开始处理
+ PREPARE, // 准备就绪
+ EXECUTE, // 执行
+ VALIDATE, // 验证
+ COMPLETE, // 完成
+ FAIL, // 失败
+ RETRY, // 重试
+ RESET // 重置
+}
diff --git a/src/main/java/com/tuoheng/status/statemachine/guard/TaskGuard.java b/src/main/java/com/tuoheng/status/statemachine/guard/TaskGuard.java
new file mode 100644
index 0000000..b0bb45c
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/guard/TaskGuard.java
@@ -0,0 +1,112 @@
+package com.tuoheng.status.statemachine.guard;
+
+import com.tuoheng.status.statemachine.events.Event;
+import com.tuoheng.status.statemachine.status.Status;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.guard.Guard;
+
+import java.util.logging.Logger;
+
+/**
+ * 任务 Guard 示例
+ * Guard 用于条件判断,决定是否允许状态转换
+ */
+public class TaskGuard {
+
+ private static final Logger logger = Logger.getLogger(TaskGuard.class.getName());
+
+ /**
+ * 检查是否可以开始处理
+ */
+ public static class CanStartGuard implements Guard {
+ @Override
+ public boolean evaluate(StateContext context) {
+ logger.info("=== Guard: 检查是否可以开始处理 ===");
+
+ // 检查是否有任务ID
+ Object taskId = context.getExtendedState().getVariables().get("taskId");
+ boolean canStart = taskId != null;
+
+ logger.info("任务ID存在: " + canStart);
+ return canStart;
+ }
+ }
+
+ /**
+ * 检查准备是否完成
+ */
+ public static class PrepareCompleteGuard implements Guard {
+ @Override
+ public boolean evaluate(StateContext context) {
+ logger.info("=== Guard: 检查准备是否完成 ===");
+
+ // 检查准备状态
+ Object prepared = context.getExtendedState().getVariables().get("prepared");
+ boolean isPrepared = prepared != null && Boolean.TRUE.equals(prepared);
+
+ logger.info("准备完成: " + isPrepared);
+ return isPrepared;
+ }
+ }
+
+ /**
+ * 检查执行是否成功
+ */
+ public static class ExecutionSuccessGuard implements Guard {
+ @Override
+ public boolean evaluate(StateContext context) {
+ logger.info("=== Guard: 检查执行是否成功 ===");
+
+ // 检查执行结果
+ Object result = context.getExtendedState().getVariables().get("executionResult");
+ boolean isSuccess = result != null && "success".equals(result);
+
+ logger.info("执行成功: " + isSuccess);
+ return isSuccess;
+ }
+ }
+
+ /**
+ * 检查验证是否通过
+ */
+ public static class ValidationPassGuard implements Guard {
+ @Override
+ public boolean evaluate(StateContext context) {
+ logger.info("=== Guard: 检查验证是否通过 ===");
+
+ // 检查验证结果
+ Object validationResult = context.getExtendedState().getVariables().get("validationResult");
+ boolean isValid = validationResult != null && Boolean.TRUE.equals(validationResult);
+
+ logger.info("验证通过: " + isValid);
+ return isValid;
+ }
+ }
+
+ /**
+ * 检查是否可以重试
+ */
+ public static class CanRetryGuard implements Guard {
+ @Override
+ public boolean evaluate(StateContext context) {
+ logger.info("=== Guard: 检查是否可以重试 ===");
+
+ // 检查重试次数
+ Integer retryCount = (Integer) context.getExtendedState().getVariables().getOrDefault("retryCount", 0);
+ boolean canRetry = retryCount < 3; // 最多重试3次
+
+ logger.info("当前重试次数: " + retryCount + ", 可以重试: " + canRetry);
+ return canRetry;
+ }
+ }
+
+ /**
+ * 总是返回 true 的 Guard(用于无条件转换)
+ */
+ public static class AlwaysTrueGuard implements Guard {
+ @Override
+ public boolean evaluate(StateContext context) {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/statemachine/listener/TaskStateMachineListener.java b/src/main/java/com/tuoheng/status/statemachine/listener/TaskStateMachineListener.java
new file mode 100644
index 0000000..a097901
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/listener/TaskStateMachineListener.java
@@ -0,0 +1,157 @@
+package com.tuoheng.status.statemachine.listener;
+
+import com.tuoheng.status.statemachine.events.Event;
+import com.tuoheng.status.statemachine.status.Status;
+import org.springframework.statemachine.StateContext;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.listener.StateMachineListener;
+import org.springframework.statemachine.state.State;
+import org.springframework.statemachine.transition.Transition;
+
+import java.util.logging.Logger;
+
+/**
+ * 状态机事件监听器
+ * 监听状态机的各种事件,如状态变化、转换等
+ */
+public class TaskStateMachineListener implements StateMachineListener {
+
+ private static final Logger logger = Logger.getLogger(TaskStateMachineListener.class.getName());
+
+ /**
+ * 状态机启动时调用
+ */
+ @Override
+ public void stateMachineStarted(StateMachine stateMachine) {
+ logger.info("========== 状态机启动 ==========");
+ if (stateMachine.getInitialState() != null) {
+ logger.info("初始状态: " + stateMachine.getInitialState().getId());
+ } else {
+ logger.info("初始状态: null");
+ }
+ }
+
+ /**
+ * 状态机停止时调用
+ */
+ @Override
+ public void stateMachineStopped(StateMachine stateMachine) {
+ logger.info("========== 状态机停止 ==========");
+ if (stateMachine.getState() != null) {
+ logger.info("最终状态: " + stateMachine.getState().getId());
+ } else {
+ logger.info("最终状态: null");
+ }
+ }
+
+ /**
+ * 状态机错误时调用
+ */
+ @Override
+ public void stateMachineError(StateMachine stateMachine, Exception exception) {
+ logger.severe("========== 状态机错误 ==========");
+ logger.severe("错误信息: " + exception.getMessage());
+ exception.printStackTrace();
+ }
+
+ /**
+ * 状态进入时调用
+ */
+ @Override
+ public void stateEntered(State state) {
+ logger.info("========== 进入状态: " + state.getId() + " ==========");
+ }
+
+ /**
+ * 状态退出时调用
+ */
+ @Override
+ public void stateExited(State state) {
+ logger.info("========== 退出状态: " + state.getId() + " ==========");
+ }
+
+ /**
+ * 状态改变时调用
+ */
+ @Override
+ public void stateChanged(State from, State to) {
+ logger.info("========== 状态改变 ==========");
+ logger.info("从: " + (from != null ? from.getId() : "null"));
+ logger.info("到: " + (to != null ? to.getId() : "null"));
+ }
+
+ /**
+ * 转换开始时调用
+ */
+ @Override
+ public void transitionStarted(Transition transition) {
+ logger.info("========== 转换开始 ==========");
+ logger.info("从: " + (transition.getSource() != null ? transition.getSource().getId() : "null"));
+ logger.info("到: " + (transition.getTarget() != null ? transition.getTarget().getId() : "null"));
+ // 检查 trigger 是否为 null(某些转换可能没有 trigger,比如初始转换)
+ if (transition.getTrigger() != null) {
+ logger.info("事件: " + transition.getTrigger().getEvent());
+ } else {
+ logger.info("事件: null (可能是初始转换或内部转换)");
+ }
+ }
+
+ /**
+ * 转换结束时调用
+ */
+ @Override
+ public void transitionEnded(Transition transition) {
+ logger.info("========== 转换结束 ==========");
+ logger.info("从: " + (transition.getSource() != null ? transition.getSource().getId() : "null"));
+ logger.info("到: " + (transition.getTarget() != null ? transition.getTarget().getId() : "null"));
+ // 检查 trigger 是否为 null
+ if (transition.getTrigger() != null) {
+ logger.info("事件: " + transition.getTrigger().getEvent());
+ } else {
+ logger.info("事件: null (可能是初始转换或内部转换)");
+ }
+ }
+
+ /**
+ * 转换选择时调用
+ */
+ @Override
+ public void transition(Transition transition) {
+ logger.info("========== 转换选择 ==========");
+ logger.info("从: " + (transition.getSource() != null ? transition.getSource().getId() : "null"));
+ logger.info("到: " + (transition.getTarget() != null ? transition.getTarget().getId() : "null"));
+ // 检查 trigger 是否为 null
+ if (transition.getTrigger() != null) {
+ logger.info("事件: " + transition.getTrigger().getEvent());
+ } else {
+ logger.info("事件: null (可能是初始转换或内部转换)");
+ }
+ }
+
+ /**
+ * 事件未接受时调用(没有匹配的转换)
+ */
+ @Override
+ public void eventNotAccepted(org.springframework.messaging.Message event) {
+ logger.warning("========== 事件未接受 ==========");
+ logger.warning("事件: " + event.getPayload());
+ logger.warning("当前状态可能不支持此事件");
+ }
+
+ /**
+ * 扩展状态改变时调用
+ */
+ @Override
+ public void extendedStateChanged(Object key, Object value) {
+ logger.info("========== 扩展状态改变 ==========");
+ logger.info("键: " + key + ", 值: " + value);
+ }
+
+ /**
+ * 状态上下文入口时调用
+ */
+ @Override
+ public void stateContext(StateContext stateContext) {
+ // 可以在这里记录状态上下文信息
+ }
+}
diff --git a/src/main/java/com/tuoheng/status/statemachine/manager/StateMachineManager.java b/src/main/java/com/tuoheng/status/statemachine/manager/StateMachineManager.java
new file mode 100644
index 0000000..641bdd9
--- /dev/null
+++ b/src/main/java/com/tuoheng/status/statemachine/manager/StateMachineManager.java
@@ -0,0 +1,262 @@
+package com.tuoheng.status.statemachine.manager;
+
+import com.tuoheng.status.statemachine.events.Event;
+import com.tuoheng.status.statemachine.status.Status;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.statemachine.StateMachine;
+import org.springframework.statemachine.config.StateMachineFactory;
+import org.springframework.statemachine.state.State;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.logging.Logger;
+
+/**
+ * 状态机管理器
+ * 用于管理多个状态机实例,通过ID获取和查询状态机
+ */
+@Component
+public class StateMachineManager {
+
+ private static final Logger logger = Logger.getLogger(StateMachineManager.class.getName());
+
+ @Autowired
+ private StateMachineFactory stateMachineFactory;
+
+ // 存储状态机实例的Map,key为状态机ID
+ private final Map> stateMachineMap = new ConcurrentHashMap<>();
+
+ /**
+ * 创建或获取状态机实例
+ * 如果ID对应的状态机不存在,则创建新实例;如果存在,则返回现有实例
+ *
+ * @param machineId 状态机ID
+ * @return 状态机实例
+ */
+ public StateMachine getOrCreateStateMachine(String machineId) {
+ return stateMachineMap.computeIfAbsent(machineId, id -> {
+ logger.info("创建新状态机实例,ID: " + id);
+ StateMachine stateMachine = stateMachineFactory.getStateMachine(id);
+ // 将machineId存储到扩展状态中
+ stateMachine.getExtendedState().getVariables().put("machineId", id);
+ // 启动状态机
+ stateMachine.start();
+ return stateMachine;
+ });
+ }
+
+ /**
+ * 获取状态机实例(如果不存在则返回null)
+ *
+ * @param machineId 状态机ID
+ * @return 状态机实例,如果不存在则返回null
+ */
+ public StateMachine getStateMachine(String machineId) {
+ return stateMachineMap.get(machineId);
+ }
+
+ /**
+ * 创建新的状态机实例(如果已存在则先停止并移除旧的)
+ *
+ * @param machineId 状态机ID
+ * @return 新创建的状态机实例
+ */
+ public StateMachine createStateMachine(String machineId) {
+ // 如果已存在,先停止并移除
+ StateMachine existing = stateMachineMap.remove(machineId);
+ if (existing != null) {
+ logger.info("停止并移除已存在的状态机,ID: " + machineId);
+ try {
+ existing.stop();
+ } catch (Exception e) {
+ logger.warning("停止状态机时发生错误: " + e.getMessage());
+ }
+ }
+
+ // 创建新实例
+ logger.info("创建新状态机实例,ID: " + machineId);
+ StateMachine stateMachine = stateMachineFactory.getStateMachine(machineId);
+ stateMachine.getExtendedState().getVariables().put("machineId", machineId);
+ stateMachine.start();
+ stateMachineMap.put(machineId, stateMachine);
+ return stateMachine;
+ }
+
+ /**
+ * 获取状态机的当前状态
+ *
+ * @param machineId 状态机ID
+ * @return 当前状态,如果状态机不存在则返回null
+ */
+ public Status getCurrentStatus(String machineId) {
+ StateMachine stateMachine = stateMachineMap.get(machineId);
+ if (stateMachine == null) {
+ logger.warning("状态机不存在,ID: " + machineId);
+ return null;
+ }
+
+ State state = stateMachine.getState();
+ if (state == null) {
+ return null;
+ }
+
+ return state.getId();
+ }
+
+ /**
+ * 获取状态机的详细信息(包括当前状态和扩展状态)
+ *
+ * @param machineId 状态机ID
+ * @return 状态机信息,如果状态机不存在则返回null
+ */
+ public StateMachineInfo getStateMachineInfo(String machineId) {
+ StateMachine stateMachine = stateMachineMap.get(machineId);
+ if (stateMachine == null) {
+ return null;
+ }
+
+ StateMachineInfo info = new StateMachineInfo();
+ info.setMachineId(machineId);
+
+ State state = stateMachine.getState();
+ if (state != null) {
+ info.setCurrentStatus(state.getId());
+ info.setIsSubState(state.isSubmachineState());
+ }
+
+ info.setExtendedState(stateMachine.getExtendedState().getVariables());
+ // 通过检查状态机是否有状态来判断是否在运行
+ // Spring StateMachine 3.2.0 没有 isRunning() 方法,使用状态判断
+ info.setIsRunning(state != null);
+
+ return info;
+ }
+
+ /**
+ * 移除状态机实例
+ *
+ * @param machineId 状态机ID
+ * @return 是否成功移除
+ */
+ public boolean removeStateMachine(String machineId) {
+ StateMachine stateMachine = stateMachineMap.remove(machineId);
+ if (stateMachine != null) {
+ logger.info("移除状态机实例,ID: " + machineId);
+ try {
+ stateMachine.stop();
+ return true;
+ } catch (Exception e) {
+ logger.warning("停止状态机时发生错误: " + e.getMessage());
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 检查状态机是否存在
+ *
+ * @param machineId 状态机ID
+ * @return 是否存在
+ */
+ public boolean exists(String machineId) {
+ return stateMachineMap.containsKey(machineId);
+ }
+
+ /**
+ * 获取所有状态机的ID列表
+ *
+ * @return 状态机ID集合
+ */
+ public java.util.Set getAllMachineIds() {
+ return stateMachineMap.keySet();
+ }
+
+ /**
+ * 获取当前管理的状态机数量
+ *
+ * @return 状态机数量
+ */
+ public int getStateMachineCount() {
+ return stateMachineMap.size();
+ }
+
+ /**
+ * 清空所有状态机实例
+ */
+ public void clearAll() {
+ logger.info("清空所有状态机实例,数量: " + stateMachineMap.size());
+ for (Map.Entry> entry : stateMachineMap.entrySet()) {
+ try {
+ entry.getValue().stop();
+ } catch (Exception e) {
+ logger.warning("停止状态机时发生错误,ID: " + entry.getKey() + ", 错误: " + e.getMessage());
+ }
+ }
+ stateMachineMap.clear();
+ }
+
+ /**
+ * 状态机信息类
+ */
+ public static class StateMachineInfo {
+ private String machineId;
+ private Status currentStatus;
+ private boolean isSubState;
+ private boolean isRunning;
+ private Map