feat(compat): 补齐文档草案接口兼容入口

This commit is contained in:
2026-06-01 06:15:44 +08:00
parent 73237507e9
commit d5d239ae3a
14 changed files with 441 additions and 6 deletions

View File

@@ -8,6 +8,7 @@ import com.bruce.agent.dto.response.AgentDefinitionResponse;
import com.bruce.agent.service.IAgentDefinitionService;
import com.bruce.common.domain.model.RequestResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@@ -18,6 +19,7 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/agents")
@RequiredArgsConstructor
@@ -55,4 +57,15 @@ public class AgentDefinitionController {
@RequestBody AgentChatRequest request) {
return RequestResult.success(agentDefinitionService.chat(agentId, request));
}
/**
* 兼容前端实现文档中的运行入口路径。
*/
@PostMapping("/{agentId}/runs")
public RequestResult<AgentChatResponse> run(@PathVariable("agentId") Long agentId,
@RequestBody AgentChatRequest request) {
log.info("Agent运行入口开始agentId={}, messageCount={}",
agentId, request.getMessages() == null ? 0 : request.getMessages().size());
return RequestResult.success(agentDefinitionService.chat(agentId, request));
}
}

View File

@@ -48,6 +48,24 @@ public class AgentSessionController {
return RequestResult.success(agentSessionService.getDetailById(id));
}
/**
* 兼容前端实现文档中的资源化会话详情路径。
*/
@GetMapping("/{sessionId}")
public RequestResult<AgentSessionDetailVO> detailByPath(@PathVariable("sessionId") Long sessionId) {
log.info("Agent会话详情按路径查询开始sessionId={}", sessionId);
return RequestResult.success(agentSessionService.getDetailById(sessionId));
}
/**
* 兼容前端实现文档中的 Agent 会话列表路径。
*/
@GetMapping("/agents/{agentId}/sessions")
public RequestResult<List<AgentSessionDetailVO>> sessionsByAgent(@PathVariable("agentId") Long agentId) {
log.info("Agent会话列表查询开始agentId={}", agentId);
return RequestResult.success(agentSessionService.listByAgentId(agentId));
}
@GetMapping("/{sessionId}/messages")
public RequestResult<List<AgentMessageVO>> messages(@PathVariable("sessionId") Long sessionId) {
log.info("Agent消息列表查询开始sessionId={}", sessionId);

View File

@@ -0,0 +1,121 @@
package com.bruce.agent.controller;
import com.bruce.agent.dto.response.AgentChatResponse;
import com.bruce.agent.service.IAgentDefinitionService;
import com.bruce.agent.service.IAgentMessageService;
import com.bruce.agent.service.IAgentSessionService;
import com.bruce.agent.service.IAgentWorkspaceService;
import com.bruce.agent.vo.AgentSessionDetailVO;
import com.bruce.common.handler.GlobalExceptionHandler;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 验证 Agent 文档草案兼容路径。
*/
@ExtendWith(MockitoExtension.class)
class AgentCompatControllerTests {
private MockMvc mockMvc;
@Mock
private IAgentDefinitionService agentDefinitionService;
@Mock
private IAgentSessionService agentSessionService;
@Mock
private IAgentMessageService agentMessageService;
@Mock
private IAgentWorkspaceService agentWorkspaceService;
@InjectMocks
private AgentDefinitionController agentDefinitionController;
@InjectMocks
private AgentSessionController agentSessionController;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(agentDefinitionController, agentSessionController)
.setControllerAdvice(new GlobalExceptionHandler())
.build();
}
@Test
void agentRunCompatShouldReturnChatResponse() throws Exception {
AgentChatResponse response = new AgentChatResponse();
response.setAgentId(1001L);
response.setAgentCode("presale_agent");
response.setAnswer("这是兼容运行入口返回的答案");
response.setModelRequestId("req-1001");
when(agentDefinitionService.chat(org.mockito.ArgumentMatchers.eq(1001L), any())).thenReturn(response);
mockMvc.perform(post("/api/agents/1001/runs")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"messages": [
{
"role": "user",
"content": "请总结合同重点"
}
],
"ragEnabled": true
}
"""))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data.agentCode").value("presale_agent"))
.andExpect(jsonPath("$.data.modelRequestId").value("req-1001"));
}
@Test
void sessionsCompatShouldReturnStructuredSessionList() throws Exception {
AgentSessionDetailVO session = new AgentSessionDetailVO();
session.setId(2001L);
session.setAgentId(1001L);
session.setSessionCode("session_001");
session.setStatus("ACTIVE");
when(agentSessionService.listByAgentId(1001L)).thenReturn(List.of(session));
mockMvc.perform(get("/api/agent-sessions/agents/1001/sessions"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data[0].sessionCode").value("session_001"));
}
@Test
void sessionDetailCompatShouldReturnStructuredDetail() throws Exception {
AgentSessionDetailVO session = new AgentSessionDetailVO();
session.setId(2001L);
session.setSessionCode("session_001");
session.setStatus("ACTIVE");
when(agentSessionService.getDetailById(2001L)).thenReturn(session);
mockMvc.perform(get("/api/agent-sessions/2001"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data.sessionCode").value("session_001"));
}
}

View File

@@ -7,8 +7,8 @@ import com.bruce.mcp.service.IMcpCapabilityService;
import com.bruce.mcp.service.IMcpImportService;
import com.bruce.mcp.service.IMcpServerService;
import com.bruce.mcp.service.IMcpWorkspaceService;
import com.bruce.mcp.vo.McpCapabilityVO;
import com.bruce.mcp.vo.McpServerVO;
import com.bruce.mcp.vo.McpCapabilityVO;
import com.bruce.mcp.vo.McpWorkspaceVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -50,6 +50,15 @@ public class McpImportController {
return RequestResult.success(mcpServerService.listServers());
}
/**
* 兼容前端实现文档中的 query 路径。
*/
@PostMapping("/servers/query")
public RequestResult<List<McpServerVO>> queryServers() {
log.info("MCP服务列表兼容查询开始");
return RequestResult.success(mcpServerService.listServers());
}
@GetMapping("/servers/{serverId}/capabilities")
public RequestResult<List<McpCapabilityVO>> listCapabilities(@PathVariable("serverId") Long serverId) {
log.info("MCP能力列表查询开始serverId={}", serverId);

View File

@@ -5,6 +5,7 @@ import com.bruce.mcp.service.IMcpCapabilityService;
import com.bruce.mcp.service.IMcpImportService;
import com.bruce.mcp.service.IMcpServerService;
import com.bruce.mcp.service.IMcpWorkspaceService;
import com.bruce.mcp.vo.McpServerVO;
import com.bruce.mcp.vo.McpWorkspaceVO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -17,6 +18,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -68,4 +70,19 @@ class McpImportControllerTests {
.andExpect(jsonPath("$.data.serverCode").value("jira_server"))
.andExpect(jsonPath("$.data.healthStatus").value("HEALTHY"));
}
@Test
void queryServersCompatShouldReturnStructuredServerList() throws Exception {
McpServerVO server = new McpServerVO();
server.setId(301L);
server.setServerCode("jira_server");
server.setServerName("Jira 服务");
when(mcpServerService.listServers()).thenReturn(java.util.List.of(server));
mockMvc.perform(post("/api/mcp/servers/query"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data[0].serverCode").value("jira_server"));
}
}

View File

@@ -11,6 +11,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@@ -41,6 +42,16 @@ public class SkillWorkspaceController {
return RequestResult.success(skillVersionService.saveDraft(skillCode, request));
}
/**
* 兼容前端实现文档中的 PUT 草稿保存路径。
*/
@PutMapping("/{skillCode}/draft")
public RequestResult<Boolean> saveDraftCompat(@PathVariable("skillCode") String skillCode,
@RequestBody SkillVersionSaveDTO request) {
log.info("Skill草稿兼容保存开始skillCode={}, versionNo={}", skillCode, request.getVersionNo());
return RequestResult.success(skillVersionService.saveDraft(skillCode, request));
}
@PostMapping("/{skillCode}/test")
public RequestResult<SkillVersionVO> test(@PathVariable("skillCode") String skillCode,
@RequestBody SkillVersionSaveDTO request) {

View File

@@ -1,6 +1,7 @@
package com.bruce.skill.controller;
import com.bruce.common.handler.GlobalExceptionHandler;
import com.bruce.skill.dto.SkillVersionSaveDTO;
import com.bruce.skill.service.ISkillVersionService;
import com.bruce.skill.service.ISkillWorkspaceService;
import com.bruce.skill.vo.SkillWorkspaceVO;
@@ -15,6 +16,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -59,4 +61,22 @@ class SkillWorkspaceControllerTests {
.andExpect(jsonPath("$.data.skillCode").value("resume_extract"))
.andExpect(jsonPath("$.data.status").value("PUBLISHED"));
}
@Test
void saveDraftCompatShouldReturnSuccess() throws Exception {
when(skillVersionService.saveDraft(org.mockito.ArgumentMatchers.eq("resume_extract"),
org.mockito.ArgumentMatchers.any(SkillVersionSaveDTO.class))).thenReturn(true);
mockMvc.perform(put("/api/skills/resume_extract/draft")
.contentType(org.springframework.http.MediaType.APPLICATION_JSON)
.content("""
{
"versionNo": 2,
"promptText": "提取候选人经历"
}
"""))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data").value(true));
}
}

View File

@@ -5,7 +5,9 @@ import com.bruce.workflow.dto.WorkflowDefinitionSaveDTO;
import com.bruce.workflow.service.IWorkflowDefinitionService;
import com.bruce.workflow.vo.WorkflowDefinitionVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -14,6 +16,7 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/workflows")
@RequiredArgsConstructor
@@ -33,8 +36,26 @@ public class WorkflowDefinitionController {
return RequestResult.success(workflowDefinitionService.getDefinition(id));
}
/**
* 兼容前端实现文档中的 REST 详情路径。
*/
@GetMapping("/{workflowId}")
public RequestResult<WorkflowDefinitionVO> detailByPath(@PathVariable("workflowId") Long workflowId) {
log.info("Workflow定义详情查询开始workflowId={}", workflowId);
return RequestResult.success(workflowDefinitionService.getDefinition(workflowId));
}
@PostMapping("/definition/save")
public RequestResult<Boolean> save(@RequestBody WorkflowDefinitionSaveDTO request) {
return RequestResult.success(workflowDefinitionService.saveDefinition(request));
}
/**
* 兼容前端实现文档中的草稿保存路径。
*/
@PostMapping("/save-draft")
public RequestResult<Boolean> saveDraft(@RequestBody WorkflowDefinitionSaveDTO request) {
log.info("Workflow草稿保存开始workflowId={}, workflowCode={}", request.getId(), request.getWorkflowCode());
return RequestResult.success(workflowDefinitionService.saveDefinition(request));
}
}

View File

@@ -5,6 +5,7 @@ import com.bruce.workflow.dto.WorkflowRunCreateDTO;
import com.bruce.workflow.service.IWorkflowRunService;
import com.bruce.workflow.vo.WorkflowRunVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/workflow-runs")
@RequiredArgsConstructor
@@ -30,4 +32,25 @@ public class WorkflowRunController {
public RequestResult<List<WorkflowRunVO>> list(@PathVariable("workflowId") Long workflowId) {
return RequestResult.success(workflowRunService.listRecentByWorkflowId(workflowId));
}
/**
* 兼容前端实现文档中的资源化运行创建路径。
*/
@PostMapping("/compat/workflows/{workflowId}/runs")
public RequestResult<Boolean> createCompat(@PathVariable("workflowId") Long workflowId,
@RequestBody WorkflowRunCreateDTO request) {
request.setWorkflowId(workflowId);
log.info("Workflow运行创建开始workflowId={}, versionId={}, requestId={}",
workflowId, request.getWorkflowVersionId(), request.getRequestId());
return RequestResult.success(workflowRunService.createRun(request));
}
/**
* 兼容前端实现文档中的按运行ID查询路径。
*/
@GetMapping("/compat/workflows/runs/{runId}")
public RequestResult<WorkflowRunVO> detailCompat(@PathVariable("runId") Long runId) {
log.info("Workflow运行详情查询开始runId={}", runId);
return RequestResult.success(workflowRunService.getRunById(runId));
}
}

View File

@@ -5,6 +5,7 @@ import com.bruce.workflow.dto.WorkflowVersionSaveDTO;
import com.bruce.workflow.service.IWorkflowVersionService;
import com.bruce.workflow.vo.WorkflowVersionVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/workflow-versions")
@RequiredArgsConstructor
@@ -30,4 +32,15 @@ public class WorkflowVersionController {
public RequestResult<Boolean> publish(@RequestBody WorkflowVersionSaveDTO request) {
return RequestResult.success(workflowVersionService.publishVersion(request));
}
/**
* 兼容前端实现文档中的资源化发布路径。
*/
@PostMapping("/compat/workflows/{workflowId}/publish")
public RequestResult<Boolean> publishCompat(@PathVariable("workflowId") Long workflowId,
@RequestBody WorkflowVersionSaveDTO request) {
request.setWorkflowId(workflowId);
log.info("Workflow版本发布开始workflowId={}, versionNo={}", workflowId, request.getVersionNo());
return RequestResult.success(workflowVersionService.publishVersion(request));
}
}

View File

@@ -12,4 +12,6 @@ public interface IWorkflowRunService extends IService<WorkflowRun> {
boolean createRun(WorkflowRunCreateDTO request);
List<WorkflowRunVO> listRecentByWorkflowId(Long workflowId);
WorkflowRunVO getRunById(Long runId);
}

View File

@@ -52,6 +52,18 @@ public class WorkflowRunServiceImpl extends ServiceImpl<WorkflowRunMapper, Workf
return workflowRunFactory.toVOList(runs);
}
@Override
public WorkflowRunVO getRunById(Long runId) {
if (runId == null) {
throw new IllegalArgumentException("Workflow运行ID不能为空");
}
WorkflowRun run = getById(runId);
if (run == null) {
return null;
}
return workflowRunFactory.toVO(run);
}
private void validateRequest(WorkflowRunCreateDTO request) {
if (request == null) {
throw new IllegalArgumentException("Workflow运行请求不能为空");

View File

@@ -0,0 +1,130 @@
package com.bruce.workflow.controller;
import com.bruce.common.handler.GlobalExceptionHandler;
import com.bruce.workflow.service.IWorkflowDefinitionService;
import com.bruce.workflow.service.IWorkflowRunService;
import com.bruce.workflow.service.IWorkflowVersionService;
import com.bruce.workflow.vo.WorkflowDefinitionVO;
import com.bruce.workflow.vo.WorkflowRunVO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 验证 Workflow 文档草案兼容路径的返回契约。
*/
@ExtendWith(MockitoExtension.class)
class WorkflowCompatControllerTests {
private MockMvc mockMvc;
@Mock
private IWorkflowDefinitionService workflowDefinitionService;
@Mock
private IWorkflowVersionService workflowVersionService;
@Mock
private IWorkflowRunService workflowRunService;
@InjectMocks
private WorkflowDefinitionController workflowDefinitionController;
@InjectMocks
private WorkflowVersionController workflowVersionController;
@InjectMocks
private WorkflowRunController workflowRunController;
@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(
workflowDefinitionController,
workflowVersionController,
workflowRunController
)
.setControllerAdvice(new GlobalExceptionHandler())
.build();
}
@Test
void workflowDetailCompatShouldReturnStructuredDefinition() throws Exception {
WorkflowDefinitionVO detail = new WorkflowDefinitionVO();
detail.setId(201L);
detail.setWorkflowCode("workflow-support-rag");
detail.setWorkflowName("合同知识召回");
when(workflowDefinitionService.getDefinition(201L)).thenReturn(detail);
mockMvc.perform(get("/api/workflows/201"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data.workflowCode").value("workflow-support-rag"));
}
@Test
void saveDraftCompatShouldDelegateToDefinitionService() throws Exception {
when(workflowDefinitionService.saveDefinition(any())).thenReturn(true);
mockMvc.perform(post("/api/workflows/save-draft")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"id": 201,
"projectId": 101,
"workflowCode": "workflow-support-rag",
"workflowName": "合同知识召回"
}
"""))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data").value(true));
}
@Test
void publishCompatShouldDelegateToVersionService() throws Exception {
when(workflowVersionService.publishVersion(any())).thenReturn(true);
mockMvc.perform(post("/api/workflow-versions/compat/workflows/201/publish")
.contentType(MediaType.APPLICATION_JSON)
.content("""
{
"versionNo": 3,
"snapshotName": "v3"
}
"""))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data").value(true));
}
@Test
void runCompatShouldReturnStructuredRunDetail() throws Exception {
WorkflowRunVO run = new WorkflowRunVO();
run.setId(301L);
run.setWorkflowId(201L);
run.setRequestId("req-1001");
run.setStatus("SUCCESS");
when(workflowRunService.getRunById(301L)).thenReturn(run);
mockMvc.perform(get("/api/workflow-runs/compat/workflows/runs/301"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.resultcode").value("0"))
.andExpect(jsonPath("$.data.requestId").value("req-1001"))
.andExpect(jsonPath("$.data.status").value("SUCCESS"));
}
}

View File

@@ -159,15 +159,18 @@
- `WorkflowWorkspaceController`
- 前端工作台聚合使用 `GET /api/workflow-workspace/detail`
旧管理接口继续承担定义、版本与运行管理能力。
说明:
- 前端实现文档中的 REST 草案与当前聚合接口命名不完全一致,但职责已由 `WorkflowWorkspaceController` 承接,且不影响旧接口兼容。
- 文档草案兼容路径已补充:
- `GET /api/workflows/{workflowId}`
- `POST /api/workflows/save-draft`
- `POST /api/workflow-versions/compat/workflows/{workflowId}/publish`
- `POST /api/workflow-runs/compat/workflows/{workflowId}/runs`
- `GET /api/workflow-runs/compat/workflows/runs/{runId}`
证据:
- 代码:`common-agent-workflow/src/main/java/com/bruce/workflow/**/*`
- 测试:
- `common-agent-workflow/src/test/java/com/bruce/workflow/controller/WorkflowCompatControllerTests.java`
- `common-agent-workflow/src/test/java/com/bruce/workflow/controller/WorkflowWorkspaceControllerTests.java`
- `common-agent-workflow/src/test/java/com/bruce/workflow/workspace/WorkflowWorkspaceServiceTests.java`
- `common-agent-workflow/src/test/java/com/bruce/workflow/version/WorkflowVersionServiceTests.java`
@@ -183,6 +186,7 @@
- 接口已覆盖:
- `POST /api/mcp/import`
- `GET /api/mcp/servers`
- `POST /api/mcp/servers/query`
- `GET /api/mcp/servers/{serverId}/capabilities`
- `GET /api/mcp/servers/code/{serverCode}/capabilities`
- `POST /api/mcp/capabilities/save`
@@ -206,6 +210,7 @@
- 工作台接口已覆盖:
- `GET /api/skills/{skillCode}`
- `POST /api/skills/{skillCode}/draft`
- `PUT /api/skills/{skillCode}/draft`
- `POST /api/skills/{skillCode}/test`
- `POST /api/skills/{skillCode}/publish`
- `POST /api/skills/{skillCode}/archive`
@@ -262,10 +267,29 @@
- `frontend/src/pages/studio/__tests__/*`
- `frontend/src/api/__tests__/*`
## 4.1 文档草案路径兼容收口
为减少“文档草案路径”和“现有聚合接口路径”之间的偏差,当前已额外补齐以下兼容入口:
- Workflow
- `GET /api/workflows/{workflowId}`
- `POST /api/workflows/save-draft`
- `POST /api/workflow-versions/compat/workflows/{workflowId}/publish`
- `POST /api/workflow-runs/compat/workflows/{workflowId}/runs`
- `GET /api/workflow-runs/compat/workflows/runs/{runId}`
- Agent
- `POST /api/agents/{agentId}/runs`
- `GET /api/agent-sessions/agents/{agentId}/sessions`
- `GET /api/agent-sessions/{sessionId}`
- MCP
- `POST /api/mcp/servers/query`
- Skill
- `PUT /api/skills/{skillCode}/draft`
## 5. 当前仍需持续关注的风险
- 当前多数“mapper / repository 验证”仍以结构契约测试为主,真实数据库集成测试覆盖度有限。
- 部分前端实现文档中的接口命名是草案,当前实现更多采用“旧管理接口 + Studio 聚合接口”的双轨方式;职责已覆盖,但验收时需要按“能力是否已落地”而非“路径字面一致”判断
- 当前实现仍保留“旧管理接口 + Studio 聚合接口 + 文档草案兼容路径”的三轨并行方式,能力已覆盖,但后续如进入正式 API 收敛阶段,仍建议选定长期主路径并逐步淘汰别名
- 现有运行链路以“主数据优先 + 最小可运行”实现为主,复杂分支调度、远程 MCP 实时执行编排、重型运行器能力仍适合后续继续增强。
## 6. 本次审计后的新增变更
@@ -273,3 +297,4 @@
- 新增 `POST /api/knowledge/ingestion-runs`,补齐前端实现文档中的摄取运行创建入口。
- 补充 `IngestionRunControllerTests` 与前端 `ingestion.spec.ts` 创建接口测试。
- 补充 `WorkflowWorkspaceController` 中文注释与标准化业务日志。
- 补充 Workflow / Agent / MCP / Skill 的文档草案兼容路径与控制器测试。