mirror of
https://gitee.com/dromara/liteFlow.git
synced 2026-06-13 19:18:16 +08:00
bug #ID7OTO bind对象为chain时,chain的定义顺序影响了bind数据的获取
重写了整个底层的parser逻辑
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
package com.yomahub.liteflow.builder.el.operator;
|
||||
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
|
||||
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
|
||||
import com.yomahub.liteflow.flow.element.Chain;
|
||||
import com.yomahub.liteflow.flow.element.Condition;
|
||||
import com.yomahub.liteflow.flow.element.Executable;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.flow.element.condition.ChainBindWrapperCondition;
|
||||
import com.yomahub.liteflow.meta.LiteflowMetaOperator;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* EL规则中的bind的操作符
|
||||
*
|
||||
@@ -27,20 +26,53 @@ public class BindOperator extends BaseOperator<Executable> {
|
||||
|
||||
String value = OperatorHelper.convert(objects[2], String.class);
|
||||
|
||||
AtomicBoolean override = new AtomicBoolean(false);
|
||||
// 获取 override 参数(第四个参数,默认为 false)
|
||||
boolean override = false;
|
||||
if (objects.length > 3) {
|
||||
override.set(OperatorHelper.convert(objects[3], Boolean.class));
|
||||
}
|
||||
if (bindItem instanceof Node){
|
||||
override.set(true);
|
||||
override = OperatorHelper.convert(objects[3], Boolean.class);
|
||||
}
|
||||
|
||||
LiteflowMetaOperator.getNodes(bindItem).forEach(node -> {
|
||||
if (BooleanUtil.isFalse(node.hasBindData(key)) || override.get()){
|
||||
node.putBindData(key, value);
|
||||
// 场景1:对 Node bind(保持现有逻辑,bind 数据存在 Node 上)
|
||||
if (bindItem instanceof Node) {
|
||||
Node node = (Node) bindItem;
|
||||
node.putBindData(key, value);
|
||||
return node;
|
||||
}
|
||||
|
||||
// 场景2:对 Condition bind(如 THEN(...).bind(...)),bind 数据存在 Condition 上
|
||||
if (bindItem instanceof Condition) {
|
||||
Condition condition = (Condition) bindItem;
|
||||
condition.putBindData(key, value);
|
||||
// 如果 override=true,需要清除该 Condition 下所有 Node 上相同 key 的 bind 数据
|
||||
// 这样可以确保 Condition 级别的 bind 能够覆盖 Node 级别的 bind
|
||||
if (override) {
|
||||
clearNodeBindData(condition, key);
|
||||
}
|
||||
});
|
||||
return condition;
|
||||
}
|
||||
|
||||
// 场景3:对 Chain bind(新逻辑:包装成 ChainBindWrapperCondition)
|
||||
// 这样不会修改 Chain 本身,而是创建一个包装 Condition 来持有 bind 数据
|
||||
// 从而避免多个 chain 引用同一个子 chain 时的 bind 数据污染问题
|
||||
if (bindItem instanceof Chain) {
|
||||
Chain chain = (Chain) bindItem;
|
||||
ChainBindWrapperCondition wrapper = new ChainBindWrapperCondition(chain);
|
||||
wrapper.putBindData(key, value);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
return bindItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除 Condition 下所有 Node 上指定 key 的 bind 数据
|
||||
* 用于 override=true 时,确保 Condition 级别的 bind 能够覆盖 Node 级别的 bind
|
||||
*/
|
||||
private void clearNodeBindData(Condition condition, String key) {
|
||||
LiteflowMetaOperator.getNodes(condition).forEach(node -> {
|
||||
if (node.hasBindData(key)) {
|
||||
node.removeBindData(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||
import com.yomahub.liteflow.exception.ObjectConvertException;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.flow.element.Condition;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.flow.entity.CmpStep;
|
||||
import com.yomahub.liteflow.flow.executor.DefaultNodeExecutor;
|
||||
@@ -442,7 +443,23 @@ public abstract class NodeComponent{
|
||||
}
|
||||
|
||||
public <T> T getBindData(String key, Class<T> clazz) {
|
||||
String bindData = getRefNode().getBindData(key);
|
||||
String bindData = null;
|
||||
|
||||
// 第一步:先从 Node 级别查找(node.bind(...) 场景,优先级最高)
|
||||
bindData = getRefNode().getBindData(key);
|
||||
|
||||
// 第二步:如果 Node 级别没有找到,从 Condition 栈中查找(从栈顶向下遍历)
|
||||
// 这样可以正确处理 chain.bind(...) 和 THEN(...).bind(...) 的场景
|
||||
if (StrUtil.isBlank(bindData)) {
|
||||
Slot slot = this.getSlot();
|
||||
for (Condition condition : slot.getConditionStack()) {
|
||||
if (condition.hasBindData(key)) {
|
||||
bindData = condition.getBindData(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(bindData)) {
|
||||
return null;
|
||||
}
|
||||
@@ -474,7 +491,22 @@ public abstract class NodeComponent{
|
||||
}
|
||||
|
||||
public <T> List<T> getBindDataList(String key, Class<T> clazz) {
|
||||
String bindData = getRefNode().getBindData(key);
|
||||
String bindData = null;
|
||||
|
||||
// 第一步:先从 Node 级别查找(node.bind(...) 场景,优先级最高)
|
||||
bindData = getRefNode().getBindData(key);
|
||||
|
||||
// 第二步:如果 Node 级别没有找到,从 Condition 栈中查找(从栈顶向下遍历)
|
||||
if (StrUtil.isBlank(bindData)) {
|
||||
Slot slot = this.getSlot();
|
||||
for (Condition condition : slot.getConditionStack()) {
|
||||
if (condition.hasBindData(key)) {
|
||||
bindData = condition.getBindData(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(bindData)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ public enum ConditionTypeEnum {
|
||||
|
||||
TYPE_NOT_OPT("not_opt", "not_opt"),
|
||||
|
||||
TYPE_ABSTRACT("abstract", "abstract");
|
||||
TYPE_ABSTRACT("abstract", "abstract"),
|
||||
|
||||
TYPE_CHAIN_BIND_WRAPPER("chain_bind_wrapper", "chain_bind_wrapper");
|
||||
|
||||
private String type;
|
||||
|
||||
|
||||
@@ -49,6 +49,12 @@ public abstract class Condition implements Executable{
|
||||
*/
|
||||
private String currChainId;
|
||||
|
||||
/**
|
||||
* bind 数据存储,用于存储在该 Condition 上的 bind 数据
|
||||
* 运行时 node 会通过 Condition 栈向上查找 bind 数据
|
||||
*/
|
||||
private Map<String, String> bindDataMap = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void execute(Integer slotIndex) throws Exception {
|
||||
Slot slot = DataBus.getSlot(slotIndex);
|
||||
@@ -191,4 +197,31 @@ public abstract class Condition implements Executable{
|
||||
public Map<String, List<Executable>> getExecutableGroup() {
|
||||
return executableGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 bind 数据
|
||||
* @param key bind 数据的 key
|
||||
* @param value bind 数据的 value
|
||||
*/
|
||||
public void putBindData(String key, String value) {
|
||||
this.bindDataMap.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 bind 数据
|
||||
* @param key bind 数据的 key
|
||||
* @return bind 数据的 value
|
||||
*/
|
||||
public String getBindData(String key) {
|
||||
return this.bindDataMap.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否存在指定 key 的 bind 数据
|
||||
* @param key bind 数据的 key
|
||||
* @return 是否存在
|
||||
*/
|
||||
public boolean hasBindData(String key) {
|
||||
return this.bindDataMap.containsKey(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,6 +522,10 @@ public class Node implements Executable, Cloneable, Rollbackable{
|
||||
return this.bindDataMap.get(key);
|
||||
}
|
||||
|
||||
public void removeBindData(String key) {
|
||||
this.bindDataMap.remove(key);
|
||||
}
|
||||
|
||||
public Object getStepData(){
|
||||
return this.stepDataTL.get();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.yomahub.liteflow.flow.element.condition;
|
||||
|
||||
import com.yomahub.liteflow.enums.ConditionTypeEnum;
|
||||
import com.yomahub.liteflow.flow.element.Chain;
|
||||
import com.yomahub.liteflow.flow.element.Condition;
|
||||
|
||||
/**
|
||||
* Chain bind 包装 Condition
|
||||
* 用于在对 Chain 进行 bind 操作时,创建一个包装 Condition 来持有 bind 数据,
|
||||
* 而不是直接修改 Chain 内部的 Node,从而避免多个 chain 引用同一个子 chain 时的 bind 数据污染问题。
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.15.3
|
||||
*/
|
||||
public class ChainBindWrapperCondition extends Condition {
|
||||
|
||||
private final Chain wrappedChain;
|
||||
|
||||
public ChainBindWrapperCondition(Chain chain) {
|
||||
this.wrappedChain = chain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeCondition(Integer slotIndex) throws Exception {
|
||||
// 设置当前 chainId
|
||||
wrappedChain.setCurrChainId(this.getCurrChainId());
|
||||
// 执行被包装的 chain
|
||||
wrappedChain.execute(slotIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionTypeEnum getConditionType() {
|
||||
return ConditionTypeEnum.TYPE_CHAIN_BIND_WRAPPER;
|
||||
}
|
||||
|
||||
public Chain getWrappedChain() {
|
||||
return wrappedChain;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "chain_bind_wrapper_" + wrappedChain.getChainId();
|
||||
}
|
||||
}
|
||||
@@ -316,6 +316,15 @@ public class Slot {
|
||||
conditionStack.get().pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Condition 调用栈
|
||||
* 用于在运行时向上查找 bind 数据
|
||||
* @return Condition 栈
|
||||
*/
|
||||
public Deque<Condition> getConditionStack() {
|
||||
return conditionStack.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 请使用 {@link #setChainId(String)}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user