mirror of https://gitee.com/dromara/liteFlow
bug #IJX8AE 表达式级 bind 的数据,在流程执行结束后无法通过节点事后读取
This commit is contained in:
parent
0892cc6620
commit
68dc35ee92
|
|
@ -48,6 +48,12 @@ public class BindOperator extends BaseOperator<Executable> {
|
|||
if (override) {
|
||||
clearNodeBindData(condition, key);
|
||||
}
|
||||
// 将 bind 数据下放到该 Condition 直属(不跨子 chain 引用)的节点上,
|
||||
// 使流程执行结束后仍能通过 Node#getBindData(如 CmpStep.getRefNode().getBindData(...))读取到,
|
||||
// 与 2.15.1 的表现保持一致。
|
||||
// 注意:这里只下放到属于当前 chain 的节点克隆,不会递归进被引用的子 chain,
|
||||
// 从而避免污染被多个 chain 共享的子 chain(见 #ID7OTO / #IDCBQ2)。
|
||||
putBindDataToLocalNodes(condition, key, value, override);
|
||||
return condition;
|
||||
}
|
||||
|
||||
|
|
@ -75,4 +81,34 @@ public class BindOperator extends BaseOperator<Executable> {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 bind 数据下放到该 Condition 直属的节点上(递归进嵌套 Condition,但不跨子 chain 引用)。
|
||||
* <p>
|
||||
* 这些节点是当前 chain 构建时 clone 出来的、专属于本 chain 的实例(每个 clone 持有独立的
|
||||
* bindDataMap),因此写入 bind 数据不会污染被多个 chain 共享的子 chain。
|
||||
* 下放之后,流程执行结束仍可通过 {@code CmpStep.getRefNode().getBindData(key)} 读取到表达式级 bind 的值。
|
||||
* <p>
|
||||
* 对于条件中以 chainId 形式引用的子 chain({@link Chain} 类型,对象在 FlowBus 中被多个 chain 共享),
|
||||
* 这里不会写入其内部节点,其 bind 数据仍由运行时通过 Condition 调用栈解析,从而避免数据污染(见 #ID7OTO / #IDCBQ2)。
|
||||
*
|
||||
* @param condition 目标 Condition
|
||||
* @param key bind 数据的 key
|
||||
* @param value bind 数据的 value
|
||||
* @param override 为 true 时强制覆盖节点级已有的同 key 数据,否则保留节点级 bind(节点级优先)
|
||||
*/
|
||||
private void putBindDataToLocalNodes(Condition condition, String key, String value, boolean override) {
|
||||
condition.getExecutableGroup().values().forEach(executableList -> executableList.forEach(executable -> {
|
||||
if (executable instanceof Node) {
|
||||
Node node = (Node) executable;
|
||||
// 节点级 bind 优先:非 override 情况下,节点已有同 key 数据时不覆盖
|
||||
if (override || !node.hasBindData(key)) {
|
||||
node.putBindData(key, value);
|
||||
}
|
||||
} else if (executable instanceof Condition) {
|
||||
// 递归进嵌套 Condition(同属当前 chain);Chain 类型不进入此分支,避免污染共享子 chain
|
||||
putBindDataToLocalNodes((Condition) executable, key, value, override);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.bindData;
|
|||
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.flow.entity.CmpStep;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import com.yomahub.liteflow.test.BaseTest;
|
||||
import com.yomahub.liteflow.util.JsonUtil;
|
||||
|
|
@ -13,6 +14,8 @@ import org.springframework.context.annotation.ComponentScan;
|
|||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
|
|
@ -105,4 +108,36 @@ public class BindDataSpringbootTest1 extends BaseTest {
|
|||
Assertions.assertTrue(response.isSuccess());
|
||||
}
|
||||
|
||||
// 复现 issue #IJT2YA:表达式级 bind 的数据,执行结束后应能通过 Node#getBindData 读取到,
|
||||
// 且节点级 bind 不会被表达式级 bind 覆盖(a=v2, b=v1, c=v2)
|
||||
@Test
|
||||
public void testBind8() throws Exception {
|
||||
String el = "THEN(a, b.bind(\"k\", \"v1\"), c).bind(\"k\", \"v2\");";
|
||||
LiteflowResponse response = flowExecutor.execute2RespWithEL(el);
|
||||
Assertions.assertTrue(response.isSuccess());
|
||||
Map<String, String> resultMap = new HashMap<>();
|
||||
for (CmpStep cmpStep : response.getExecuteStepQueue()) {
|
||||
resultMap.put(cmpStep.getNodeId(), cmpStep.getRefNode().getBindData("k"));
|
||||
}
|
||||
Assertions.assertEquals("v2", resultMap.get("a"));
|
||||
Assertions.assertEquals("v1", resultMap.get("b"));
|
||||
Assertions.assertEquals("v2", resultMap.get("c"));
|
||||
}
|
||||
|
||||
// 回归保护:表达式级 bind 作用于含子 chain 引用的条件时,绝不能污染被共享的子 chain(#ID7OTO)
|
||||
@Test
|
||||
public void testBind9() throws Exception {
|
||||
// 带 bind 的 chain:运行时子 chain(psub)内的节点 a 应解析到 outer
|
||||
LiteflowResponse responseC = flowExecutor.execute2Resp("pchainC", "arg");
|
||||
DefaultContext ctxC = responseC.getFirstContextBean();
|
||||
Assertions.assertTrue(responseC.isSuccess());
|
||||
Assertions.assertEquals("outer", ctxC.getData("a"));
|
||||
|
||||
// 未 bind 的 chain 单独引用同一子 chain,绝不能被上面的 bind 污染
|
||||
LiteflowResponse responseB = flowExecutor.execute2Resp("pchainB", "arg");
|
||||
DefaultContext ctxB = responseB.getFirstContextBean();
|
||||
Assertions.assertTrue(responseB.isSuccess());
|
||||
Assertions.assertNull(ctxB.getData("a"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,4 +33,17 @@
|
|||
<chain id="chain7">
|
||||
THEN(a, b.bind("k1", "test_b"), c).bind("k1", "test", true);
|
||||
</chain>
|
||||
|
||||
<!-- 以下链路用于:表达式级 bind 作用于含子 chain 引用的条件时,绝不能污染被共享的子 chain(#ID7OTO 回归保护) -->
|
||||
<chain id="psub">
|
||||
THEN(a);
|
||||
</chain>
|
||||
|
||||
<chain id="pchainB">
|
||||
THEN(psub);
|
||||
</chain>
|
||||
|
||||
<chain id="pchainC">
|
||||
THEN(d, psub).bind("k1", "outer");
|
||||
</chain>
|
||||
</flow>
|
||||
Loading…
Reference in New Issue