feat: 完善用户确认节点选项与输出流转

- 重构确认节点单选多选配置及输出参数契约

- 统一管理端、用户中心、匿名分享和公共接口恢复流程

- 增加保存校验、错误契约及交互测试
This commit is contained in:
2026-09-04 14:55:55 +08:00
parent 65c85180c2
commit 0968e3bfa5
51 changed files with 2465 additions and 1591 deletions

View File

@@ -18,6 +18,7 @@ import tech.easyflow.ai.easyagentsflow.entity.WorkflowCheckStage;
import tech.easyflow.ai.easyagentsflow.service.TinyFlowService;
import tech.easyflow.ai.easyagentsflow.service.WorkflowCheckService;
import tech.easyflow.ai.easyagentsflow.service.WorkflowRunningParameterResolver;
import tech.easyflow.ai.easyagentsflow.service.WorkflowResumeService;
import tech.easyflow.ai.easyagentsflow.support.PublishedWorkflowDefinitionIds;
import tech.easyflow.ai.easyagentsflow.upload.WorkflowApiPreparedUpload;
import tech.easyflow.ai.easyagentsflow.upload.WorkflowApiUploadLifecycleService;
@@ -70,6 +71,8 @@ public class PublicWorkflowController {
@Resource
private WorkflowRunningParameterResolver workflowRunningParameterResolver;
@Resource
private WorkflowResumeService workflowResumeService;
@Resource
private WorkflowApiPermissionService workflowApiPermissionService;
@Resource
private WorkflowExecResultService workflowExecResultService;
@@ -250,14 +253,7 @@ public class PublicWorkflowController {
SysApiKey apiKey = workflowApiPermissionService.assertWorkflowApi(request.getHeader("ApiKey"), request.getRequestURI());
WorkflowExecResult execResult = assertApiKeyExecutionOwnership(apiKey, executeId);
assertWorkflowExecutionResumable(execResult);
if (!chainExecutor.resumeAsyncIfSuspended(
executeId,
confirmParams)) {
throw new BusinessException(
409,
40901,
"当前执行状态不可恢复,仅暂停中的工作流允许恢复");
}
workflowResumeService.resume(executeId, confirmParams);
return Result.ok();
}

View File

@@ -394,6 +394,7 @@ public final class WorkflowRunAsyncErrorProfile
*/
private boolean isStableBusinessCode(int code) {
return (code >= 40011 && code <= 40017)
|| code == 40031
|| (code >= 40101 && code <= 40103)
|| (code >= 40301 && code <= 40302)
|| (code >= 40401 && code <= 40402)
@@ -412,7 +413,8 @@ public final class WorkflowRunAsyncErrorProfile
* @return 对外 HTTP 状态
*/
private int normalizeHttpStatus(int code, int fallback) {
if (code >= 40011 && code <= 40017) {
if ((code >= 40011 && code <= 40017)
|| code == 40031) {
return 400;
}
if (code >= 40101 && code <= 40103) {

View File

@@ -12,6 +12,7 @@ import org.springframework.test.util.ReflectionTestUtils;
import tech.easyflow.ai.easyagentsflow.entity.ChainInfo;
import tech.easyflow.ai.easyagentsflow.entity.NodeInfo;
import tech.easyflow.ai.easyagentsflow.service.TinyFlowService;
import tech.easyflow.ai.easyagentsflow.service.WorkflowResumeService;
import tech.easyflow.ai.entity.Workflow;
import tech.easyflow.ai.entity.WorkflowExecResult;
import tech.easyflow.ai.enums.PublishStatus;
@@ -44,6 +45,7 @@ public class PublicWorkflowControllerBehaviorTest {
private PublicWorkflowController controller;
private ChainExecutor chainExecutor;
private WorkflowResumeService workflowResumeService;
private TinyFlowService tinyFlowService;
private HttpServletRequest request;
@@ -54,6 +56,7 @@ public class PublicWorkflowControllerBehaviorTest {
public void setUp() {
controller = new PublicWorkflowController();
chainExecutor = Mockito.mock(ChainExecutor.class);
workflowResumeService = Mockito.mock(WorkflowResumeService.class);
tinyFlowService = Mockito.mock(TinyFlowService.class);
WorkflowApiPermissionService permissionService =
Mockito.mock(WorkflowApiPermissionService.class);
@@ -79,6 +82,10 @@ public class PublicWorkflowControllerBehaviorTest {
controller,
"chainExecutor",
chainExecutor);
ReflectionTestUtils.setField(
controller,
"workflowResumeService",
workflowResumeService);
ReflectionTestUtils.setField(
controller,
"tinyFlowService",
@@ -108,10 +115,12 @@ public class PublicWorkflowControllerBehaviorTest {
public void resumeShouldRejectNonSuspendedExecution() {
when(request.getRequestURI()).thenReturn(
"/public-api/workflow/resume");
when(chainExecutor.resumeAsyncIfSuspended(
EXECUTE_ID,
Map.of("approved", true)))
.thenReturn(false);
Mockito.doThrow(new BusinessException(
409,
40901,
"当前执行状态不可恢复,仅暂停中的工作流允许恢复"))
.when(workflowResumeService)
.resume(EXECUTE_ID, Map.of("approved", true));
try {
controller.resume(
@@ -124,7 +133,7 @@ public class PublicWorkflowControllerBehaviorTest {
Assert.assertEquals(40901, exception.getErrorCode());
}
verify(chainExecutor).resumeAsyncIfSuspended(
verify(workflowResumeService).resume(
EXECUTE_ID,
Map.of("approved", true));
}

View File

@@ -167,6 +167,28 @@ public class WorkflowRunAsyncErrorProfileTest {
resolution.modelAndView.getModel().get("message"));
}
/**
* 验证确认节点恢复校验保留专用错误码,不回退为运行参数错误。
*/
@Test
public void shouldKeepResumeValidationCode() {
Resolution resolution = resolve(
"/public-api/workflow/resume",
MediaType.APPLICATION_JSON_VALUE,
new BusinessException(
400,
40031,
"确认参数[模板类型]包含未配置选项"));
Assert.assertEquals(400, resolution.response.getStatus());
Assert.assertEquals(
40031,
resolution.modelAndView.getModel().get("errorCode"));
Assert.assertEquals(
"确认参数[模板类型]包含未配置选项",
resolution.modelAndView.getModel().get("message"));
}
/**
* 验证 API Key 无效和两层权限错误保持可区分。
*/