feat: 完善循环输出扁平聚合

- 校验并恢复循环输出的聚合策略

- 将最终类型并入参数值并优化同排开关交互
This commit is contained in:
2026-07-31 14:45:26 +08:00
parent 0754ad0792
commit fb08424cef
14 changed files with 504 additions and 59 deletions

View File

@@ -241,7 +241,9 @@ public class WorkflowExecutionAuditConsumer implements MQConsumerHandler {
"iterationCount"))
.intValue(),
String.valueOf(
map.get("outputName")));
map.get("outputName")),
Boolean.TRUE.equals(
map.get("flattenAggregation")));
}
Map<Object, Object> restored =
new LinkedHashMap<>();

View File

@@ -248,6 +248,7 @@ public class WorkflowCheckService {
for (NodeView node : nodes) {
checkConfiguredLoopCount(node, issues, issueKeys);
checkExplicitLoopInputs(node, issues, issueKeys);
checkLoopOutputAggregations(node, issues, issueKeys);
if (StringUtils.hasText(node.parentId)) {
NodeView parent = nodeMap.get(node.parentId);
if (parent != null && !TYPE_LOOP.equals(parent.type)) {
@@ -605,6 +606,51 @@ public class WorkflowCheckService {
true, 0, "Array<", 0, "Array<".length()));
}
/**
* 校验循环输出的扁平聚合只作用于数组引用。
*
* @param node 循环节点
* @param issues 问题列表
* @param issueKeys 问题去重键
*/
private void checkLoopOutputAggregations(
NodeView node,
List<WorkflowCheckIssue> issues,
Set<String> issueKeys) {
if (!TYPE_LOOP.equals(node.type) || node.data == null) {
return;
}
JSONArray outputDefs = node.data.getJSONArray("outputDefs");
if (outputDefs == null || outputDefs.isEmpty()) {
return;
}
for (int index = 0; index < outputDefs.size(); index++) {
JSONObject outputDef = outputDefs.getJSONObject(index);
if (outputDef == null
|| !outputDef.getBooleanValue("flattenAggregation")) {
continue;
}
String refType = trimToNull(outputDef.getString("refType"));
String ref = trimToNull(outputDef.getString("ref"));
String dataType = trimToNull(outputDef.getString("dataType"));
if ("ref".equals(refType)
&& StringUtils.hasText(ref)
&& isArrayDataType(dataType)) {
continue;
}
String outputName = trimToNull(outputDef.getString("name"));
addIssue(
issues,
issueKeys,
"LOOP_OUTPUT_FLATTEN_TYPE_INVALID",
"循环输出参数[" + safe(outputName)
+ "]启用扁平聚合时必须引用数组变量",
node.id,
null,
node.name);
}
}
/**
* 校验 index 和 loopItem 仅在所属循环体内引用。
*

View File

@@ -627,11 +627,11 @@ public class WorkflowExecutionAuditConsumerTest {
loopRepository.append(
resultId,
0,
Map.of("answer", "first"));
Map.of("answer", List.of("first", "second")));
loopRepository.append(
resultId,
1,
Map.of("answer", "second"));
Map.of("answer", List.of("third")));
WorkflowExecutionAuditConsumer consumer =
new WorkflowExecutionAuditConsumer(
resultService,
@@ -650,7 +650,8 @@ public class WorkflowExecutionAuditConsumerTest {
new LoopResultReference(
resultId,
2,
"answer"));
"answer",
true));
WorkflowExecStep incomingStep =
new WorkflowExecStep();
incomingStep.setExecKey("step-loop");
@@ -700,10 +701,10 @@ public class WorkflowExecutionAuditConsumerTest {
resultCaptor.getValue().getOutput(),
Map.class);
Assert.assertEquals(
List.of("first", "second"),
List.of("first", "second", "third"),
stepOutput.get("answers"));
Assert.assertEquals(
List.of("first", "second"),
List.of("first", "second", "third"),
resultOutput.get("answers"));
}

View File

@@ -221,6 +221,55 @@ public class WorkflowCheckServiceTest {
assertHasCode(result, "EXPLICIT_LOOP_ITEMS_TYPE_INVALID");
}
/**
* 验证循环数组输出允许启用扁平聚合。
*/
@Test
public void testSaveShouldPassArrayLoopOutputFlattenAggregation() throws Exception {
WorkflowCheckService service = newService(new HashMap<>());
JSONObject loopData = loopData(
fixedParameter("count", "2", "Number"), null);
JSONObject output = refParameter(
"res",
"knowledge.documents.content",
"Array<String>");
output.put("flattenAggregation", true);
loopData.put("outputDefs", array(output));
String content = workflowJson(
array(node("loop-1", "loopNode", null, loopData)),
new JSONArray());
WorkflowCheckResult result = service.checkContent(
content, WorkflowCheckStage.SAVE, null);
Assert.assertTrue(result.isPassed());
}
/**
* 验证循环标量输出不能启用扁平聚合。
*/
@Test
public void testSaveShouldBlockScalarLoopOutputFlattenAggregation() throws Exception {
WorkflowCheckService service = newService(new HashMap<>());
JSONObject loopData = loopData(
fixedParameter("count", "2", "Number"), null);
JSONObject output = refParameter(
"res",
"child.output",
"String");
output.put("flattenAggregation", true);
loopData.put("outputDefs", array(output));
String content = workflowJson(
array(node("loop-1", "loopNode", null, loopData)),
new JSONArray());
WorkflowCheckResult result = service.checkContent(
content, WorkflowCheckStage.SAVE, null);
Assert.assertFalse(result.isPassed());
assertHasCode(result, "LOOP_OUTPUT_FLATTEN_TYPE_INVALID");
}
/**
* 验证新旧循环输入不能同时提交。
*/