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.date.StopWatch;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
|
||||||
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
|
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
|
||||||
import com.yomahub.liteflow.enums.CmpStepTypeEnum;
|
import com.yomahub.liteflow.enums.CmpStepTypeEnum;
|
||||||
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||||
@@ -419,10 +418,26 @@ public abstract class NodeComponent{
|
|||||||
return this.getRefNode().getLoopIndex();
|
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() {
|
public <T> T getCurrLoopObj() {
|
||||||
return this.getRefNode().getCurrLoopObject();
|
return this.getRefNode().getCurrLoopObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> T getPreLoopObj() {
|
||||||
|
return this.getRefNode().getPreLoopObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T getPreNLoopObj(int n) {
|
||||||
|
return this.getRefNode().getPreNLoopObject(n);
|
||||||
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void invoke(String chainId, Object param) throws Exception {
|
public void invoke(String chainId, Object param) throws Exception {
|
||||||
FlowExecutorHolder.loadInstance().invoke(chainId, param, this.getSlotIndex());
|
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.enums.NodeTypeEnum;
|
||||||
import com.yomahub.liteflow.exception.ChainEndException;
|
import com.yomahub.liteflow.exception.ChainEndException;
|
||||||
import com.yomahub.liteflow.exception.FlowSystemException;
|
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.NodeExecutor;
|
||||||
import com.yomahub.liteflow.flow.executor.NodeExecutorHelper;
|
import com.yomahub.liteflow.flow.executor.NodeExecutorHelper;
|
||||||
import com.yomahub.liteflow.log.LFLog;
|
import com.yomahub.liteflow.log.LFLog;
|
||||||
import com.yomahub.liteflow.log.LFLoggerManager;
|
import com.yomahub.liteflow.log.LFLoggerManager;
|
||||||
import com.yomahub.liteflow.slot.DataBus;
|
import com.yomahub.liteflow.slot.DataBus;
|
||||||
import com.yomahub.liteflow.slot.Slot;
|
import com.yomahub.liteflow.slot.Slot;
|
||||||
|
import com.yomahub.liteflow.util.TupleOf2;
|
||||||
|
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Node节点,实现可执行器 Node节点并不是单例的,每构建一次都会copy出一个新的实例
|
* Node节点,实现可执行器 Node节点并不是单例的,每构建一次都会copy出一个新的实例
|
||||||
@@ -60,10 +64,10 @@ public class Node implements Executable, Cloneable, Rollbackable{
|
|||||||
private TransmittableThreadLocal<Boolean> accessResult = new TransmittableThreadLocal<>();
|
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
|
// 当前slot的index
|
||||||
private TransmittableThreadLocal<Integer> slotIndexTL = new TransmittableThreadLocal<>();
|
private TransmittableThreadLocal<Integer> slotIndexTL = new TransmittableThreadLocal<>();
|
||||||
@@ -290,28 +294,106 @@ public class Node implements Executable, Cloneable, Rollbackable{
|
|||||||
this.isContinueOnErrorResult.remove();
|
this.isContinueOnErrorResult.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoopIndex(int index) {
|
public void setLoopIndex(LoopCondition condition, int index) {
|
||||||
this.loopIndexTL.set(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() {
|
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() {
|
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) {
|
public void setCurrLoopObject(LoopCondition condition, Object obj) {
|
||||||
this.currLoopObject.set(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() {
|
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() {
|
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(){
|
public Integer getSlotIndex(){
|
||||||
@@ -355,7 +437,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
|
|||||||
public Node clone() throws CloneNotSupportedException {
|
public Node clone() throws CloneNotSupportedException {
|
||||||
Node node = (Node)super.clone();
|
Node node = (Node)super.clone();
|
||||||
node.loopIndexTL = new TransmittableThreadLocal<>();
|
node.loopIndexTL = new TransmittableThreadLocal<>();
|
||||||
node.currLoopObject = new TransmittableThreadLocal<>();
|
node.loopObjectTL = new TransmittableThreadLocal<>();
|
||||||
node.accessResult = new TransmittableThreadLocal<>();
|
node.accessResult = new TransmittableThreadLocal<>();
|
||||||
node.slotIndexTL = new TransmittableThreadLocal<>();
|
node.slotIndexTL = new TransmittableThreadLocal<>();
|
||||||
node.isEndTL = new TransmittableThreadLocal<>();
|
node.isEndTL = new TransmittableThreadLocal<>();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ public abstract class LoopCondition extends Condition {
|
|||||||
((Condition) executableItem).getExecutableGroup()
|
((Condition) executableItem).getExecutableGroup()
|
||||||
.forEach((key, value) -> value.forEach(executable -> setLoopIndex(executable, index)));
|
.forEach((key, value) -> value.forEach(executable -> setLoopIndex(executable, index)));
|
||||||
} else if (executableItem instanceof Node) {
|
} 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()
|
((Condition) executableItem).getExecutableGroup()
|
||||||
.forEach((key, value) -> value.forEach(executable -> setCurrLoopObject(executable, obj)));
|
.forEach((key, value) -> value.forEach(executable -> setCurrLoopObject(executable, obj)));
|
||||||
} else if (executableItem instanceof Node) {
|
} 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");
|
LiteflowResponse response = flowExecutor.execute2Resp("chain3");
|
||||||
Assertions.assertTrue(response.isSuccess());
|
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");
|
LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg");
|
||||||
Assertions.assertTrue(response.isSuccess());
|
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;
|
package com.yomahub.liteflow.test.loop.cmp;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.yomahub.liteflow.core.NodeComponent;
|
import com.yomahub.liteflow.core.NodeComponent;
|
||||||
|
import com.yomahub.liteflow.slot.DefaultContext;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
@Component("c")
|
@Component("c")
|
||||||
@@ -15,6 +17,16 @@ public class CCmp extends NodeComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() {
|
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!");
|
System.out.println("CCmp executed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,10 @@
|
|||||||
|
|
||||||
);
|
);
|
||||||
</chain>
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain4">
|
||||||
|
ITERATOR(x1).DO(
|
||||||
|
ITERATOR(x2).DO(e)
|
||||||
|
);
|
||||||
|
</chain>
|
||||||
</flow>
|
</flow>
|
||||||
@@ -52,4 +52,8 @@
|
|||||||
<chain name="chain9">
|
<chain name="chain9">
|
||||||
FOR(2).DO(THEN(c, c, c));
|
FOR(2).DO(THEN(c, c, c));
|
||||||
</chain>
|
</chain>
|
||||||
|
|
||||||
|
<chain name="chain10">
|
||||||
|
FOR(4).DO(FOR(3).DO(FOR(2).DO(c)));
|
||||||
|
</chain>
|
||||||
</flow>
|
</flow>
|
||||||
Reference in New Issue
Block a user