feat(workflow): 补齐项目版本运行与工作台链路
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.bruce.workflow.service;
|
||||
|
||||
import com.bruce.workflow.vo.WorkflowWorkspaceVO;
|
||||
|
||||
public interface IWorkflowWorkspaceService {
|
||||
|
||||
WorkflowWorkspaceVO getWorkspace(Long projectId, Long workflowId);
|
||||
}
|
||||
@@ -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("项目名称不能为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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名称不能为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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不能为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -21,4 +21,6 @@ public class WorkflowDefinitionVO {
|
||||
private String status;
|
||||
|
||||
private String description;
|
||||
|
||||
private String remark;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
44
frontend/src/api/__tests__/workflow.spec.ts
Normal file
44
frontend/src/api/__tests__/workflow.spec.ts
Normal 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' },
|
||||
});
|
||||
});
|
||||
});
|
||||
87
frontend/src/api/workflow.ts
Normal file
87
frontend/src/api/workflow.ts
Normal 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,
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user