diff --git a/solon-projects/solon-base/solon-statemachine/NOTICE b/solon-projects/solon-base/solon-statemachine/NOTICE new file mode 100644 index 0000000000000000000000000000000000000000..0a7dd15facdb99fca5e29ee9aa2246fcd52d170c --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/NOTICE @@ -0,0 +1,28 @@ +Copyright 2017-2025 noear.org and authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +This project includes: + asm under BSD-3-Clause + JUnit Jupiter (Aggregator) under Eclipse Public License v2.0 + JUnit Jupiter API under Eclipse Public License v2.0 + JUnit Jupiter Engine under Eclipse Public License v2.0 + JUnit Jupiter Params under Eclipse Public License v2.0 + JUnit Platform Commons under Eclipse Public License v2.0 + JUnit Platform Engine API under Eclipse Public License v2.0 + org.apiguardian:apiguardian-api under The Apache License, Version 2.0 + org.opentest4j:opentest4j under The Apache License, Version 2.0 + SLF4J API Module under MIT License + solon under The Apache Software License, Version 2.0 + solon-proxy under The Apache Software License, Version 2.0 + diff --git a/solon-projects/solon-base/solon-statemachine/pom.xml b/solon-projects/solon-base/solon-statemachine/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..aa7147009cfe76562bef4b22ed76829d4600c066 --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + + org.noear + solon-parent + 3.4.2 + ../../../solon-parent/pom.xml + + + solon-statemachine + ${project.artifactId} + jar + + + + org.noear + solon + + + + org.junit.jupiter + junit-jupiter + ${junit5.version} + test + + + \ No newline at end of file diff --git a/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/Event.java b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/Event.java new file mode 100644 index 0000000000000000000000000000000000000000..114fb370a9e0368983b5c1fc7514faac0e98bc77 --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/Event.java @@ -0,0 +1,7 @@ +package org.noear.solon.statemachine; + +/** + * 事件接口 + */ +public interface Event { +} \ No newline at end of file diff --git a/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/State.java b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/State.java new file mode 100644 index 0000000000000000000000000000000000000000..998cf31e1b08772eda4099f6b431f837e17c95f8 --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/State.java @@ -0,0 +1,7 @@ +package org.noear.solon.statemachine; + +/** + * 状态接口 + */ +public interface State { +} \ No newline at end of file diff --git a/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateContext.java b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateContext.java new file mode 100644 index 0000000000000000000000000000000000000000..a7b7b1c87a395ddbba773de32ab367e8870d74be --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateContext.java @@ -0,0 +1,36 @@ +package org.noear.solon.statemachine; + +/** + * 状态机上下文,保存状态转换相关信息 + */ +public class StateContext { + private final S from; + private final S to; + private final E event; + private final T payload; + + public StateContext(S from, S to, E event, T payload) { + this.from = from; + this.to = to; + this.event = event; + this.payload = payload; + } + + public S getFrom() { + return from; + } + + public S getTo() { + return to; + } + + public E getEvent() { + return event; + } + + + public T getPayload() { + return payload; + } + +} \ No newline at end of file diff --git a/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateMachine.java b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateMachine.java new file mode 100644 index 0000000000000000000000000000000000000000..c9fc4eaa1da3e368ee560dd5498b614389111d62 --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateMachine.java @@ -0,0 +1,50 @@ +package org.noear.solon.statemachine; + +import java.util.ArrayList; +import java.util.List; + +public class StateMachine { + /** + * 当前状态 + */ + private S currentState; + + private final List> transitions = new ArrayList<>(); + + public StateMachine(S initState) { + this.currentState = initState; + } + + /** + * 添加状态转换规则 + */ + public void addTransition(StateTransition transition) { + transitions.add(transition); + } + + public synchronized boolean execute(E event, T payload) { + for (StateTransition transition : transitions) { + if (transition.matches(currentState, event, payload)) { + S from = currentState; + S to = transition.getTo(); + currentState = to; + transition.execute(new StateContext<>(from, to, event, payload)); + return true; + } + } + return false; + } + + public synchronized boolean execute(E event) { + return execute(event, null); + + } + + public S getCurrentState() { + return currentState; + } + + public void restore(S state) { + this.currentState = state; + } +} \ No newline at end of file diff --git a/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateTransition.java b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateTransition.java new file mode 100644 index 0000000000000000000000000000000000000000..582ac214a53e7ec3a0ad0026a7c58cb9049d90ae --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/main/java/org/noear/solon/statemachine/StateTransition.java @@ -0,0 +1,55 @@ +package org.noear.solon.statemachine; + +import java.util.function.BiPredicate; +import java.util.function.Consumer; + +/** + * 状态转换定义 + */ +public class StateTransition { + private final S from; + private final S to; + private final E event; + + private final BiPredicate, T> condition; + private final Consumer> action; + + public StateTransition(S from, S to, E event) { + this(from, to, event, (ctx, payload) -> true, (ctx) -> { + }); + } + + public StateTransition(S from, S to, E event, Consumer> action) { + this(from, to, event, (ctx, payload) -> true, action); + } + + public StateTransition(S from, S to, E event, BiPredicate, T> condition, Consumer> action) { + this.from = from; + this.to = to; + this.event = event; + this.condition = condition; + this.action = action; + } + + + public S getFrom() { + return from; + } + + public S getTo() { + return to; + } + + public E getEvent() { + return event; + } + + public boolean matches(S state, E event, T payload) { + return from.equals(state) && this.event.equals(event) && condition.test(new StateContext<>(from, to, event, payload), payload); + } + + public void execute(StateContext context) { + action.accept(context); + } + +} \ No newline at end of file diff --git a/solon-projects/solon-base/solon-statemachine/src/test/java/test1/App.java b/solon-projects/solon-base/solon-statemachine/src/test/java/test1/App.java new file mode 100644 index 0000000000000000000000000000000000000000..08979ea02664bd47d67effb502ee009414e651cb --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/test/java/test1/App.java @@ -0,0 +1,75 @@ +/* + * Copyright 2017-2025 noear.org and authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package test1; + + +import org.junit.jupiter.api.Test; +import org.noear.solon.statemachine.StateMachine; +import org.noear.solon.statemachine.StateTransition; +import test1.enums.OrderEvent; +import test1.enums.OrderState; + +public class App { + @Test + public void test() { + // 创建状态机实例 + StateMachine stateMachine = new StateMachine<>(OrderState.NONE); + + // 无 -> 创建订单 + stateMachine.addTransition(new StateTransition<>(OrderState.NONE, OrderState.CREATED, OrderEvent.CREATE, + (order) -> { + Order payload = order.getPayload(); + payload.setStatus("已创建"); + System.out.println(payload); + } + )); + + // 创建 -> 支付 + stateMachine.addTransition(new StateTransition<>(OrderState.CREATED, OrderState.PAID, OrderEvent.PAY, + (order) -> { + Order payload = order.getPayload(); + payload.setStatus("已支付"); + System.out.println(payload); + } + )); + + // 支付 -> 发货 + stateMachine.addTransition(new StateTransition<>(OrderState.PAID, OrderState.SHIPPED, OrderEvent.SHIP, + (order) -> { + Order payload = order.getPayload(); + payload.setStatus("已发货"); + System.out.println(payload); + } + )); + + + // 发货 -> 送达 + stateMachine.addTransition(new StateTransition<>(OrderState.SHIPPED, OrderState.DELIVERED, OrderEvent.DELIVER, + (order) -> { + Order payload = order.getPayload(); + payload.setStatus("已送达"); + System.out.println(payload); + } + )); + + Order order = new Order("1", "iphone16 pro max", null); + + stateMachine.execute(OrderEvent.CREATE, order); + stateMachine.execute(OrderEvent.PAY, order); + stateMachine.execute(OrderEvent.SHIP, order); + stateMachine.execute(OrderEvent.DELIVER, order); + } +} \ No newline at end of file diff --git a/solon-projects/solon-base/solon-statemachine/src/test/java/test1/Order.java b/solon-projects/solon-base/solon-statemachine/src/test/java/test1/Order.java new file mode 100644 index 0000000000000000000000000000000000000000..b1a281498717b7a171bdd7ffdedaeda6ea7c8bb4 --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/test/java/test1/Order.java @@ -0,0 +1,49 @@ +package test1; + +public class Order { + private String id; + private String productName; + private String status; + + public Order(String id, String productName, String status) { + this.id = id; + this.productName = productName; + this.status = status; + } + + public Order() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getProductName() { + return productName; + } + + public void setProductName(String productName) { + this.productName = productName; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + @Override + public String toString() { + return "Order{" + + "id='" + id + '\'' + + ", productName='" + productName + '\'' + + ", status='" + status + '\'' + + '}'; + } +} diff --git a/solon-projects/solon-base/solon-statemachine/src/test/java/test1/enums/OrderEvent.java b/solon-projects/solon-base/solon-statemachine/src/test/java/test1/enums/OrderEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..bca6be4a1e661c44813213ad6894866a388fd3e9 --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/test/java/test1/enums/OrderEvent.java @@ -0,0 +1,11 @@ +package test1.enums; + + +import org.noear.solon.statemachine.Event; + +/** + * 订单事件枚举 + */ +public enum OrderEvent implements Event { + CREATE, PAY, SHIP, DELIVER, CANCEL; +} \ No newline at end of file diff --git a/solon-projects/solon-base/solon-statemachine/src/test/java/test1/enums/OrderState.java b/solon-projects/solon-base/solon-statemachine/src/test/java/test1/enums/OrderState.java new file mode 100644 index 0000000000000000000000000000000000000000..2665fc2f5447eba3a65e7f170440caf296bbfa66 --- /dev/null +++ b/solon-projects/solon-base/solon-statemachine/src/test/java/test1/enums/OrderState.java @@ -0,0 +1,11 @@ +package test1.enums; + + +import org.noear.solon.statemachine.State; + +/** + * 订单状态枚举 + */ +public enum OrderState implements State { + NONE,CREATED, PAID, SHIPPED, DELIVERED, CANCELLED; +} \ No newline at end of file