From 8c051a429001ed8949039f3bc24f496497f730ac Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Wed, 28 Aug 2024 23:41:36 +0800 Subject: [PATCH] =?UTF-8?q?feature=20#I9T6PB=20=E5=B5=8C=E5=A5=97=E5=BE=AA?= =?UTF-8?q?=E7=8E=AF=E8=8E=B7=E5=BE=97=E4=BB=BB=E6=84=8F=E5=A4=96=E5=B1=82?= =?UTF-8?q?=E7=9A=84=E4=B8=8B=E6=A0=87=E6=88=96=E8=80=85=E5=AF=B9=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/core/NodeComponent.java | 17 ++- .../yomahub/liteflow/flow/element/Node.java | 104 ++++++++++++++++-- .../flow/element/condition/LoopCondition.java | 4 +- .../com/yomahub/liteflow/util/TupleOf2.java | 29 +++++ .../iterator/IteratorELSpringbootTest.java | 12 ++ .../liteflow/test/iterator/cmp/ECmp.java | 34 ++++++ .../test/loop/LoopELSpringbootTest.java | 10 ++ .../yomahub/liteflow/test/loop/cmp/CCmp.java | 12 ++ .../src/test/resources/iterator/flow.xml | 6 + .../src/test/resources/loop/flow.xml | 4 + 10 files changed, 218 insertions(+), 14 deletions(-) create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/util/TupleOf2.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ECmp.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index 6cc841574..23d0b5df4 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -10,7 +10,6 @@ package com.yomahub.liteflow.core; import cn.hutool.core.date.StopWatch; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; -import com.alibaba.ttl.TransmittableThreadLocal; import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil; import com.yomahub.liteflow.enums.CmpStepTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; @@ -419,10 +418,26 @@ public abstract class NodeComponent{ return this.getRefNode().getLoopIndex(); } + public Integer getPreLoopIndex() { + return this.getRefNode().getPreLoopIndex(); + } + + public Integer getPreNLoopIndex(int n) { + return this.getRefNode().getPreNLoopIndex(n); + } + public T getCurrLoopObj() { return this.getRefNode().getCurrLoopObject(); } + public T getPreLoopObj() { + return this.getRefNode().getPreLoopObject(); + } + + public T getPreNLoopObj(int n) { + return this.getRefNode().getPreNLoopObject(n); + } + @Deprecated public void invoke(String chainId, Object param) throws Exception { FlowExecutorHolder.loadInstance().invoke(chainId, param, this.getSlotIndex()); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java index 3659586e8..d0d4586ca 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java @@ -17,12 +17,16 @@ import com.yomahub.liteflow.enums.ExecuteableTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.exception.ChainEndException; import com.yomahub.liteflow.exception.FlowSystemException; +import com.yomahub.liteflow.flow.element.condition.LoopCondition; import com.yomahub.liteflow.flow.executor.NodeExecutor; import com.yomahub.liteflow.flow.executor.NodeExecutorHelper; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.slot.DataBus; import com.yomahub.liteflow.slot.Slot; +import com.yomahub.liteflow.util.TupleOf2; + +import java.util.Stack; /** * Node节点,实现可执行器 Node节点并不是单例的,每构建一次都会copy出一个新的实例 @@ -60,10 +64,10 @@ public class Node implements Executable, Cloneable, Rollbackable{ private TransmittableThreadLocal accessResult = new TransmittableThreadLocal<>(); // 循环下标 - private TransmittableThreadLocal loopIndexTL = new TransmittableThreadLocal<>(); + private TransmittableThreadLocal>> loopIndexTL = new TransmittableThreadLocal<>(); // 迭代对象 - private TransmittableThreadLocal currLoopObject = new TransmittableThreadLocal<>(); + private TransmittableThreadLocal>> loopObjectTL = new TransmittableThreadLocal<>(); // 当前slot的index private TransmittableThreadLocal slotIndexTL = new TransmittableThreadLocal<>(); @@ -290,28 +294,106 @@ public class Node implements Executable, Cloneable, Rollbackable{ this.isContinueOnErrorResult.remove(); } - public void setLoopIndex(int index) { - this.loopIndexTL.set(index); + public void setLoopIndex(LoopCondition condition, int index) { + if (this.loopIndexTL.get() == null){ + Stack> stack = new Stack<>(); + TupleOf2 tuple = new TupleOf2<>(condition.hashCode(), index); + stack.push(tuple); + this.loopIndexTL.set(stack); + }else{ + Stack> stack = this.loopIndexTL.get(); + TupleOf2 thisConditionTuple = stack.stream().filter(tuple -> tuple.getA().equals(condition.hashCode())).findFirst().orElse(null); + if (thisConditionTuple != null){ + thisConditionTuple.setB(index); + }else{ + TupleOf2 tuple = new TupleOf2<>(condition.hashCode(), index); + stack.push(tuple); + } + } } public Integer getLoopIndex() { - return this.loopIndexTL.get(); + Stack> stack = this.loopIndexTL.get(); + if (stack != null){ + return stack.peek().getB(); + }else{ + return null; + } + } + + public Integer getPreLoopIndex(){ + return getPreNLoopIndex(1); + } + + public Integer getPreNLoopIndex(int n){ + Stack> stack = this.loopIndexTL.get(); + if (stack != null && stack.size() > n){ + return stack.elementAt(stack.size() - (n + 1)).getB(); + }else{ + return null; + } } public void removeLoopIndex() { - this.loopIndexTL.remove(); + Stack> stack = this.loopIndexTL.get(); + if (stack != null){ + if (stack.size() > 1){ + stack.pop(); + }else{ + this.loopIndexTL.remove(); + } + } } - public void setCurrLoopObject(Object obj) { - this.currLoopObject.set(obj); + public void setCurrLoopObject(LoopCondition condition, Object obj) { + if (this.loopObjectTL.get() == null){ + Stack> stack = new Stack<>(); + TupleOf2 tuple = new TupleOf2<>(condition.hashCode(), obj); + stack.push(tuple); + this.loopObjectTL.set(stack); + }else{ + Stack> stack = this.loopObjectTL.get(); + TupleOf2 thisConditionTuple = stack.stream().filter(tuple -> tuple.getA().equals(condition.hashCode())).findFirst().orElse(null); + if (thisConditionTuple != null){ + thisConditionTuple.setB(obj); + }else{ + TupleOf2 tuple = new TupleOf2<>(condition.hashCode(), obj); + stack.push(tuple); + } + } } public T getCurrLoopObject() { - return (T) this.currLoopObject.get(); + Stack> stack = this.loopObjectTL.get(); + if (stack != null){ + return (T) stack.peek().getB(); + }else{ + return null; + } + } + + public T getPreLoopObject(){ + return getPreNLoopObject(1); + } + + public T getPreNLoopObject(int n){ + Stack> stack = this.loopObjectTL.get(); + if (stack != null && stack.size() > n){ + return (T) stack.elementAt(stack.size() - (n + 1)).getB(); + }else{ + return null; + } } public void removeCurrLoopObject() { - this.currLoopObject.remove(); + Stack> stack = this.loopObjectTL.get(); + if (stack != null){ + if (stack.size() > 1){ + stack.pop(); + }else{ + this.loopObjectTL.remove(); + } + } } public Integer getSlotIndex(){ @@ -355,7 +437,7 @@ public class Node implements Executable, Cloneable, Rollbackable{ public Node clone() throws CloneNotSupportedException { Node node = (Node)super.clone(); node.loopIndexTL = new TransmittableThreadLocal<>(); - node.currLoopObject = new TransmittableThreadLocal<>(); + node.loopObjectTL = new TransmittableThreadLocal<>(); node.accessResult = new TransmittableThreadLocal<>(); node.slotIndexTL = new TransmittableThreadLocal<>(); node.isEndTL = new TransmittableThreadLocal<>(); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/LoopCondition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/LoopCondition.java index 0355ffd3a..0b957d25d 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/LoopCondition.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/LoopCondition.java @@ -43,7 +43,7 @@ public abstract class LoopCondition extends Condition { ((Condition) executableItem).getExecutableGroup() .forEach((key, value) -> value.forEach(executable -> setLoopIndex(executable, index))); } else if (executableItem instanceof Node) { - ((Node) executableItem).setLoopIndex(index); + ((Node) executableItem).setLoopIndex(this, index); } } @@ -54,7 +54,7 @@ public abstract class LoopCondition extends Condition { ((Condition) executableItem).getExecutableGroup() .forEach((key, value) -> value.forEach(executable -> setCurrLoopObject(executable, obj))); } else if (executableItem instanceof Node) { - ((Node) executableItem).setCurrLoopObject(obj); + ((Node) executableItem).setCurrLoopObject(this, obj); } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/util/TupleOf2.java b/liteflow-core/src/main/java/com/yomahub/liteflow/util/TupleOf2.java new file mode 100644 index 000000000..3a9730205 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/util/TupleOf2.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.util; + +public class TupleOf2 { + + private A a; + + private B b; + + public TupleOf2(A a, B b) { + this.a = a; + this.b = b; + } + + public A getA() { + return a; + } + + public B getB() { + return b; + } + + public void setA(A a) { + this.a = a; + } + + public void setB(B b) { + this.b = b; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringbootTest.java index cdeb448d0..e3a3b47a5 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringbootTest.java @@ -56,4 +56,16 @@ public class IteratorELSpringbootTest extends BaseTest { LiteflowResponse response = flowExecutor.execute2Resp("chain3"); Assertions.assertTrue(response.isSuccess()); } + + //多层迭代循环,取各层obj + @Test + public void testIt4() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain4"); + DefaultContext context = response.getFirstContextBean(); + String indexStr = context.getData("index_str"); + String objStr = context.getData("obj_str"); + System.out.println(indexStr); + System.out.println(objStr); + Assertions.assertTrue(response.isSuccess()); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ECmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ECmp.java new file mode 100644 index 000000000..34f8dd5af --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ECmp.java @@ -0,0 +1,34 @@ + +package com.yomahub.liteflow.test.iterator.cmp; + +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; +import org.springframework.stereotype.Component; + +@Component("e") +public class ECmp extends NodeComponent { + + @Override + public void process() throws Exception { + DefaultContext context = this.getFirstContextBean(); + + if(context.hasData("index_str")){ + String indexStr = context.getData("index_str").toString(); + indexStr = StrUtil.format("{}[{}{}]", indexStr, this.getPreLoopIndex(), this.getLoopIndex()); + context.setData("index_str", indexStr); + }else{ + context.setData("index_str", StrUtil.format("[{}{}]", this.getPreLoopIndex(), this.getLoopIndex())); + } + + if(context.hasData("obj_str")){ + String objStr = context.getData("obj_str").toString(); + objStr = StrUtil.format("{}[{}{}]", objStr, this.getPreLoopObj(), this.getCurrLoopObj()); + context.setData("obj_str", objStr); + }else{ + context.setData("obj_str", StrUtil.format("[{}{}]", this.getPreLoopObj().toString(), this.getCurrLoopObj().toString())); + } + + System.out.println("ECmp executed!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/loop/LoopELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/loop/LoopELSpringbootTest.java index ac5916b09..afa0fb8fc 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/loop/LoopELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/loop/LoopELSpringbootTest.java @@ -108,4 +108,14 @@ public class LoopELSpringbootTest extends BaseTest { LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); Assertions.assertTrue(response.isSuccess()); } + + //FOR循环多层嵌套获取下标 + @Test + public void testLoop10() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + DefaultContext context = response.getFirstContextBean(); + String assertStr = context.getData("index_str").toString(); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("[000][001][010][011][020][021][100][101][110][111][120][121][200][201][210][211][220][221][300][301][310][311][320][321]", assertStr); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/loop/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/loop/cmp/CCmp.java index 9de22280d..bd613b5a2 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/loop/cmp/CCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/loop/cmp/CCmp.java @@ -7,7 +7,9 @@ */ package com.yomahub.liteflow.test.loop.cmp; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; import org.springframework.stereotype.Component; @Component("c") @@ -15,6 +17,16 @@ public class CCmp extends NodeComponent { @Override public void process() { + DefaultContext context = this.getFirstContextBean(); + + if(context.hasData("index_str")){ + String indexStr = context.getData("index_str").toString(); + indexStr = StrUtil.format("{}[{}{}{}]", indexStr, this.getPreNLoopIndex(2), this.getPreLoopIndex(), this.getLoopIndex()); + context.setData("index_str", indexStr); + }else{ + context.setData("index_str", StrUtil.format("[{}{}{}]", this.getPreNLoopIndex(2), this.getPreLoopIndex(), this.getLoopIndex())); + } + System.out.println("CCmp executed!"); } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/flow.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/flow.xml index a6015d40f..6c16656bb 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/flow.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/flow.xml @@ -18,4 +18,10 @@ ); + + + ITERATOR(x1).DO( + ITERATOR(x2).DO(e) + ); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/loop/flow.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/loop/flow.xml index 6dcdb59e4..f21788640 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/loop/flow.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/loop/flow.xml @@ -52,4 +52,8 @@ FOR(2).DO(THEN(c, c, c)); + + + FOR(4).DO(FOR(3).DO(FOR(2).DO(c))); + \ No newline at end of file