mirror of
https://gitee.com/dromara/liteFlow.git
synced 2026-05-14 12:12:08 +08:00
feature #I9T6PB 嵌套循环获得任意外层的下标或者对象
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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<>();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
|
||||
|
||||
@@ -18,4 +18,10 @@
|
||||
|
||||
);
|
||||
</chain>
|
||||
|
||||
<chain name="chain4">
|
||||
ITERATOR(x1).DO(
|
||||
ITERATOR(x2).DO(e)
|
||||
);
|
||||
</chain>
|
||||
</flow>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user