fix: 兼容工作流开始节点字段化参数解析
- 为开始节点输入补齐裸参数名与节点作用域别名映射 - 文本模板优先解析扁平节点路径并补充回归测试
This commit is contained in:
@@ -62,6 +62,12 @@
|
|||||||
<version>77.1</version>
|
<version>77.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ public class Chain {
|
|||||||
|
|
||||||
if (variables != null && !variables.isEmpty()) {
|
if (variables != null && !variables.isEmpty()) {
|
||||||
state.getMemory().putAll(variables);
|
state.getMemory().putAll(variables);
|
||||||
|
applyStartParameterAliases(state.getMemory(), variables);
|
||||||
fields.add(ChainStateField.MEMORY);
|
fields.add(ChainStateField.MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,6 +246,48 @@ public class Chain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为开始节点输入参数补齐 `nodeId.paramName` 与 `paramName` 双向别名。
|
||||||
|
* 这样既兼容运行表单仅提交裸参数名,也兼容设计器内部统一保存完整引用路径。
|
||||||
|
*
|
||||||
|
* @param memory 流程内存
|
||||||
|
* @param variables 本次注入的变量
|
||||||
|
*/
|
||||||
|
public void applyStartParameterAliases(Map<String, Object> memory, Map<String, Object> variables) {
|
||||||
|
if (memory == null || variables == null || variables.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Node> startNodes = definition == null ? Collections.emptyList() : definition.getStartNodes();
|
||||||
|
if (startNodes == null || startNodes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (Node startNode : startNodes) {
|
||||||
|
if (startNode == null || StringUtil.noText(startNode.getId())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<Parameter> parameters = startNode.getParameters();
|
||||||
|
if (parameters == null || parameters.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Parameter parameter : parameters) {
|
||||||
|
if (parameter == null || parameter.getRefType() != RefType.INPUT || StringUtil.noText(parameter.getName())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String parameterName = parameter.getName().trim();
|
||||||
|
String scopedName = startNode.getId() + "." + parameterName;
|
||||||
|
Object plainValue = variables.get(parameterName);
|
||||||
|
Object scopedValue = variables.get(scopedName);
|
||||||
|
|
||||||
|
if (plainValue != null && !memory.containsKey(scopedName)) {
|
||||||
|
memory.put(scopedName, plainValue);
|
||||||
|
}
|
||||||
|
if (scopedValue != null && !memory.containsKey(parameterName)) {
|
||||||
|
memory.put(parameterName, scopedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void executeNode(Node node, Trigger trigger) {
|
public void executeNode(Node node, Trigger trigger) {
|
||||||
try {
|
try {
|
||||||
EXECUTION_THREAD_LOCAL.set(this);
|
EXECUTION_THREAD_LOCAL.set(this);
|
||||||
@@ -745,4 +788,4 @@ public class Chain {
|
|||||||
public void setStateInstanceId(String stateInstanceId) {
|
public void setStateInstanceId(String stateInstanceId) {
|
||||||
this.stateInstanceId = stateInstanceId;
|
this.stateInstanceId = stateInstanceId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ public class ChainExecutor {
|
|||||||
if (variables != null && !variables.isEmpty()) {
|
if (variables != null && !variables.isEmpty()) {
|
||||||
temp.updateStateSafely(s -> {
|
temp.updateStateSafely(s -> {
|
||||||
s.getMemory().putAll(variables);
|
s.getMemory().putAll(variables);
|
||||||
|
temp.applyStartParameterAliases(s.getMemory(), variables);
|
||||||
return EnumSet.of(ChainStateField.MEMORY);
|
return EnumSet.of(ChainStateField.MEMORY);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,14 @@ public class TextTemplate {
|
|||||||
*/
|
*/
|
||||||
private Object getValueByJsonPath(Map<String, Object> root, String path, boolean escapeForJsonOutput) {
|
private Object getValueByJsonPath(Map<String, Object> root, String path, boolean escapeForJsonOutput) {
|
||||||
try {
|
try {
|
||||||
|
Object directValue = MapUtil.getByPath(root, path);
|
||||||
|
if (directValue != null) {
|
||||||
|
if (escapeForJsonOutput && directValue instanceof String) {
|
||||||
|
return escapeJsonString((String) directValue);
|
||||||
|
}
|
||||||
|
return directValue;
|
||||||
|
}
|
||||||
|
|
||||||
String fullPath = path.startsWith("$") ? path : "$." + path;
|
String fullPath = path.startsWith("$") ? path : "$." + path;
|
||||||
JSONPath compiled = MapUtil.computeIfAbsent(JSONPATH_CACHE, fullPath, JSONPath::compile);
|
JSONPath compiled = MapUtil.computeIfAbsent(JSONPATH_CACHE, fullPath, JSONPath::compile);
|
||||||
Object value = compiled.eval(root);
|
Object value = compiled.eval(root);
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package com.easyagents.flow.core.test;
|
||||||
|
|
||||||
|
import com.easyagents.flow.core.chain.Chain;
|
||||||
|
import com.easyagents.flow.core.chain.ChainDefinition;
|
||||||
|
import com.easyagents.flow.core.chain.Parameter;
|
||||||
|
import com.easyagents.flow.core.chain.RefType;
|
||||||
|
import com.easyagents.flow.core.node.StartNode;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证开始节点输入参数在运行时会同时写入裸参数名与 `nodeId.paramName` 别名。
|
||||||
|
*/
|
||||||
|
public class ChainStartParameterAliasTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCreateScopedAliasFromPlainStartInput() {
|
||||||
|
Chain chain = createChain();
|
||||||
|
Map<String, Object> memory = new HashMap<>();
|
||||||
|
Map<String, Object> variables = new HashMap<>();
|
||||||
|
variables.put("user_input", "hello");
|
||||||
|
|
||||||
|
chain.applyStartParameterAliases(memory, variables);
|
||||||
|
|
||||||
|
Assert.assertEquals("hello", memory.get("start_1.user_input"));
|
||||||
|
Assert.assertNull(memory.get("user_input"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldCreatePlainAliasFromScopedStartInput() {
|
||||||
|
Chain chain = createChain();
|
||||||
|
Map<String, Object> memory = new HashMap<>();
|
||||||
|
Map<String, Object> variables = new HashMap<>();
|
||||||
|
variables.put("start_1.user_input", "hello");
|
||||||
|
|
||||||
|
chain.applyStartParameterAliases(memory, variables);
|
||||||
|
|
||||||
|
Assert.assertEquals("hello", memory.get("user_input"));
|
||||||
|
Assert.assertNull(memory.get("start_1.user_input"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Chain createChain() {
|
||||||
|
ChainDefinition definition = new ChainDefinition();
|
||||||
|
StartNode startNode = new StartNode();
|
||||||
|
startNode.setId("start_1");
|
||||||
|
|
||||||
|
Parameter parameter = new Parameter();
|
||||||
|
parameter.setName("user_input");
|
||||||
|
parameter.setRefType(RefType.INPUT);
|
||||||
|
startNode.setParameters(java.util.Collections.singletonList(parameter));
|
||||||
|
|
||||||
|
definition.addNode(startNode);
|
||||||
|
definition.setEdges(Collections.emptyList());
|
||||||
|
return new Chain(definition, "state_1");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.easyagents.flow.core.test;
|
||||||
|
|
||||||
|
import com.easyagents.flow.core.util.TextTemplate;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证文本模板能解析带点号的扁平 key,例如 `nodeId.paramName`。
|
||||||
|
*/
|
||||||
|
public class TextTemplatePathTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldResolveFlatScopedKeyBeforeJsonPathFallback() {
|
||||||
|
Map<String, Object> parameters = new HashMap<>();
|
||||||
|
parameters.put("node_1.user_input", "你好啊");
|
||||||
|
|
||||||
|
String result = TextTemplate.of("{{node_1.user_input}}").formatToString(parameters);
|
||||||
|
|
||||||
|
Assert.assertEquals("你好啊", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user