feat(workflow): 补齐项目版本运行与工作台链路

This commit is contained in:
2026-06-01 04:18:01 +08:00
parent 5e0212d2a0
commit 8596f5074b
37 changed files with 1300 additions and 6 deletions

View File

@@ -0,0 +1,38 @@
package com.bruce.workflow.controller;
import com.bruce.common.domain.model.RequestResult;
import com.bruce.workflow.dto.ProjectSaveDTO;
import com.bruce.workflow.service.IProjectService;
import com.bruce.workflow.vo.ProjectVO;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/studio-projects")
@RequiredArgsConstructor
public class ProjectController {
private final IProjectService projectService;
@GetMapping("/list")
public RequestResult<List<ProjectVO>> list() {
return RequestResult.success(projectService.listProjects());
}
@GetMapping("/detail")
public RequestResult<ProjectVO> detail(@RequestParam("id") Long id) {
return RequestResult.success(projectService.getProject(id));
}
@PostMapping("/save")
public RequestResult<Boolean> save(@RequestBody ProjectSaveDTO request) {
return RequestResult.success(projectService.saveProject(request));
}
}

View File

@@ -22,8 +22,10 @@ public class WorkflowDefinitionController {
private final IWorkflowDefinitionService workflowDefinitionService;
@GetMapping("/definitions")
public RequestResult<List<WorkflowDefinitionVO>> list() {
return RequestResult.success(workflowDefinitionService.listDefinitions());
public RequestResult<List<WorkflowDefinitionVO>> list(@RequestParam(value = "projectId", required = false) Long projectId) {
return RequestResult.success(projectId == null
? workflowDefinitionService.listDefinitions()
: workflowDefinitionService.listByProjectId(projectId));
}
@GetMapping("/definition/detail")

View File

@@ -0,0 +1,33 @@
package com.bruce.workflow.controller;
import com.bruce.common.domain.model.RequestResult;
import com.bruce.workflow.dto.WorkflowRunCreateDTO;
import com.bruce.workflow.service.IWorkflowRunService;
import com.bruce.workflow.vo.WorkflowRunVO;
import lombok.RequiredArgsConstructor;
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;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/workflow-runs")
@RequiredArgsConstructor
public class WorkflowRunController {
private final IWorkflowRunService workflowRunService;
@PostMapping("/create")
public RequestResult<Boolean> create(@RequestBody WorkflowRunCreateDTO request) {
return RequestResult.success(workflowRunService.createRun(request));
}
@GetMapping("/{workflowId}")
public RequestResult<List<WorkflowRunVO>> list(@PathVariable("workflowId") Long workflowId) {
return RequestResult.success(workflowRunService.listRecentByWorkflowId(workflowId));
}
}

View File

@@ -0,0 +1,33 @@
package com.bruce.workflow.controller;
import com.bruce.common.domain.model.RequestResult;
import com.bruce.workflow.dto.WorkflowVersionSaveDTO;
import com.bruce.workflow.service.IWorkflowVersionService;
import com.bruce.workflow.vo.WorkflowVersionVO;
import lombok.RequiredArgsConstructor;
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;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api/workflow-versions")
@RequiredArgsConstructor
public class WorkflowVersionController {
private final IWorkflowVersionService workflowVersionService;
@GetMapping("/{workflowId}")
public RequestResult<List<WorkflowVersionVO>> list(@PathVariable("workflowId") Long workflowId) {
return RequestResult.success(workflowVersionService.listByWorkflowId(workflowId));
}
@PostMapping("/publish")
public RequestResult<Boolean> publish(@RequestBody WorkflowVersionSaveDTO request) {
return RequestResult.success(workflowVersionService.publishVersion(request));
}
}

View File

@@ -0,0 +1,24 @@
package com.bruce.workflow.controller;
import com.bruce.common.domain.model.RequestResult;
import com.bruce.workflow.service.IWorkflowWorkspaceService;
import com.bruce.workflow.vo.WorkflowWorkspaceVO;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/workflow-workspace")
@RequiredArgsConstructor
public class WorkflowWorkspaceController {
private final IWorkflowWorkspaceService workflowWorkspaceService;
@GetMapping("/detail")
public RequestResult<WorkflowWorkspaceVO> detail(@RequestParam("projectId") Long projectId,
@RequestParam(value = "workflowId", required = false) Long workflowId) {
return RequestResult.success(workflowWorkspaceService.getWorkspace(projectId, workflowId));
}
}

View File

@@ -0,0 +1,14 @@
package com.bruce.workflow.dto;
import lombok.Data;
@Data
public class ProjectSaveDTO {
private Long id;
private String projectCode;
private String projectName;
private String environment;
private String publishStatus;
private String currentVersion;
private String remark;
}

View File

@@ -0,0 +1,18 @@
package com.bruce.workflow.dto;
import lombok.Data;
@Data
public class WorkflowRunCreateDTO {
private Long workflowId;
private Long workflowVersionId;
private Long agentId;
private String requestId;
private String runSource;
private String status;
private String inputJson;
private String outputJson;
private Integer durationMs;
private java.math.BigDecimal estimatedCost;
private String remark;
}

View File

@@ -0,0 +1,13 @@
package com.bruce.workflow.dto;
import lombok.Data;
@Data
public class WorkflowVersionSaveDTO {
private Long workflowId;
private Integer versionNo;
private String snapshotName;
private String graphJson;
private String publishStatus;
private String remark;
}

View File

@@ -15,6 +15,8 @@ import lombok.EqualsAndHashCode;
@Schema(description = "Studio项目空间")
public class StudioProject extends BaseEntity {
private static final long serialVersionUID = 1L;
private String projectCode;
private String projectName;
@@ -24,4 +26,6 @@ public class StudioProject extends BaseEntity {
private String publishStatus;
private String currentVersion;
private String remark;
}

View File

@@ -13,6 +13,8 @@ import lombok.EqualsAndHashCode;
@TableName("workflow_definition")
public class WorkflowDefinition extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long projectId;
private String workflowCode;
@@ -24,4 +26,6 @@ public class WorkflowDefinition extends BaseEntity {
private Long boundAgentId;
private String status;
private String remark;
}

View File

@@ -17,6 +17,8 @@ import java.math.BigDecimal;
@TableName("workflow_run")
public class WorkflowRun extends BaseEntity {
private static final long serialVersionUID = 1L;
private String requestId;
private Long workflowId;
@@ -38,4 +40,6 @@ public class WorkflowRun extends BaseEntity {
private Integer durationMs;
private BigDecimal estimatedCost;
private String remark;
}

View File

@@ -15,6 +15,8 @@ import lombok.EqualsAndHashCode;
@TableName("workflow_run_step")
public class WorkflowRunStep extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long runId;
private String nodeId;
@@ -34,4 +36,6 @@ public class WorkflowRunStep extends BaseEntity {
private Integer durationMs;
private String errorMessage;
private String remark;
}

View File

@@ -15,6 +15,8 @@ import lombok.EqualsAndHashCode;
@TableName("workflow_version")
public class WorkflowVersion extends BaseEntity {
private static final long serialVersionUID = 1L;
private Long workflowId;
private Integer versionNo;
@@ -27,4 +29,6 @@ public class WorkflowVersion extends BaseEntity {
private String publishStatus;
private java.time.LocalDateTime publishedTime;
private String remark;
}

View File

@@ -0,0 +1,52 @@
package com.bruce.workflow.factory;
import com.bruce.workflow.dto.ProjectSaveDTO;
import com.bruce.workflow.entity.StudioProject;
import com.bruce.workflow.vo.ProjectVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* Studio 项目空间工厂,统一处理请求、实体和返回对象之间的转换。
*/
@Component
public class ProjectFactory {
public StudioProject toEntity(ProjectSaveDTO request) {
if (request == null) {
return null;
}
StudioProject entity = new StudioProject();
entity.setId(request.getId());
entity.setProjectCode(trimToNull(request.getProjectCode()));
entity.setProjectName(trimToNull(request.getProjectName()));
entity.setEnvironment(trimToNull(request.getEnvironment()));
entity.setPublishStatus(trimToNull(request.getPublishStatus()));
entity.setCurrentVersion(trimToNull(request.getCurrentVersion()));
entity.setRemark(trimToNull(request.getRemark()));
return entity;
}
public ProjectVO toVO(StudioProject entity) {
if (entity == null) {
return null;
}
ProjectVO vo = new ProjectVO();
BeanUtils.copyProperties(entity, vo);
return vo;
}
public List<ProjectVO> toVOList(List<StudioProject> entities) {
return entities == null ? List.of() : entities.stream().map(this::toVO).toList();
}
private String trimToNull(String value) {
if (!StringUtils.hasText(value)) {
return null;
}
return value.trim();
}
}

View File

@@ -3,6 +3,7 @@ package com.bruce.workflow.factory;
import com.bruce.workflow.dto.WorkflowDefinitionSaveDTO;
import com.bruce.workflow.entity.WorkflowDefinition;
import com.bruce.workflow.vo.WorkflowDefinitionVO;
import org.springframework.util.StringUtils;
import org.springframework.stereotype.Component;
/**
@@ -23,7 +24,8 @@ public class WorkflowDefinitionFactory {
entity.setWorkflowName(dto.getWorkflowName());
entity.setDescription(dto.getDescription());
entity.setBoundAgentId(dto.getBoundAgentId());
entity.setStatus(dto.getStatus());
entity.setStatus(trimToNull(dto.getStatus()));
entity.setRemark(trimToNull(dto.getRemark()));
return entity;
}
@@ -39,6 +41,14 @@ public class WorkflowDefinitionFactory {
vo.setBoundAgentId(entity.getBoundAgentId());
vo.setStatus(entity.getStatus());
vo.setDescription(entity.getDescription());
vo.setRemark(entity.getRemark());
return vo;
}
private String trimToNull(String value) {
if (!StringUtils.hasText(value)) {
return null;
}
return value.trim();
}
}

View File

@@ -0,0 +1,56 @@
package com.bruce.workflow.factory;
import com.bruce.workflow.dto.WorkflowRunCreateDTO;
import com.bruce.workflow.entity.WorkflowRun;
import com.bruce.workflow.vo.WorkflowRunVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* Workflow 运行工厂,统一处理运行记录转换。
*/
@Component
public class WorkflowRunFactory {
public WorkflowRun toEntity(WorkflowRunCreateDTO request) {
if (request == null) {
return null;
}
WorkflowRun entity = new WorkflowRun();
entity.setWorkflowId(request.getWorkflowId());
entity.setWorkflowVersionId(request.getWorkflowVersionId());
entity.setAgentId(request.getAgentId());
entity.setRequestId(trimToNull(request.getRequestId()));
entity.setRunSource(trimToNull(request.getRunSource()));
entity.setStatus(trimToNull(request.getStatus()));
entity.setInputJson(trimToNull(request.getInputJson()));
entity.setOutputJson(trimToNull(request.getOutputJson()));
entity.setDurationMs(request.getDurationMs());
entity.setEstimatedCost(request.getEstimatedCost());
entity.setRemark(trimToNull(request.getRemark()));
return entity;
}
public WorkflowRunVO toVO(WorkflowRun entity) {
if (entity == null) {
return null;
}
WorkflowRunVO vo = new WorkflowRunVO();
BeanUtils.copyProperties(entity, vo);
return vo;
}
public List<WorkflowRunVO> toVOList(List<WorkflowRun> entities) {
return entities == null ? List.of() : entities.stream().map(this::toVO).toList();
}
private String trimToNull(String value) {
if (!StringUtils.hasText(value)) {
return null;
}
return value.trim();
}
}

View File

@@ -0,0 +1,51 @@
package com.bruce.workflow.factory;
import com.bruce.workflow.dto.WorkflowVersionSaveDTO;
import com.bruce.workflow.entity.WorkflowVersion;
import com.bruce.workflow.vo.WorkflowVersionVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* Workflow 版本工厂,统一负责快照 DTO、Entity、VO 转换。
*/
@Component
public class WorkflowVersionFactory {
public WorkflowVersion toEntity(WorkflowVersionSaveDTO request) {
if (request == null) {
return null;
}
WorkflowVersion entity = new WorkflowVersion();
entity.setWorkflowId(request.getWorkflowId());
entity.setVersionNo(request.getVersionNo());
entity.setSnapshotName(trimToNull(request.getSnapshotName()));
entity.setGraphJson(trimToNull(request.getGraphJson()));
entity.setPublishStatus(trimToNull(request.getPublishStatus()));
entity.setRemark(trimToNull(request.getRemark()));
return entity;
}
public WorkflowVersionVO toVO(WorkflowVersion entity) {
if (entity == null) {
return null;
}
WorkflowVersionVO vo = new WorkflowVersionVO();
BeanUtils.copyProperties(entity, vo);
return vo;
}
public List<WorkflowVersionVO> toVOList(List<WorkflowVersion> entities) {
return entities == null ? List.of() : entities.stream().map(this::toVO).toList();
}
private String trimToNull(String value) {
if (!StringUtils.hasText(value)) {
return null;
}
return value.trim();
}
}

View File

@@ -0,0 +1,17 @@
package com.bruce.workflow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bruce.workflow.dto.ProjectSaveDTO;
import com.bruce.workflow.entity.StudioProject;
import com.bruce.workflow.vo.ProjectVO;
import java.util.List;
public interface IProjectService extends IService<StudioProject> {
List<ProjectVO> listProjects();
ProjectVO getProject(Long id);
boolean saveProject(ProjectSaveDTO request);
}

View File

@@ -11,6 +11,8 @@ public interface IWorkflowDefinitionService extends IService<WorkflowDefinition>
List<WorkflowDefinitionVO> listDefinitions();
List<WorkflowDefinitionVO> listByProjectId(Long projectId);
WorkflowDefinitionVO getDefinition(Long id);
boolean saveDefinition(WorkflowDefinitionSaveDTO request);

View File

@@ -0,0 +1,15 @@
package com.bruce.workflow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bruce.workflow.dto.WorkflowRunCreateDTO;
import com.bruce.workflow.entity.WorkflowRun;
import com.bruce.workflow.vo.WorkflowRunVO;
import java.util.List;
public interface IWorkflowRunService extends IService<WorkflowRun> {
boolean createRun(WorkflowRunCreateDTO request);
List<WorkflowRunVO> listRecentByWorkflowId(Long workflowId);
}

View File

@@ -0,0 +1,16 @@
package com.bruce.workflow.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bruce.workflow.dto.WorkflowVersionSaveDTO;
import com.bruce.workflow.entity.WorkflowDefinition;
import com.bruce.workflow.entity.WorkflowVersion;
import com.bruce.workflow.vo.WorkflowVersionVO;
import java.util.List;
public interface IWorkflowVersionService extends IService<WorkflowVersion> {
boolean publishVersion(WorkflowVersionSaveDTO request);
List<WorkflowVersionVO> listByWorkflowId(Long workflowId);
}

View File

@@ -0,0 +1,8 @@
package com.bruce.workflow.service;
import com.bruce.workflow.vo.WorkflowWorkspaceVO;
public interface IWorkflowWorkspaceService {
WorkflowWorkspaceVO getWorkspace(Long projectId, Long workflowId);
}

View File

@@ -0,0 +1,82 @@
package com.bruce.workflow.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bruce.workflow.dto.ProjectSaveDTO;
import com.bruce.workflow.entity.StudioProject;
import com.bruce.workflow.factory.ProjectFactory;
import com.bruce.workflow.mapper.StudioProjectMapper;
import com.bruce.workflow.service.IProjectService;
import com.bruce.workflow.vo.ProjectVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class ProjectServiceImpl extends ServiceImpl<StudioProjectMapper, StudioProject> implements IProjectService {
private final ProjectFactory projectFactory;
@Override
public List<ProjectVO> listProjects() {
List<ProjectVO> result = projectFactory.toVOList(lambdaQuery()
.orderByAsc(StudioProject::getProjectCode)
.list());
log.info("查询Studio项目列表完成count={}", result.size());
return result;
}
@Override
public ProjectVO getProject(Long id) {
if (id == null) {
throw new IllegalArgumentException("项目ID不能为空");
}
ProjectVO result = projectFactory.toVO(getById(id));
log.info("查询Studio项目详情完成projectId={}, found={}", id, result != null);
return result;
}
@Override
public boolean saveProject(ProjectSaveDTO request) {
validateRequest(request);
String projectCode = request.getProjectCode().trim();
StudioProject duplicate = lambdaQuery()
.eq(StudioProject::getProjectCode, projectCode)
.ne(request.getId() != null, StudioProject::getId, request.getId())
.one();
if (duplicate != null) {
throw new IllegalArgumentException("项目编码已存在: " + projectCode);
}
StudioProject requestEntity = projectFactory.toEntity(request);
StudioProject entity = request.getId() == null ? new StudioProject() : getById(request.getId());
if (entity == null) {
throw new IllegalArgumentException("项目不存在ID: " + request.getId());
}
entity.setProjectCode(requestEntity.getProjectCode());
entity.setProjectName(requestEntity.getProjectName());
entity.setEnvironment(StringUtils.hasText(requestEntity.getEnvironment()) ? requestEntity.getEnvironment() : "DEV");
entity.setPublishStatus(StringUtils.hasText(requestEntity.getPublishStatus()) ? requestEntity.getPublishStatus() : "DRAFT");
entity.setCurrentVersion(requestEntity.getCurrentVersion());
entity.setRemark(requestEntity.getRemark());
boolean result = request.getId() == null ? save(entity) : updateById(entity);
log.info("保存Studio项目完成projectId={}, projectCode={}, result={}",
entity.getId(), entity.getProjectCode(), result);
return result;
}
private void validateRequest(ProjectSaveDTO request) {
if (request == null) {
throw new IllegalArgumentException("项目保存请求不能为空");
}
if (!StringUtils.hasText(request.getProjectCode())) {
throw new IllegalArgumentException("项目编码不能为空");
}
if (!StringUtils.hasText(request.getProjectName())) {
throw new IllegalArgumentException("项目名称不能为空");
}
}
}

View File

@@ -10,6 +10,7 @@ import com.bruce.workflow.vo.WorkflowDefinitionVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@@ -33,6 +34,22 @@ public class WorkflowDefinitionServiceImpl extends ServiceImpl<WorkflowDefinitio
return result;
}
@Override
public List<WorkflowDefinitionVO> listByProjectId(Long projectId) {
if (projectId == null) {
throw new IllegalArgumentException("项目ID不能为空");
}
List<WorkflowDefinitionVO> result = lambdaQuery()
.eq(WorkflowDefinition::getProjectId, projectId)
.orderByAsc(WorkflowDefinition::getWorkflowCode)
.list()
.stream()
.map(workflowDefinitionFactory::toVO)
.toList();
log.info("按项目查询Workflow定义完成projectId={}, count={}", projectId, result.size());
return result;
}
@Override
public WorkflowDefinitionVO getDefinition(Long id) {
log.info("查询Workflow定义详情开始workflowId={}", id);
@@ -45,11 +62,47 @@ public class WorkflowDefinitionServiceImpl extends ServiceImpl<WorkflowDefinitio
public boolean saveDefinition(WorkflowDefinitionSaveDTO request) {
log.info("保存Workflow定义开始workflowId={}, workflowCode={}", request == null ? null : request.getId(),
request == null ? null : request.getWorkflowCode());
boolean result = saveOrUpdate(workflowDefinitionFactory.toEntity(request));
validateRequest(request);
String workflowCode = request.getWorkflowCode().trim();
WorkflowDefinition duplicate = lambdaQuery()
.eq(WorkflowDefinition::getWorkflowCode, workflowCode)
.ne(request.getId() != null, WorkflowDefinition::getId, request.getId())
.one();
if (duplicate != null) {
throw new IllegalArgumentException("Workflow编码已存在: " + workflowCode);
}
WorkflowDefinition requestEntity = workflowDefinitionFactory.toEntity(request);
WorkflowDefinition entity = request.getId() == null ? new WorkflowDefinition() : getById(request.getId());
if (entity == null) {
throw new IllegalArgumentException("Workflow不存在ID: " + request.getId());
}
entity.setProjectId(requestEntity.getProjectId());
entity.setWorkflowCode(workflowCode);
entity.setWorkflowName(requestEntity.getWorkflowName());
entity.setDescription(requestEntity.getDescription());
entity.setBoundAgentId(requestEntity.getBoundAgentId());
entity.setStatus(StringUtils.hasText(requestEntity.getStatus()) ? requestEntity.getStatus() : "DRAFT");
entity.setRemark(requestEntity.getRemark());
boolean result = request.getId() == null ? save(entity) : updateById(entity);
log.info("保存Workflow定义结束workflowId={}, workflowCode={}, result={}",
request == null ? null : request.getId(),
request == null ? null : request.getWorkflowCode(),
entity.getId(),
entity.getWorkflowCode(),
result);
return result;
}
private void validateRequest(WorkflowDefinitionSaveDTO request) {
if (request == null) {
throw new IllegalArgumentException("Workflow保存请求不能为空");
}
if (request.getProjectId() == null) {
throw new IllegalArgumentException("项目ID不能为空");
}
if (!StringUtils.hasText(request.getWorkflowCode())) {
throw new IllegalArgumentException("Workflow编码不能为空");
}
if (!StringUtils.hasText(request.getWorkflowName())) {
throw new IllegalArgumentException("Workflow名称不能为空");
}
}
}

View File

@@ -0,0 +1,69 @@
package com.bruce.workflow.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bruce.workflow.dto.WorkflowRunCreateDTO;
import com.bruce.workflow.entity.WorkflowRun;
import com.bruce.workflow.factory.WorkflowRunFactory;
import com.bruce.workflow.mapper.WorkflowRunMapper;
import com.bruce.workflow.service.IWorkflowRunService;
import com.bruce.workflow.vo.WorkflowRunVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class WorkflowRunServiceImpl extends ServiceImpl<WorkflowRunMapper, WorkflowRun> implements IWorkflowRunService {
private final WorkflowRunFactory workflowRunFactory;
@Override
public boolean createRun(WorkflowRunCreateDTO request) {
validateRequest(request);
WorkflowRun entity = workflowRunFactory.toEntity(request);
entity.setStatus(StringUtils.hasText(entity.getStatus()) ? entity.getStatus() : "RUNNING");
entity.setRunSource(StringUtils.hasText(entity.getRunSource()) ? entity.getRunSource() : "MANUAL_TEST");
if (!StringUtils.hasText(entity.getInputJson())) {
entity.setInputJson("{}");
}
if (!StringUtils.hasText(entity.getOutputJson())) {
entity.setOutputJson("{}");
}
boolean result = save(entity);
log.info("Workflow运行记录创建完成workflowId={}, versionId={}, requestId={}, result={}",
entity.getWorkflowId(), entity.getWorkflowVersionId(), entity.getRequestId(), result);
return result;
}
@Override
public List<WorkflowRunVO> listRecentByWorkflowId(Long workflowId) {
if (workflowId == null) {
throw new IllegalArgumentException("Workflow ID不能为空");
}
List<WorkflowRun> runs = lambdaQuery()
.eq(WorkflowRun::getWorkflowId, workflowId)
.orderByDesc(WorkflowRun::getId)
.last("limit 10")
.list();
return workflowRunFactory.toVOList(runs);
}
private void validateRequest(WorkflowRunCreateDTO request) {
if (request == null) {
throw new IllegalArgumentException("Workflow运行请求不能为空");
}
if (request.getWorkflowId() == null) {
throw new IllegalArgumentException("Workflow ID不能为空");
}
if (request.getWorkflowVersionId() == null) {
throw new IllegalArgumentException("Workflow版本ID不能为空");
}
if (!StringUtils.hasText(request.getRequestId())) {
throw new IllegalArgumentException("requestId不能为空");
}
}
}

View File

@@ -0,0 +1,124 @@
package com.bruce.workflow.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bruce.agent.entity.AgentDefinition;
import com.bruce.agent.service.IAgentDefinitionService;
import com.bruce.workflow.dto.WorkflowVersionSaveDTO;
import com.bruce.workflow.entity.WorkflowDefinition;
import com.bruce.workflow.entity.WorkflowVersion;
import com.bruce.workflow.factory.WorkflowVersionFactory;
import com.bruce.workflow.mapper.WorkflowVersionMapper;
import com.bruce.workflow.service.IWorkflowDefinitionService;
import com.bruce.workflow.service.IWorkflowVersionService;
import com.bruce.workflow.vo.WorkflowVersionVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class WorkflowVersionServiceImpl extends ServiceImpl<WorkflowVersionMapper, WorkflowVersion> implements IWorkflowVersionService {
private final IWorkflowDefinitionService workflowDefinitionService;
private final IAgentDefinitionService agentDefinitionService;
private final WorkflowVersionFactory workflowVersionFactory;
@Override
public boolean publishVersion(WorkflowVersionSaveDTO request) {
validateRequest(request);
WorkflowDefinition definition = loadDefinition(request.getWorkflowId());
WorkflowVersion duplicate = findByWorkflowAndVersionNo(request.getWorkflowId(), request.getVersionNo());
if (duplicate != null) {
throw new IllegalArgumentException("发布版本号已存在: " + request.getVersionNo());
}
validateDefinitionBinding(definition);
validateGraphJson(request.getGraphJson());
WorkflowVersion entity = loadFactory().toEntity(request);
entity.setPublishStatus(StringUtils.hasText(entity.getPublishStatus()) ? entity.getPublishStatus() : "PUBLISHED");
entity.setPublishedTime(LocalDateTime.now());
boolean result = save(entity);
log.info("Workflow版本发布完成workflowId={}, versionNo={}, result={}",
entity.getWorkflowId(), entity.getVersionNo(), result);
return result;
}
@Override
public List<WorkflowVersionVO> listByWorkflowId(Long workflowId) {
if (workflowId == null) {
throw new IllegalArgumentException("Workflow ID不能为空");
}
return loadFactory().toVOList(lambdaQuery()
.eq(WorkflowVersion::getWorkflowId, workflowId)
.orderByDesc(WorkflowVersion::getVersionNo)
.list());
}
public WorkflowDefinition loadDefinition(Long workflowId) {
WorkflowDefinition definition = workflowDefinitionService.getById(workflowId);
if (definition == null) {
throw new IllegalArgumentException("Workflow定义不存在ID: " + workflowId);
}
return definition;
}
public WorkflowVersion findByWorkflowAndVersionNo(Long workflowId, Integer versionNo) {
if (baseMapper == null) {
return null;
}
return lambdaQuery()
.eq(WorkflowVersion::getWorkflowId, workflowId)
.eq(WorkflowVersion::getVersionNo, versionNo)
.one();
}
private void validateDefinitionBinding(WorkflowDefinition definition) {
if (definition.getBoundAgentId() == null) {
return;
}
AgentDefinition agent = agentDefinitionService.getById(definition.getBoundAgentId());
if (agent == null) {
throw new IllegalArgumentException("绑定Agent不存在ID: " + definition.getBoundAgentId());
}
if (!"ENABLED".equals(agent.getStatus())) {
throw new IllegalArgumentException("绑定Agent未启用无法发布");
}
}
/**
* 当前版本先做最小图结构校验,确保至少有节点定义,后续节点级配置校验会在运行器接入后继续增强。
*/
private void validateGraphJson(String graphJson) {
if (!StringUtils.hasText(graphJson)) {
throw new IllegalArgumentException("graphJson不能为空");
}
String normalized = graphJson.trim();
if (!normalized.startsWith("{") || !normalized.contains("\"nodes\"")) {
throw new IllegalArgumentException("graphJson结构非法至少需要包含nodes");
}
}
private void validateRequest(WorkflowVersionSaveDTO request) {
if (request == null) {
throw new IllegalArgumentException("Workflow版本保存请求不能为空");
}
if (request.getWorkflowId() == null) {
throw new IllegalArgumentException("Workflow ID不能为空");
}
if (request.getVersionNo() == null) {
throw new IllegalArgumentException("版本号不能为空");
}
if (!StringUtils.hasText(request.getSnapshotName())) {
throw new IllegalArgumentException("快照名称不能为空");
}
}
private WorkflowVersionFactory loadFactory() {
return workflowVersionFactory == null ? new WorkflowVersionFactory() : workflowVersionFactory;
}
}

View File

@@ -0,0 +1,88 @@
package com.bruce.workflow.service.impl;
import com.bruce.workflow.service.IProjectService;
import com.bruce.workflow.service.IWorkflowDefinitionService;
import com.bruce.workflow.service.IWorkflowRunService;
import com.bruce.workflow.service.IWorkflowVersionService;
import com.bruce.workflow.service.IWorkflowWorkspaceService;
import com.bruce.workflow.vo.ProjectVO;
import com.bruce.workflow.vo.WorkflowDefinitionVO;
import com.bruce.workflow.vo.WorkflowRunVO;
import com.bruce.workflow.vo.WorkflowVersionVO;
import com.bruce.workflow.vo.WorkflowWorkspaceVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
@Slf4j
@Service
@RequiredArgsConstructor
public class WorkflowWorkspaceServiceImpl implements IWorkflowWorkspaceService {
private final IProjectService projectService;
private final IWorkflowDefinitionService workflowDefinitionService;
private final IWorkflowVersionService workflowVersionService;
private final IWorkflowRunService workflowRunService;
@Override
public WorkflowWorkspaceVO getWorkspace(Long projectId, Long workflowId) {
log.info("Workflow工作台查询开始projectId={}, workflowId={}", projectId, workflowId);
if (projectId == null) {
throw new IllegalArgumentException("项目ID不能为空");
}
ProjectVO project = projectService.getProject(projectId);
if (project == null) {
throw new IllegalArgumentException("项目不存在ID: " + projectId);
}
List<WorkflowDefinitionVO> workflows = workflowDefinitionService.listByProjectId(projectId);
WorkflowDefinitionVO currentWorkflow = resolveWorkflow(workflowId, workflows);
List<WorkflowVersionVO> versions = currentWorkflow == null ? List.of() : workflowVersionService.listByWorkflowId(currentWorkflow.getId());
List<WorkflowRunVO> runs = currentWorkflow == null ? List.of() : workflowRunService.listRecentByWorkflowId(currentWorkflow.getId());
WorkflowWorkspaceVO workspace = new WorkflowWorkspaceVO();
workspace.setProjectId(project.getId());
workspace.setProjectCode(project.getProjectCode());
workspace.setProjectName(project.getProjectName());
workspace.setEnvironment(project.getEnvironment());
workspace.setPublishStatus(project.getPublishStatus());
workspace.setWorkflows(workflows);
workspace.setVersions(versions);
workspace.setRecentRuns(runs);
if (currentWorkflow != null) {
workspace.setWorkflowId(currentWorkflow.getId());
workspace.setWorkflowCode(currentWorkflow.getWorkflowCode());
workspace.setWorkflowName(currentWorkflow.getWorkflowName());
workspace.setWorkflowStatus(currentWorkflow.getStatus());
}
WorkflowVersionVO currentPublishedVersion = versions.stream()
.filter(version -> "PUBLISHED".equals(version.getPublishStatus()))
.findFirst()
.orElse(null);
if (currentPublishedVersion != null) {
workspace.setCurrentPublishedVersionNo(currentPublishedVersion.getVersionNo());
}
WorkflowRunVO latestRun = runs.isEmpty() ? null : runs.getFirst();
if (latestRun != null) {
workspace.setLatestRequestId(latestRun.getRequestId());
workspace.setLatestDurationMs(latestRun.getDurationMs());
}
log.info("Workflow工作台查询结束projectId={}, workflowId={}, versionCount={}, recentRunCount={}",
projectId, workspace.getWorkflowId(), versions.size(), runs.size());
return workspace;
}
private WorkflowDefinitionVO resolveWorkflow(Long workflowId, List<WorkflowDefinitionVO> workflows) {
if (workflows == null || workflows.isEmpty()) {
return null;
}
if (workflowId == null) {
return workflows.getFirst();
}
return workflows.stream().filter(item -> workflowId.equals(item.getId())).findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,14 @@
package com.bruce.workflow.vo;
import lombok.Data;
@Data
public class ProjectVO {
private Long id;
private String projectCode;
private String projectName;
private String environment;
private String publishStatus;
private String currentVersion;
private String remark;
}

View File

@@ -21,4 +21,6 @@ public class WorkflowDefinitionVO {
private String status;
private String description;
private String remark;
}

View File

@@ -0,0 +1,21 @@
package com.bruce.workflow.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class WorkflowRunVO {
private Long id;
private Long workflowId;
private Long workflowVersionId;
private Long agentId;
private String requestId;
private String runSource;
private String status;
private String inputJson;
private String outputJson;
private Integer durationMs;
private BigDecimal estimatedCost;
private String remark;
}

View File

@@ -0,0 +1,17 @@
package com.bruce.workflow.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class WorkflowVersionVO {
private Long id;
private Long workflowId;
private Integer versionNo;
private String snapshotName;
private String graphJson;
private String publishStatus;
private LocalDateTime publishedTime;
private String remark;
}

View File

@@ -0,0 +1,24 @@
package com.bruce.workflow.vo;
import lombok.Data;
import java.util.List;
@Data
public class WorkflowWorkspaceVO {
private Long projectId;
private String projectCode;
private String projectName;
private String environment;
private String publishStatus;
private Long workflowId;
private String workflowCode;
private String workflowName;
private String workflowStatus;
private Integer currentPublishedVersionNo;
private String latestRequestId;
private Integer latestDurationMs;
private List<WorkflowDefinitionVO> workflows;
private List<WorkflowVersionVO> versions;
private List<WorkflowRunVO> recentRuns;
}

View File

@@ -0,0 +1,64 @@
package com.bruce.workflow.factory;
import com.bruce.workflow.dto.ProjectSaveDTO;
import com.bruce.workflow.dto.WorkflowVersionSaveDTO;
import com.bruce.workflow.entity.StudioProject;
import com.bruce.workflow.entity.WorkflowVersion;
import com.bruce.workflow.vo.ProjectVO;
import com.bruce.workflow.vo.WorkflowVersionVO;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class WorkflowFactoryTests {
private final ProjectFactory projectFactory = new ProjectFactory();
private final WorkflowVersionFactory workflowVersionFactory = new WorkflowVersionFactory();
@Test
void projectFactoryShouldTrimRequestAndBuildVo() {
ProjectSaveDTO request = new ProjectSaveDTO();
request.setId(11L);
request.setProjectCode(" STUDIO_DEMO ");
request.setProjectName(" 演示项目 ");
request.setEnvironment(" DEV ");
request.setPublishStatus(" DRAFT ");
request.setCurrentVersion(" v1 ");
request.setRemark(" 初始项目 ");
StudioProject entity = projectFactory.toEntity(request);
assertEquals("STUDIO_DEMO", entity.getProjectCode());
assertEquals("演示项目", entity.getProjectName());
assertEquals("DEV", entity.getEnvironment());
assertEquals("DRAFT", entity.getPublishStatus());
assertEquals("v1", entity.getCurrentVersion());
assertEquals("初始项目", entity.getRemark());
ProjectVO vo = projectFactory.toVO(entity);
assertEquals(11L, vo.getId());
assertEquals("STUDIO_DEMO", vo.getProjectCode());
}
@Test
void workflowVersionFactoryShouldPreserveGraphJsonAndPublishStatus() {
WorkflowVersionSaveDTO request = new WorkflowVersionSaveDTO();
request.setWorkflowId(1001L);
request.setVersionNo(7);
request.setSnapshotName(" 知识问答发布版 ");
request.setGraphJson(" {\"nodes\":[{\"id\":\"start\"}]} ");
request.setPublishStatus(" PUBLISHED ");
request.setRemark(" 首次发布 ");
WorkflowVersion entity = workflowVersionFactory.toEntity(request);
assertNotNull(entity);
assertEquals("知识问答发布版", entity.getSnapshotName());
assertEquals("{\"nodes\":[{\"id\":\"start\"}]}", entity.getGraphJson());
assertEquals("PUBLISHED", entity.getPublishStatus());
WorkflowVersionVO vo = workflowVersionFactory.toVO(entity);
assertEquals(1001L, vo.getWorkflowId());
assertEquals(7, vo.getVersionNo());
assertEquals("PUBLISHED", vo.getPublishStatus());
}
}

View File

@@ -0,0 +1,91 @@
package com.bruce.workflow.version;
import com.bruce.agent.entity.AgentDefinition;
import com.bruce.agent.service.IAgentDefinitionService;
import com.bruce.workflow.dto.WorkflowVersionSaveDTO;
import com.bruce.workflow.entity.WorkflowDefinition;
import com.bruce.workflow.entity.WorkflowVersion;
import com.bruce.workflow.service.IWorkflowDefinitionService;
import com.bruce.workflow.service.impl.WorkflowVersionServiceImpl;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class WorkflowVersionServiceTests {
@Mock
private IWorkflowDefinitionService workflowDefinitionService;
@Mock
private IAgentDefinitionService agentDefinitionService;
@Spy
@InjectMocks
private WorkflowVersionServiceImpl workflowVersionService;
@Test
void publishShouldRejectDuplicateVersionNo() {
WorkflowDefinition definition = new WorkflowDefinition();
definition.setId(1001L);
definition.setWorkflowCode("WF_RAG_SUPPORT");
definition.setBoundAgentId(2001L);
doReturn(definition).when(workflowVersionService).loadDefinition(1001L);
WorkflowVersion duplicate = new WorkflowVersion();
duplicate.setId(3001L);
doReturn(duplicate).when(workflowVersionService).findByWorkflowAndVersionNo(1001L, 2);
WorkflowVersionSaveDTO request = new WorkflowVersionSaveDTO();
request.setWorkflowId(1001L);
request.setVersionNo(2);
request.setSnapshotName("版本2");
request.setGraphJson("{\"nodes\":[{\"id\":\"start\",\"type\":\"START\"}]}");
assertThrows(IllegalArgumentException.class, () -> workflowVersionService.publishVersion(request));
}
@Test
void publishShouldPersistPublishedSnapshot() {
WorkflowDefinition definition = new WorkflowDefinition();
definition.setId(1001L);
definition.setWorkflowCode("WF_RAG_SUPPORT");
definition.setBoundAgentId(2001L);
doReturn(definition).when(workflowVersionService).loadDefinition(1001L);
doReturn(null).when(workflowVersionService).findByWorkflowAndVersionNo(1001L, 3);
doAnswer(invocation -> true).when(workflowVersionService).save(any(WorkflowVersion.class));
AgentDefinition agent = new AgentDefinition();
agent.setId(2001L);
agent.setStatus("ENABLED");
when(agentDefinitionService.getById(2001L)).thenReturn(agent);
WorkflowVersionSaveDTO request = new WorkflowVersionSaveDTO();
request.setWorkflowId(1001L);
request.setVersionNo(3);
request.setSnapshotName("知识问答正式版");
request.setGraphJson("{\"nodes\":[{\"id\":\"start\",\"type\":\"START\"},{\"id\":\"answer\",\"type\":\"ANSWER\"}],\"edges\":[{\"from\":\"start\",\"to\":\"answer\"}]}");
request.setRemark("正式发布");
boolean result = workflowVersionService.publishVersion(request);
assertTrue(result);
ArgumentCaptor<WorkflowVersion> captor = ArgumentCaptor.forClass(WorkflowVersion.class);
verify(workflowVersionService).save(captor.capture());
assertEquals("PUBLISHED", captor.getValue().getPublishStatus());
assertEquals("知识问答正式版", captor.getValue().getSnapshotName());
}
}

View File

@@ -0,0 +1,92 @@
package com.bruce.workflow.workspace;
import com.bruce.workflow.entity.StudioProject;
import com.bruce.workflow.service.IProjectService;
import com.bruce.workflow.service.IWorkflowDefinitionService;
import com.bruce.workflow.service.IWorkflowRunService;
import com.bruce.workflow.service.IWorkflowVersionService;
import com.bruce.workflow.service.impl.WorkflowWorkspaceServiceImpl;
import com.bruce.workflow.vo.ProjectVO;
import com.bruce.workflow.vo.WorkflowDefinitionVO;
import com.bruce.workflow.vo.WorkflowRunVO;
import com.bruce.workflow.vo.WorkflowVersionVO;
import com.bruce.workflow.vo.WorkflowWorkspaceVO;
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 java.math.BigDecimal;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class WorkflowWorkspaceServiceTests {
@Mock
private IProjectService projectService;
@Mock
private IWorkflowDefinitionService workflowDefinitionService;
@Mock
private IWorkflowVersionService workflowVersionService;
@Mock
private IWorkflowRunService workflowRunService;
@InjectMocks
private WorkflowWorkspaceServiceImpl workflowWorkspaceService;
@Test
void getWorkspaceShouldAggregateProjectDefinitionsVersionsAndRuns() {
ProjectVO project = new ProjectVO();
project.setId(101L);
project.setProjectCode("STUDIO_DEMO");
project.setProjectName("演示项目");
project.setEnvironment("DEV");
project.setPublishStatus("DRAFT");
WorkflowDefinitionVO definition = new WorkflowDefinitionVO();
definition.setId(201L);
definition.setProjectId(101L);
definition.setWorkflowCode("WF_RAG_SUPPORT");
definition.setWorkflowName("知识问答流程");
definition.setStatus("ENABLED");
WorkflowVersionVO version = new WorkflowVersionVO();
version.setId(301L);
version.setWorkflowId(201L);
version.setVersionNo(7);
version.setPublishStatus("PUBLISHED");
version.setSnapshotName("发布版v7");
WorkflowRunVO run = new WorkflowRunVO();
run.setId(401L);
run.setWorkflowId(201L);
run.setWorkflowVersionId(301L);
run.setRequestId("req-wf-001");
run.setStatus("SUCCESS");
run.setDurationMs(1280);
run.setEstimatedCost(new BigDecimal("0.018"));
when(projectService.getProject(101L)).thenReturn(project);
when(workflowDefinitionService.listByProjectId(101L)).thenReturn(List.of(definition));
when(workflowVersionService.listByWorkflowId(201L)).thenReturn(List.of(version));
when(workflowRunService.listRecentByWorkflowId(201L)).thenReturn(List.of(run));
WorkflowWorkspaceVO workspace = workflowWorkspaceService.getWorkspace(101L, 201L);
assertNotNull(workspace);
assertEquals("STUDIO_DEMO", workspace.getProjectCode());
assertEquals("WF_RAG_SUPPORT", workspace.getWorkflowCode());
assertEquals(1, workspace.getRecentRuns().size());
assertEquals("req-wf-001", workspace.getLatestRequestId());
assertEquals(1280, workspace.getLatestDurationMs());
assertEquals(7, workspace.getCurrentPublishedVersionNo());
}
}

View File

@@ -0,0 +1,44 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
getWorkflowDefinition,
getWorkflowWorkspace,
listWorkflowDefinitions,
saveWorkflowDefinition,
} from '../workflow';
import { get, post } from '../request';
vi.mock('../request', () => ({
get: vi.fn(),
post: vi.fn(),
}));
describe('workflow api', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('maps workflow endpoints correctly', () => {
listWorkflowDefinitions();
getWorkflowDefinition('1001');
saveWorkflowDefinition({
projectId: '2001',
workflowCode: 'WF_RAG_SUPPORT',
workflowName: '知识问答流程',
status: 'DRAFT',
});
getWorkflowWorkspace('2001', '1001');
expect(get).toHaveBeenCalledWith('/workflows/definitions');
expect(get).toHaveBeenCalledWith('/workflows/definition/detail', { params: { id: '1001' } });
expect(post).toHaveBeenCalledWith('/workflows/definition/save', {
projectId: '2001',
workflowCode: 'WF_RAG_SUPPORT',
workflowName: '知识问答流程',
status: 'DRAFT',
});
expect(get).toHaveBeenCalledWith('/workflow-workspace/detail', {
params: { projectId: '2001', workflowId: '1001' },
});
});
});

View File

@@ -0,0 +1,87 @@
import { get, post } from './request';
export interface ProjectRecord {
id?: string;
projectCode: string;
projectName: string;
environment?: string;
publishStatus?: string;
currentVersion?: string;
remark?: string;
}
export interface WorkflowDefinitionRecord {
id?: string;
projectId: string;
workflowCode: string;
workflowName: string;
description?: string;
boundAgentId?: string;
status?: string;
remark?: string;
}
export interface WorkflowVersionRecord {
id?: string;
workflowId: string;
versionNo: number;
snapshotName: string;
graphJson: string;
publishStatus?: string;
publishedTime?: string;
remark?: string;
}
export interface WorkflowRunRecord {
id?: string;
workflowId: string;
workflowVersionId: string;
agentId?: string;
requestId: string;
runSource?: string;
status?: string;
inputJson?: string;
outputJson?: string;
durationMs?: number;
estimatedCost?: number;
remark?: string;
}
export interface WorkflowWorkspace {
projectId: string;
projectCode: string;
projectName: string;
environment?: string;
publishStatus?: string;
workflowId?: string;
workflowCode?: string;
workflowName?: string;
workflowStatus?: string;
currentPublishedVersionNo?: number;
latestRequestId?: string;
latestDurationMs?: number;
workflows: WorkflowDefinitionRecord[];
versions: WorkflowVersionRecord[];
recentRuns: WorkflowRunRecord[];
}
export function listWorkflowDefinitions() {
return get<WorkflowDefinitionRecord[]>('/workflows/definitions');
}
export function getWorkflowDefinition(id: string) {
return get<WorkflowDefinitionRecord>('/workflows/definition/detail', { params: { id } });
}
export function saveWorkflowDefinition(data: WorkflowDefinitionRecord) {
return post<boolean, WorkflowDefinitionRecord>('/workflows/definition/save', data);
}
export function getWorkflowWorkspace(projectId: string, workflowId?: string) {
return get<WorkflowWorkspace>('/workflow-workspace/detail', {
params: {
projectId,
workflowId,
},
});
}