소스 검색

feature #I9T6PB 嵌套循环获得任意外层的下标或者对象

everywhere.z 9 달 전
부모
커밋
8c051a4290

+ 16 - 1
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> T getCurrLoopObj() {
 		return this.getRefNode().getCurrLoopObject();
 	}
 
+	public <T> T getPreLoopObj() {
+		return this.getRefNode().getPreLoopObject();
+	}
+
+	public <T> 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());

+ 93 - 11
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<Boolean> accessResult = new TransmittableThreadLocal<>();
 
 	// 循环下标
-	private TransmittableThreadLocal<Integer> loopIndexTL = new TransmittableThreadLocal<>();
+	private TransmittableThreadLocal<Stack<TupleOf2<Integer, Integer>>> loopIndexTL = new TransmittableThreadLocal<>();
 
 	// 迭代对象
-	private TransmittableThreadLocal<Object> currLoopObject = new TransmittableThreadLocal<>();
+	private TransmittableThreadLocal<Stack<TupleOf2<Integer, Object>>> loopObjectTL = new TransmittableThreadLocal<>();
 
 	// 当前slot的index
 	private TransmittableThreadLocal<Integer> 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<TupleOf2<Integer, Integer>> stack = new Stack<>();
+			TupleOf2<Integer, Integer> tuple = new TupleOf2<>(condition.hashCode(), index);
+			stack.push(tuple);
+			this.loopIndexTL.set(stack);
+		}else{
+			Stack<TupleOf2<Integer, Integer>> stack = this.loopIndexTL.get();
+			TupleOf2<Integer, Integer> thisConditionTuple =  stack.stream().filter(tuple -> tuple.getA().equals(condition.hashCode())).findFirst().orElse(null);
+			if (thisConditionTuple != null){
+				thisConditionTuple.setB(index);
+			}else{
+				TupleOf2<Integer, Integer> tuple = new TupleOf2<>(condition.hashCode(), index);
+				stack.push(tuple);
+			}
+		}
 	}
 
 	public Integer getLoopIndex() {
-		return this.loopIndexTL.get();
+		Stack<TupleOf2<Integer, Integer>> 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<TupleOf2<Integer, Integer>> 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<TupleOf2<Integer, Integer>> 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<TupleOf2<Integer, Object>> stack = new Stack<>();
+			TupleOf2<Integer, Object> tuple = new TupleOf2<>(condition.hashCode(), obj);
+			stack.push(tuple);
+			this.loopObjectTL.set(stack);
+		}else{
+			Stack<TupleOf2<Integer, Object>> stack = this.loopObjectTL.get();
+			TupleOf2<Integer, Object> thisConditionTuple =  stack.stream().filter(tuple -> tuple.getA().equals(condition.hashCode())).findFirst().orElse(null);
+			if (thisConditionTuple != null){
+				thisConditionTuple.setB(obj);
+			}else{
+				TupleOf2<Integer, Object> tuple = new TupleOf2<>(condition.hashCode(), obj);
+				stack.push(tuple);
+			}
+		}
 	}
 
 	public <T> T getCurrLoopObject() {
-		return (T) this.currLoopObject.get();
+		Stack<TupleOf2<Integer, Object>> stack = this.loopObjectTL.get();
+		if (stack != null){
+			return (T) stack.peek().getB();
+		}else{
+			return null;
+		}
+	}
+
+	public <T> T getPreLoopObject(){
+		return getPreNLoopObject(1);
+	}
+
+	public <T> T getPreNLoopObject(int n){
+		Stack<TupleOf2<Integer, Object>> 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<TupleOf2<Integer, Object>> 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<>();

+ 2 - 2
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);
         }
     }
 

+ 29 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/util/TupleOf2.java

@@ -0,0 +1,29 @@
+package com.yomahub.liteflow.util;
+
+public class TupleOf2<A, B> {
+
+    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;
+    }
+}

+ 12 - 0
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());
+	}
 }

+ 34 - 0
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!");
+	}
+}

+ 10 - 0
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);
+	}
 }

+ 12 - 0
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!");
 	}
 

+ 6 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/flow.xml

@@ -18,4 +18,10 @@
 
         );
     </chain>
+
+    <chain name="chain4">
+        ITERATOR(x1).DO(
+            ITERATOR(x2).DO(e)
+        );
+    </chain>
 </flow>

+ 4 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/loop/flow.xml

@@ -52,4 +52,8 @@
     <chain name="chain9">
         FOR(2).DO(THEN(c, c, c));
     </chain>
+
+    <chain name="chain10">
+        FOR(4).DO(FOR(3).DO(FOR(2).DO(c)));
+    </chain>
 </flow>