删除掉所有没用的工作流相关的类,重新开发。

This commit is contained in:
dengqichen 2024-12-09 18:39:21 +08:00
parent 216839dd59
commit 01ab6721c6
108 changed files with 1399 additions and 5474 deletions

View File

@ -0,0 +1,670 @@
# 工作流引擎改造方案设计文档
## 1. 现状分析
### 1.1 现有组件
1. 工作流引擎核心组件
- `AbstractNodeExecutor`: 节点执行器抽象类,提供基础的节点执行逻辑
- `WorkflowGraph`: 工作流图模型,管理节点和转换关系
- `INodeExecutor`: 节点执行器接口
- `WorkflowInstance`: 工作流实例实体
- `NodeInstance`: 节点实例实体
2. 数据模型
```sql
-- 工作流定义表
CREATE TABLE wf_workflow_definition (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
code VARCHAR(100) NOT NULL COMMENT '工作流编码',
name VARCHAR(100) NOT NULL COMMENT '工作流名称',
description VARCHAR(255) NULL COMMENT '工作流描述',
status TINYINT NOT NULL COMMENT '工作流状态0草稿1已发布2已禁用',
version_no INT NOT NULL DEFAULT 1 COMMENT '版本号',
node_config TEXT NULL COMMENT '节点配置(JSON)',
transition_config TEXT NULL COMMENT '流转配置(JSON)',
form_definition TEXT NULL COMMENT '表单定义(JSON)',
graph_definition TEXT NULL COMMENT '图形信息(JSON)',
enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用',
CONSTRAINT UK_workflow_definition_code_version UNIQUE (code, version_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流定义表';
-- 工作流实例表
CREATE TABLE wf_workflow_instance (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
business_key VARCHAR(100) NOT NULL COMMENT '业务标识',
status TINYINT NOT NULL COMMENT '状态0已创建1等待执行2执行中3已完成4执行失败5已取消6已暂停7已终止',
start_time DATETIME(6) NULL COMMENT '开始时间',
end_time DATETIME(6) NULL COMMENT '结束时间',
error TEXT NULL COMMENT '错误信息',
CONSTRAINT FK_workflow_instance_definition FOREIGN KEY (workflow_definition_id) REFERENCES wf_workflow_definition (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表';
-- 节点实例表
CREATE TABLE wf_node_instance (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
node_id VARCHAR(100) NOT NULL COMMENT '节点ID',
node_type TINYINT NOT NULL COMMENT '节点类型0开始节点1结束节点2任务节点3网关节点4子流程节点5Shell脚本节点6审批节点7Jenkins任务节点8Git操作节点',
name VARCHAR(100) NOT NULL COMMENT '节点名称',
status TINYINT NOT NULL COMMENT '状态0已创建1等待执行2执行中3已完成4执行失败5已取消6已暂停7已终止',
start_time DATETIME(6) NULL COMMENT '开始时间',
end_time DATETIME(6) NULL COMMENT '结束时间',
config TEXT NULL COMMENT '节点配置(JSON)',
description TEXT NULL COMMENT '节点描述',
input TEXT NULL COMMENT '输入参数(JSON)',
output TEXT NULL COMMENT '输出结果(JSON)',
error TEXT NULL COMMENT '错误信息',
pre_node_id VARCHAR(100) NULL COMMENT '前置节点ID',
CONSTRAINT FK_node_instance_workflow FOREIGN KEY (workflow_instance_id) REFERENCES wf_workflow_instance (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='节点实例表';
-- 工作流变量表
CREATE TABLE wf_workflow_variable (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
name VARCHAR(100) NOT NULL COMMENT '变量名',
value TEXT NULL COMMENT '变量值',
type VARCHAR(20) NOT NULL COMMENT '变量类型',
CONSTRAINT FK_workflow_variable_instance FOREIGN KEY (workflow_instance_id) REFERENCES wf_workflow_instance (id),
CONSTRAINT UK_workflow_variable_instance_name UNIQUE (workflow_instance_id, name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流变量表';
-- 工作流日志表
CREATE TABLE wf_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
node_id VARCHAR(50) NULL COMMENT '节点ID',
level VARCHAR(10) NOT NULL COMMENT '日志级别',
message TEXT NOT NULL COMMENT '日志内容',
detail TEXT,
create_time DATETIME NOT NULL,
create_by VARCHAR(50),
update_time DATETIME,
update_by VARCHAR(50),
version INT DEFAULT 0,
deleted BOOLEAN DEFAULT FALSE,
CONSTRAINT FK_workflow_log_instance FOREIGN KEY (workflow_instance_id) REFERENCES wf_workflow_instance (id)
);
-- 工作流权限表
CREATE TABLE wf_workflow_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
type VARCHAR(50) NOT NULL COMMENT '权限类型',
user_id BIGINT COMMENT '用户ID',
role_id BIGINT COMMENT '角色ID',
department_id BIGINT COMMENT '部门ID',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
create_by BIGINT NOT NULL COMMENT '创建人',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
update_by BIGINT NOT NULL COMMENT '更新人',
version INT NOT NULL DEFAULT 0 COMMENT '版本号',
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
INDEX idx_workflow_definition_id (workflow_definition_id),
INDEX idx_user_id (user_id),
INDEX idx_role_id (role_id),
INDEX idx_department_id (department_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工作流权限';
-- 节点类型表
CREATE TABLE wf_node_type (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
code VARCHAR(100) NOT NULL COMMENT '节点类型编码',
name VARCHAR(100) NOT NULL COMMENT '节点类型名称',
description TEXT NULL COMMENT '节点类型描述',
category VARCHAR(50) NOT NULL COMMENT '节点类型分类',
icon VARCHAR(100) NULL COMMENT '节点图标',
color VARCHAR(20) NULL COMMENT '节点颜色',
executors TEXT NULL COMMENT '执行器列表(JSON)',
config_schema TEXT NULL COMMENT '节点配置模式(JSON)',
default_config TEXT NULL COMMENT '默认配置(JSON)',
enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用',
CONSTRAINT UK_node_type_code UNIQUE (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='节点类型表';
## 2. 改造目标
### 2.1 核心需求
1. 支持审批节点
2. 实现实时日志
3. 工作流状态持久化
4. 支持程序重启后恢复执行
### 2.2 技术要求
1. 遵循现有代码规范
2. 保持向后兼容
3. 确保高可用性
4. 支持并发执行
## 3. 详细设计
### 3.1 数据库改造
#### 3.1.1 修改现有表
```sql
-- 修改工作流实例表,添加执行状态字段
ALTER TABLE wf_workflow_instance
ADD COLUMN current_node_id varchar(100) COMMENT '当前执行的节点ID',
ADD COLUMN execution_status varchar(32) COMMENT '执行状态';
-- 修改节点实例表,添加重试相关字段
ALTER TABLE wf_node_instance
ADD COLUMN retry_count INT DEFAULT 0 COMMENT '重试次数',
ADD COLUMN max_retry_count INT DEFAULT 0 COMMENT '最大重试次数',
ADD COLUMN retry_interval INT DEFAULT 0 COMMENT '重试间隔(秒)',
ADD COLUMN last_retry_time TIMESTAMP COMMENT '最后重试时间';
```
#### 3.1.2 新增审批相关表
```sql
-- 审批配置表
CREATE TABLE wf_approval_config (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
project_id VARCHAR(64) NOT NULL COMMENT '项目ID',
env_type VARCHAR(20) NOT NULL COMMENT '环境类型',
approval_type VARCHAR(20) NOT NULL COMMENT '审批类型SINGLE/MULTI/ANY',
approver_ids TEXT NOT NULL COMMENT '审批人ID列表(JSON)',
required_count INT NOT NULL COMMENT '需要的审批人数',
timeout_hours INT NOT NULL COMMENT '审批超时时间(小时)',
enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用',
CONSTRAINT UK_approval_config_project_env UNIQUE (project_id, env_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批配置表';
-- 审批实例表
CREATE TABLE wf_approval_instance (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
node_instance_id VARCHAR(64) NOT NULL COMMENT '节点实例ID',
workflow_instance_id VARCHAR(64) NOT NULL COMMENT '工作流实例ID',
project_id VARCHAR(64) NOT NULL COMMENT '项目ID',
env_type VARCHAR(20) NOT NULL COMMENT '环境类型',
status VARCHAR(20) NOT NULL COMMENT '状态PENDING/APPROVED/REJECTED/TIMEOUT',
initiator_id VARCHAR(64) NOT NULL COMMENT '发起人ID',
approved_time DATETIME(6) NULL COMMENT '审批通过时间',
approver_id VARCHAR(64) NULL COMMENT '审批人ID',
comment TEXT NULL COMMENT '审批意见',
INDEX idx_workflow_instance (workflow_instance_id),
INDEX idx_node_instance (node_instance_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批实例表';
-- 审批记录表
CREATE TABLE wf_approval_record (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
approval_instance_id BIGINT NOT NULL COMMENT '审批实例ID',
approver_id VARCHAR(64) NOT NULL COMMENT '审批人ID',
action VARCHAR(20) NOT NULL COMMENT '审批动作APPROVE/REJECT',
comment TEXT NULL COMMENT '审批意见',
CONSTRAINT FK_approval_record_instance FOREIGN KEY (approval_instance_id)
REFERENCES wf_approval_instance (id),
INDEX idx_approval_instance (approval_instance_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='审批记录表';
### 3.2 代码改造
#### 3.2.1 新增枚举
```java
/**
* 节点类型枚举扩展
*/
public enum NodeTypeEnum {
SCRIPT, // 脚本节点
HTTP, // HTTP请求节点
APPROVAL, // 审批节点
// ... 其他节点类型
}
/**
* 审批类型枚举
*/
public enum ApprovalTypeEnum {
SINGLE, // 单人审批
MULTI, // 多人会签
ANY // 任意人审批
}
/**
* 审批状态枚举
*/
public enum ApprovalStatusEnum {
PENDING, // 待审批
APPROVED, // 已通过
REJECTED, // 已拒绝
TIMEOUT // 已超时
}
```
#### 3.2.2 实体类
```java
/**
* 审批配置实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "wf_approval_config")
@LogicDelete
public class ApprovalConfig extends BaseEntity<Long> {
@Column(nullable = false)
private String projectId;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private String envType;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private ApprovalTypeEnum approvalType;
@Column(nullable = false)
private String approverIds;
@Column(nullable = false)
private Integer requiredCount;
@Column(nullable = false)
private Integer timeoutHours;
}
/**
* 审批实例实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@Table(name = "wf_approval_instance")
@LogicDelete
public class ApprovalInstance extends BaseEntity<Long> {
@Column(nullable = false)
private String nodeInstanceId;
@Column(nullable = false)
private String workflowInstanceId;
@Column(nullable = false)
private String projectId;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private String envType;
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private ApprovalStatusEnum status;
@Column(nullable = false)
private String initiatorId;
private LocalDateTime approvedTime;
private String approverId;
private String comment;
}
```
#### 3.2.3 Repository接口
```java
/**
* 审批配置Repository
*/
@Repository
public interface IApprovalConfigRepository extends IBaseRepository<ApprovalConfig, Long> {
ApprovalConfig findByProjectIdAndEnvType(String projectId, String envType);
}
/**
* 审批实例Repository
*/
@Repository
public interface IApprovalInstanceRepository extends IBaseRepository<ApprovalInstance, Long> {
List<ApprovalInstance> findByStatus(ApprovalStatusEnum status);
ApprovalInstance findByWorkflowInstanceIdAndNodeInstanceId(String workflowInstanceId, String nodeInstanceId);
}
```
#### 3.2.4 Service接口
```java
/**
* 审批服务接口
*/
public interface IApprovalService {
/**
* 创建审批实例
*/
ApprovalInstance createApprovalInstance(String nodeInstanceId, String workflowInstanceId,
String projectId, ApprovalConfig config);
/**
* 处理审批结果
*/
void handleApproval(Long approvalInstanceId, String approverId,
boolean approved, String comment);
/**
* 检查审批状态
*/
ApprovalStatusEnum checkApprovalStatus(Long approvalInstanceId);
/**
* 发送审批通知
*/
void sendApprovalNotifications(ApprovalInstance instance);
}
```
#### 3.2.5 工作流执行管理器
```java
/**
* 工作流执行管理器
*/
@Component
@Slf4j
public class WorkflowExecutionManager {
@Resource
private ThreadPoolExecutor workflowExecutor;
@Resource
private WorkflowInstanceRepository instanceRepository;
@PostConstruct
public void init() {
// 初始化线程池
workflowExecutor = new ThreadPoolExecutor(
10, 20, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),
new ThreadFactoryBuilder().setNameFormat("workflow-executor-%d").build()
);
// 恢复运行中的工作流
resumeRunningWorkflows();
}
// ... 其他方法实现
}
```
#### 3.2.6 审批节点执行器
```java
/**
* 审批节点执行器
*/
@Component
@Slf4j
public class ApprovalNodeExecutor extends AbstractNodeExecutor {
@Resource
private IApprovalService approvalService;
@Override
public NodeTypeEnum getNodeType() {
return NodeTypeEnum.APPROVAL;
}
@Override
protected void doExecute(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
// ... 实现审批节点执行逻辑
}
}
```
### 3.3 实时日志实现
#### 3.3.1 日志收集器
```java
/**
* 日志收集器
*/
@Component
@Slf4j
public class ProcessLogManager {
@Resource
private WorkflowLogService workflowLogService;
private final Map<String, CircularBuffer> logBuffers = new ConcurrentHashMap<>();
private final Map<String, List<Consumer<String>>> subscribers = new ConcurrentHashMap<>();
// ... 实现日志收集和推送逻辑
}
```
#### 3.3.2 SSE控制器
```java
/**
* 日志SSE控制器
*/
@RestController
@RequestMapping("/api/v1/logs")
@Tag(name = "工作流日志", description = "工作流日志相关接口")
public class LogController {
@GetMapping(value = "/{nodeInstanceId}/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter streamLogs(@PathVariable String nodeInstanceId) {
// ... 实现SSE日志推送
}
}
```
## 4. 开发计划
### 4.1 核心功能改造
#### 4.1.1 工作流引擎核心
1. **状态管理优化**
- 实现工作流实例状态机,支持更细粒度的状态转换
- 增加节点实例的状态追踪和历史记录
- 添加工作流暂停/恢复机制
2. **并发控制增强**
- 实现基于数据库的分布式锁
- 添加节点级别的并发控制
- 优化多实例并行执行的性能
3. **错误处理机制**
- 完善节点执行失败的重试机制
- 实现节点回滚功能
- 添加全局异常处理和日志记录
- 支持自定义错误处理策略
#### 4.1.2 审批功能实现
1. **审批节点开发**
- 实现审批节点执行器
- 支持多种审批模式(单人、多人、任意人)
- 添加审批超时处理机制
- 实现审批回调通知
2. **审批流程管理**
- 开发审批配置管理接口
- 实现审批人动态配置
- 支持审批规则的版本控制
- 添加审批历史查询功能
3. **审批UI组件**
- 开发审批任务列表
- 实现审批详情页面
- 添加审批操作接口
- 支持审批意见和附件上传
### 4.2 新增功能
#### 4.2.1 工作流监控
1. **实时监控**
- 实现工作流执行状态实时展示
- 添加节点执行时间统计
- 支持执行瓶颈分析
- 实现资源使用监控
2. **报警机制**
- 添加执行超时告警
- 实现错误率监控告警
- 支持自定义告警规则
- 集成多种告警通道邮件、短信、webhook等
#### 4.2.2 工作流分析
1. **统计分析**
- 实现工作流执行趋势分析
- 添加节点执行效率分析
- 支持审批效率统计
- 开发自定义报表功能
2. **可视化展示**
- 实现工作流执行路径可视化
- 添加状态转换图表展示
- 支持审批流程可视化
- 开发数据大屏功能
### 4.3 性能优化
1. **数据库优化**
- 优化表结构和索引
- 实现分库分表方案
- 添加缓存机制
- 优化大数据量查询性能
2. **执行引擎优化**
- 优化线程池管理
- 实现节点执行预热
- 添加资源限流机制
- 优化内存使用
### 4.4 开发排期
#### Phase 1: 核心功能改造4周
- Week 1-2: 工作流引擎核心改造
- Week 3-4: 审批功能实现
#### Phase 2: 新增功能开发3周
- Week 1-2: 工作流监控功能
- Week 3: 工作流分析功能
#### Phase 3: 性能优化2周
- Week 1: 数据库优化
- Week 2: 执行引擎优化
#### Phase 4: 测试与上线1周
- Day 1-3: 功能测试与bug修复
- Day 4-5: 性能测试与优化
- Day 6-7: 准备上线与部署
### 4.5 风险评估
1. **技术风险**
- 分布式环境下的状态一致性
- 大规模并发下的性能表现
- 复杂工作流的稳定性保证
2. **项目风险**
- 开发周期可能超出预期
- 新旧版本兼容性问题
- 现有业务的平滑迁移
### 4.6 后续规划
1. **功能扩展**
- 支持更多类型的节点
- 添加工作流模板功能
- 实现工作流版本管理
- 支持跨系统工作流
2. **生态建设**
- 开发SDK工具包
- 提供更多开箱即用的组件
- 完善开发文档
- 建设示例中心
## 5. 注意事项
1. 数据迁移
- 需要处理已有的运行中工作流
- 确保数据一致性
2. 性能考虑
- 控制线程池大小
- 合理设置日志缓冲区
- 定期清理历史数据
3. 监控告警
- 添加工作流执行监控
- 配置审批超时告警
- 监控系统资源使用
## 6. 后续优化
1. 功能优化
- 支持更多审批策略
- 优化日志查询性能
- 添加工作流统计分析
2. 性能优化
- 引入分布式锁
- 优化数据库查询
- 添加缓存机制
3. 运维优化
- 完善监控指标
- 优化日志清理
- 添加性能诊断工具

View File

@ -26,6 +26,7 @@
<jjwt.version>0.12.3</jjwt.version> <jjwt.version>0.12.3</jjwt.version>
<springdoc.version>2.2.0</springdoc.version> <springdoc.version>2.2.0</springdoc.version>
<hutool.version>5.8.23</hutool.version> <hutool.version>5.8.23</hutool.version>
<flowable.version>7.0.0</flowable.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@ -201,6 +202,17 @@
<artifactId>caffeine</artifactId> <artifactId>caffeine</artifactId>
<version>3.1.8</version> <version>3.1.8</version>
</dependency> </dependency>
<!-- Flowable -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>${flowable.version}</version>
</dependency>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-bpmn-layout</artifactId>
<version>${flowable.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,25 +0,0 @@
package com.qqchen.deploy.backend.workflow.api;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.NodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.query.NodeInstanceQuery;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/v1/node-instances")
@Tag(name = "节点实例管理", description = "节点实例管理相关接口")
public class NodeInstanceApiController extends BaseController<NodeInstance, NodeInstanceDTO, Long, NodeInstanceQuery> {
@Override
protected void exportData(HttpServletResponse response, List<NodeInstanceDTO> data) {
}
}

View File

@ -1,33 +0,0 @@
package com.qqchen.deploy.backend.workflow.api;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.NodeTypeDTO;
import com.qqchen.deploy.backend.workflow.dto.query.NodeTypeQuery;
import com.qqchen.deploy.backend.workflow.entity.NodeType;
import com.qqchen.deploy.backend.workflow.service.INodeTypeService;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 节点类型接口
*/
@Slf4j
@Tag(name = "节点类型管理", description = "节点类型相关接口")
@RestController
@RequestMapping("/api/v1/node-types")
public class NodeTypeApiController extends BaseController<NodeType, NodeTypeDTO, Long, NodeTypeQuery> {
@Resource
private INodeTypeService nodeTypeService;
@Override
protected void exportData(HttpServletResponse response, List<NodeTypeDTO> data) {
}
}

View File

@ -1,59 +0,0 @@
package com.qqchen.deploy.backend.workflow.api;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.query.WorkflowDefinitionQuery;
import com.qqchen.deploy.backend.workflow.service.IWorkflowDefinitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow-definitions")
@Tag(name = "工作流定义管理", description = "工作流定义管理相关接口")
public class WorkflowDefinitionApiController extends BaseController<WorkflowDefinition, WorkflowDefinitionDTO, Long, WorkflowDefinitionQuery> {
@Resource
private IWorkflowDefinitionService workflowDefinitionService;
@Operation(summary = "发布工作流")
@PostMapping("/{id}/publish")
public Response<Void> publish(
@Parameter(description = "工作流定义ID", required = true) @PathVariable Long id
) {
workflowDefinitionService.publish(id);
return Response.success();
}
@Operation(summary = "禁用工作流")
@PostMapping("/{id}/disable")
public Response<Void> disable(
@Parameter(description = "工作流定义ID", required = true) @PathVariable Long id
) {
workflowDefinitionService.disable(id);
return Response.success();
}
@Operation(summary = "启用工作流")
@PostMapping("/{id}/enable")
public Response<Void> enable(
@Parameter(description = "工作流定义ID", required = true) @PathVariable Long id
) {
workflowDefinitionService.enable(id);
return Response.success();
}
@Override
protected void exportData(HttpServletResponse response, List<WorkflowDefinitionDTO> data) {
}
}

View File

@ -1,52 +0,0 @@
package com.qqchen.deploy.backend.workflow.api;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.dto.request.WorkflowStartRequest;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.service.IWorkflowEngineService;
import com.qqchen.deploy.backend.workflow.query.WorkflowInstanceQuery;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import com.qqchen.deploy.backend.workflow.converter.WorkflowInstanceConverter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow-instances")
@Tag(name = "工作流实例管理", description = "工作流实例管理相关接口")
public class WorkflowInstanceApiController extends BaseController<WorkflowInstance, WorkflowInstanceDTO, Long, WorkflowInstanceQuery> {
@Resource
private IWorkflowEngineService workflowEngine;
@Resource
private IWorkflowInstanceService workflowInstanceService;
@Resource
private WorkflowInstanceConverter converter;
@Operation(summary = "启动工作流实例")
@PostMapping("/start")
public Response<WorkflowInstanceDTO> startWorkflow(@RequestBody WorkflowStartRequest request) {
return Response.success(converter.toDto(workflowEngine.startWorkflow(request)));
}
@Override
public void exportData(HttpServletResponse response, List<WorkflowInstanceDTO> data) {
// TODO: 实现导出功能
}
}

View File

@ -1,64 +0,0 @@
package com.qqchen.deploy.backend.workflow.api;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.WorkflowLogDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
import com.qqchen.deploy.backend.workflow.query.WorkflowLogQuery;
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow-logs")
@Tag(name = "工作流日志管理", description = "工作流日志管理相关接口")
public class WorkflowLogApiController extends BaseController<WorkflowLog, WorkflowLogDTO, Long, WorkflowLogQuery> {
@Resource
private IWorkflowLogService workflowLogService;
@Operation(summary = "查询工作流实例日志")
@GetMapping("/workflow/{workflowInstanceId}")
public Response<List<WorkflowLogDTO>> getWorkflowLogs(
@Parameter(description = "工作流实例ID", required = true) @PathVariable Long workflowInstanceId
) {
return Response.success(workflowLogService.getLogs(workflowInstanceId));
}
@Operation(summary = "查询节点实例日志")
@GetMapping("/node/{workflowInstanceId}/{nodeId}")
public Response<List<WorkflowLogDTO>> getNodeLogs(
@Parameter(description = "工作流实例ID", required = true) @PathVariable Long workflowInstanceId,
@Parameter(description = "节点ID", required = true) @PathVariable String nodeId
) {
return Response.success(workflowLogService.getNodeLogs(workflowInstanceId, nodeId));
}
@Operation(summary = "记录日志")
@PostMapping("/record")
public Response<Void> recordLog(
@Parameter(description = "工作流实例ID", required = true) @RequestParam Long workflowInstanceId,
@Parameter(description = "节点ID") @RequestParam(required = false) String nodeId,
@Parameter(description = "日志内容", required = true) @RequestParam String message,
@Parameter(description = "日志级别", required = true) @RequestParam LogLevelEnum level,
@Parameter(description = "详细信息") @RequestParam(required = false) String detail
) {
workflowLogService.recordLog(workflowInstanceId, nodeId, message, level, detail);
return Response.success();
}
@Override
public void exportData(HttpServletResponse response, List<WorkflowLogDTO> data) {
// TODO: 实现导出功能
}
}

View File

@ -0,0 +1,20 @@
package com.qqchen.deploy.backend.workflow.config;
import org.flowable.spring.SpringProcessEngineConfiguration;
import org.flowable.spring.boot.EngineConfigurationConfigurer;
import org.springframework.context.annotation.Configuration;
/**
* Flowable配置类
*/
@Configuration
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
@Override
public void configure(SpringProcessEngineConfiguration engineConfiguration) {
engineConfiguration.setActivityFontName("宋体");
engineConfiguration.setLabelFontName("宋体");
engineConfiguration.setAnnotationFontName("宋体");
engineConfiguration.setDatabaseSchemaUpdate("true");
}
}

View File

@ -0,0 +1,72 @@
package com.qqchen.deploy.backend.workflow.controller;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.query.WorkflowDefinitionQuery;
import com.qqchen.deploy.backend.workflow.service.IWorkflowDefinitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 工作流定义控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow/definition")
@Tag(name = "工作流定义管理", description = "工作流定义管理相关接口")
public class WorkflowDefinitionController extends BaseController<WorkflowDefinition, WorkflowDefinitionDTO, Long, WorkflowDefinitionQuery> {
@Resource
private IWorkflowDefinitionService workflowDefinitionService;
@Operation(summary = "部署工作流")
@PostMapping("/deploy")
public Response<WorkflowDefinitionDTO> deployWorkflow(@RequestBody WorkflowDefinitionDTO dto) {
return Response.success(workflowDefinitionService.deployWorkflow(dto));
}
@Operation(summary = "启动工作流实例")
@PostMapping("/start")
public Response<String> startWorkflow(
@Parameter(description = "流程标识", required = true) @RequestParam String processKey,
@Parameter(description = "业务标识", required = true) @RequestParam String businessKey,
@Parameter(description = "流程变量") @RequestBody(required = false) Map<String, Object> variables
) {
ProcessInstance instance = workflowDefinitionService.startWorkflow(processKey, businessKey, variables);
return Response.success(instance.getId());
}
@Operation(summary = "挂起工作流实例")
@PostMapping("/{processInstanceId}/suspend")
public Response<Void> suspendWorkflow(
@Parameter(description = "流程实例ID", required = true) @PathVariable String processInstanceId
) {
workflowDefinitionService.suspendWorkflow(processInstanceId);
return Response.success();
}
@Operation(summary = "恢复工作流实例")
@PostMapping("/{processInstanceId}/resume")
public Response<Void> resumeWorkflow(
@Parameter(description = "流程实例ID", required = true) @PathVariable String processInstanceId
) {
workflowDefinitionService.resumeWorkflow(processInstanceId);
return Response.success();
}
@Override
protected void exportData(HttpServletResponse response, List<WorkflowDefinitionDTO> data) {
}
}

View File

@ -0,0 +1,40 @@
package com.qqchen.deploy.backend.workflow.controller;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDesignDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.query.WorkflowDefinitionQuery;
import com.qqchen.deploy.backend.workflow.service.IWorkflowDesignService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 工作流设计控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow/design")
@Tag(name = "工作流设计", description = "工作流设计相关接口")
public class WorkflowDesignController extends BaseController<WorkflowDefinition, WorkflowDesignDTO, Long, WorkflowDefinitionQuery> {
@Resource
private IWorkflowDesignService workflowDesignService;
@Operation(summary = "保存工作流设计")
@PostMapping("/save")
public Response<WorkflowDesignDTO> saveWorkflowDesign(@RequestBody WorkflowDesignDTO dto) throws Exception {
return Response.success(workflowDesignService.saveWorkflowDesign(dto));
}
@Override
protected void exportData(HttpServletResponse response, List<WorkflowDesignDTO> data) {
// TODO: 实现导出功能
}
}

View File

@ -1,28 +0,0 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.workflow.engine.definition.NodeExecutorDefinition;
import java.util.List;
/**
* JSON转换器接口
* 用于处理系统中的JSON序列化和反序列化
*/
public interface JsonConverter {
/**
* 将JSON字符串转换为执行器定义列表
*
* @param json JSON字符串
* @return 执行器定义列表
*/
List<NodeExecutorDefinition> toExecutorList(String json);
/**
* 将执行器定义列表转换为JSON字符串
*
* @param executors 执行器定义列表
* @return JSON字符串
*/
String fromExecutorList(List<NodeExecutorDefinition> executors);
}

View File

@ -1,45 +0,0 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
/**
* JSON对象转换器
* 用于在数据库字符串和Java对象之间进行转换
*/
@Slf4j
@Converter
public class JsonObjectConverter implements AttributeConverter<Object, String> {
private static final ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(Object attribute) {
if (attribute == null) {
return null;
}
try {
return objectMapper.writeValueAsString(attribute);
} catch (JsonProcessingException e) {
log.error("Error converting object to json string", e);
return null;
}
}
@Override
public Object convertToEntityAttribute(String dbData) {
if (!StringUtils.hasText(dbData)) {
return null;
}
try {
return objectMapper.readValue(dbData, Object.class);
} catch (JsonProcessingException e) {
log.error("Error converting json string to object", e);
return null;
}
}
}

View File

@ -1,43 +0,0 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.workflow.dto.NodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
/**
* 节点实例转换器
*/
@Mapper(config = BaseConverter.class)
public interface NodeInstanceConverter extends BaseConverter<NodeInstance, NodeInstanceDTO> {
@Override
@Mapping(target = "nodeId", source = "nodeId")
@Mapping(target = "nodeType", source = "nodeType")
@Mapping(target = "name", source = "name")
@Mapping(target = "status", source = "status")
@Mapping(target = "startTime", source = "startTime")
@Mapping(target = "endTime", source = "endTime")
@Mapping(target = "config", source = "config")
@Mapping(target = "input", source = "input")
@Mapping(target = "output", source = "output")
@Mapping(target = "error", source = "error")
@Mapping(target = "preNodeId", source = "preNodeId")
NodeInstanceDTO toDto(NodeInstance entity);
@Override
@Mapping(target = "workflowInstance", ignore = true)
@Mapping(target = "nodeId", source = "nodeId")
@Mapping(target = "nodeType", source = "nodeType")
@Mapping(target = "name", source = "name")
@Mapping(target = "status", source = "status")
@Mapping(target = "startTime", source = "startTime")
@Mapping(target = "endTime", source = "endTime")
@Mapping(target = "config", source = "config")
@Mapping(target = "input", source = "input")
@Mapping(target = "output", source = "output")
@Mapping(target = "error", source = "error")
@Mapping(target = "preNodeId", source = "preNodeId")
NodeInstance toEntity(NodeInstanceDTO dto);
}

View File

@ -1,36 +0,0 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.workflow.dto.NodeTypeDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeType;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 节点类型转换器
*/
@Mapper(config = BaseConverter.class, componentModel = "spring", uses = {JsonConverter.class})
public abstract class NodeTypeConverter implements BaseConverter<NodeType, NodeTypeDTO> {
@Autowired
protected JsonConverter jsonConverter;
@Override
@Mapping(target = "executors", expression = "java(jsonConverter.toExecutorList(entity.getExecutors()))")
public abstract NodeTypeDTO toDto(NodeType entity);
@Override
@Mapping(target = "executors", expression = "java(jsonConverter.fromExecutorList(dto.getExecutors()))")
public abstract NodeType toEntity(NodeTypeDTO dto);
@Override
public void updateEntity(NodeType entity, NodeTypeDTO dto) {
// 使用MapStruct生成的更新方法
updateEntityInternal(entity, dto);
}
@Mapping(target = "executors", expression = "java(jsonConverter.fromExecutorList(dto.getExecutors()))")
protected abstract void updateEntityInternal(@MappingTarget NodeType entity, NodeTypeDTO dto);
}

View File

@ -8,6 +8,6 @@ import org.mapstruct.Mapper;
/** /**
* 工作流定义转换器 * 工作流定义转换器
*/ */
@Mapper(componentModel = "spring") @Mapper(config = BaseConverter.class)
public interface WorkflowDefinitionConverter extends BaseConverter<WorkflowDefinition, WorkflowDefinitionDTO> { public interface WorkflowDefinitionConverter extends BaseConverter<WorkflowDefinition, WorkflowDefinitionDTO> {
} }

View File

@ -1,22 +0,0 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
/**
* 工作流实例转换器
*/
@Mapper(config = BaseConverter.class)
public interface WorkflowInstanceConverter extends BaseConverter<WorkflowInstance, WorkflowInstanceDTO> {
@Override
@Mapping(target = "definitionId", source = "workflowDefinition.id")
WorkflowInstanceDTO toDto(WorkflowInstance entity);
@Override
@Mapping(target = "workflowDefinition.id", source = "definitionId")
WorkflowInstance toEntity(WorkflowInstanceDTO dto);
}

View File

@ -1,13 +0,0 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowLogDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
import org.mapstruct.Mapper;
/**
* 工作流日志转换器
*/
@Mapper(config = BaseConverter.class)
public interface WorkflowLogConverter extends BaseConverter<WorkflowLog, WorkflowLogDTO> {
}

View File

@ -1,13 +0,0 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowVariableDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable;
import org.mapstruct.Mapper;
/**
* 工作流变量转换器
*/
@Mapper(config = BaseConverter.class)
public interface WorkflowVariableConverter extends BaseConverter<WorkflowVariable, WorkflowVariableDTO> {
}

View File

@ -1,67 +0,0 @@
package com.qqchen.deploy.backend.workflow.converter.impl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.SystemException;
import com.qqchen.deploy.backend.workflow.converter.JsonConverter;
import com.qqchen.deploy.backend.workflow.engine.definition.NodeExecutorDefinition;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* JSON转换器默认实现
* 使用Jackson进行JSON序列化和反序列化
*/
@Component
public class DefaultJsonConverter implements JsonConverter {
private final ObjectMapper objectMapper;
public DefaultJsonConverter(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public List<NodeExecutorDefinition> toExecutorList(String json) {
if (json == null || json.isEmpty()) {
return new ArrayList<>();
}
try {
// 先将JSON解析为JsonNode以便我们可以手动处理configSchema字段
JsonNode rootNode = objectMapper.readTree(json);
if (!rootNode.isArray()) {
throw new SystemException(ResponseCode.WORKFLOW_CONFIG_INVALID, new Object[]{"Executors JSON must be an array"}, null);
}
List<NodeExecutorDefinition> result = new ArrayList<>();
for (JsonNode node : rootNode) {
// 将configSchema转换为字符串
if (node.has("configSchema")) {
((ObjectNode) node).put("configSchema", node.get("configSchema").toString());
}
// 将处理后的节点转换为TaskExecutorDefinition对象
result.add(objectMapper.treeToValue(node, NodeExecutorDefinition.class));
}
return result;
} catch (JsonProcessingException e) {
throw new SystemException(ResponseCode.WORKFLOW_CONFIG_INVALID, new Object[]{"Failed to parse executors JSON: " + e.getMessage()}, e);
}
}
@Override
public String fromExecutorList(List<NodeExecutorDefinition> executors) {
if (executors == null || executors.isEmpty()) {
return "[]";
}
try {
return objectMapper.writeValueAsString(executors);
} catch (JsonProcessingException e) {
throw new SystemException("Failed to serialize executors: " + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,29 @@
package com.qqchen.deploy.backend.workflow.delegate;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
/**
* 简单任务委托实现
*/
@Slf4j
@Component
public class SimpleTaskDelegate implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) {
String taskName = execution.getCurrentFlowElement().getName();
log.info("Executing task: {}", taskName);
// 获取流程变量
String input = (String) execution.getVariable("input");
log.info("Task input: {}", input);
// 设置输出变量
execution.setVariable("output", "Processed: " + input);
log.info("Task completed: {}", taskName);
}
}

View File

@ -1,55 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 节点定义DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NodeDefinitionDTO extends BaseDTO {
/**
* 节点ID
*/
@NotBlank(message = "节点ID不能为空")
private String nodeId;
/**
* 节点名称
*/
@NotBlank(message = "节点名称不能为空")
private String name;
/**
* 节点类型
*/
@NotNull(message = "节点类型不能为空")
private NodeTypeEnum type;
/**
* 节点配置(JSON)
*/
private String config;
/**
* 节点描述
*/
private String description;
/**
* 工作流定义ID
*/
@NotNull(message = "工作流定义ID不能为空")
private Long workflowDefinitionId;
/**
* 排序号
*/
private Integer orderNum;
}

View File

@ -1,89 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 节点实例DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NodeInstanceDTO extends BaseDTO {
/**
* 工作流实例ID
*/
@NotNull(message = "工作流实例ID不能为空")
private Long workflowInstanceId;
/**
* 节点ID
*/
@NotBlank(message = "节点ID不能为空")
private String nodeId;
/**
* 节点类型
*/
@NotNull(message = "节点类型不能为空")
private NodeTypeEnum nodeType;
/**
* 节点名称
*/
@NotBlank(message = "节点名称不能为空")
private String name;
/**
* 节点状态
*/
@NotNull(message = "节点状态不能为空")
private NodeStatusEnum status;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
/**
* 节点配置(JSON)
*/
private String config;
/**
* 节点描述
*/
private String description;
/**
* 输入参数(JSON)
*/
private String input;
/**
* 输出结果(JSON)
*/
private String output;
/**
* 错误信息
*/
private String error;
/**
* 前置节点ID
*/
private String preNodeId;
}

View File

@ -1,88 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.workflow.engine.definition.NodeExecutorDefinition;
import com.qqchen.deploy.backend.workflow.enums.NodeCategoryEnum;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 节点类型DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NodeTypeDTO extends BaseDTO {
/**
* 节点类型编码
*/
@NotBlank(message = "节点类型编码不能为空")
private String code;
/**
* 节点类型名称
*/
@NotBlank(message = "节点类型名称不能为空")
private String name;
/**
* 节点类型描述
*/
private String description;
/**
* 节点类型分类
*/
@NotNull(message = "节点类型分类不能为空")
private NodeCategoryEnum category;
/**
* 节点图标
*/
private String icon;
/**
* 节点颜色
*/
private String color;
/**
* 任务执行器定义列表
* 定义该节点类型支持的所有任务执行器
* 说明
* 1. 只有TASK类型的节点才需要配置执行器
* 2. 一个任务节点可以支持多种执行器
* 3. 每种执行器都有自己的配置模式
*/
@Valid
private List<NodeExecutorDefinition> executors;
/**
* 节点配置模式(JSON)
* 使用JSON Schema格式定义节点的配置结构
* 说明
* 1. 基础配置所有类型节点都需要的配置如节点名称描述等
* 2. 分类配置不同分类节点特有的配置
* - TASK任务节点配置如执行器选择重试策略等
* - GATEWAY网关节点配置如条件表达式分支策略等
* - EVENT事件节点配置如事件类型触发条件等
*/
private String configSchema;
/**
* 默认配置(JSON)
* 符合configSchema定义的默认配置值
*/
private String defaultConfig;
/**
* 是否启用
*/
@NotNull(message = "是否启用不能为空")
private Boolean enabled = true;
}

View File

@ -1,9 +1,6 @@
package com.qqchen.deploy.backend.workflow.dto; package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO; import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -15,60 +12,27 @@ import lombok.EqualsAndHashCode;
public class WorkflowDefinitionDTO extends BaseDTO { public class WorkflowDefinitionDTO extends BaseDTO {
/** /**
* 工作流编码 * 流程名称
*/ */
@NotBlank(message = "工作流编码不能为空")
private String code;
/**
* 工作流名称
*/
@NotBlank(message = "工作流名称不能为空")
private String name; private String name;
/** /**
* 工作流描述 * 流程标识
*/ */
private String description; private String key;
/** /**
* 工作流状态 * 流程版本
*/
private WorkflowDefinitionStatusEnum status;
/**
* 版本号
*/ */
private Integer version; private Integer version;
/** /**
* 节点配置(JSON) * BPMN XML内容
*/ */
private String nodeConfig; private String bpmnXml;
/** /**
* 转配置(JSON) * 程描述
*/ */
private String transitionConfig; private String description;
/**
* 表单定义(JSON)
*/
private String formDefinition;
/**
* 图形信息(JSON)
*/
private String graphDefinition;
/**
* 是否启用
*/
@NotNull(message = "是否启用不能为空")
private Boolean enabled = true;
/**
* 备注
*/
private String remark;
} }

View File

@ -0,0 +1,30 @@
package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import lombok.Data;
/**
* 工作流设计DTO
*/
@Data
public class WorkflowDesignDTO extends BaseDTO {
/**
* 流程名称
*/
private String name;
/**
* 流程标识
*/
private String key;
/**
* 流程描述
*/
private String description;
/**
* 流程图数据
*/
private String graphJson;
}

View File

@ -1,51 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnum;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowInstanceDTO extends BaseDTO {
/**
* 工作流定义ID
*/
@NotNull(message = "工作流定义ID不能为空")
private Long definitionId;
/**
* 业务标识
*/
@NotNull(message = "业务标识不能为空")
private String businessKey;
/**
* 工作流状态
*/
private WorkflowInstanceStatusEnum status;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
/**
* 工作流变量(JSON)
*/
private String variables;
/**
* 错误信息
*/
private String error;
}

View File

@ -1,46 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 工作流日志DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowLogDTO extends BaseDTO {
/**
* 节点ID
*/
private String nodeId;
/**
* 日志级别
*/
private LogLevelEnum level;
/**
* 日志内容
*/
private String message;
/**
* 详细信息
*/
private String detail;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 创建人
*/
private String createBy;
}

View File

@ -1,45 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 工作流变量DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowVariableDTO extends BaseDTO {
/**
* 工作流实例ID
*/
@NotNull(message = "工作流实例ID不能为空")
private Long workflowInstanceId;
/**
* 变量名称
*/
@NotBlank(message = "变量名称不能为空")
private String name;
/**
* 变量值(JSON)
*/
@NotBlank(message = "变量值不能为空")
private String value;
/**
* 变量类型
*/
@NotBlank(message = "变量类型不能为空")
private String type;
/**
* 变量作用域
*/
@NotBlank(message = "变量作用域不能为空")
private String scope;
}

View File

@ -1,47 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 节点实例查询条件
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NodeInstanceQuery extends BaseQuery {
/**
* 工作流实例ID
*/
@QueryField(field = "workflowInstanceId")
private Long workflowInstanceId;
/**
* 节点ID
*/
@QueryField(field = "nodeId", type = QueryType.LIKE)
private String nodeId;
/**
* 节点类型
*/
@QueryField(field = "nodeType")
private NodeTypeEnum nodeType;
/**
* 节点名称
*/
@QueryField(field = "name", type = QueryType.LIKE)
private String name;
/**
* 节点状态
*/
@QueryField(field = "status")
private NodeStatusEnum status;
}

View File

@ -1,34 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import com.qqchen.deploy.backend.workflow.enums.NodeCategoryEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 节点类型查询对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NodeTypeQuery extends BaseQuery {
/**
* 节点类型编码
*/
@QueryField(field = "code", type = QueryType.LIKE)
private String code;
/**
* 节点类型名称
*/
@QueryField(field = "name", type = QueryType.LIKE)
private String name;
/**
* 节点类型分类
*/
@QueryField(field = "category")
private NodeCategoryEnum category;
}

View File

@ -1,19 +0,0 @@
package com.qqchen.deploy.backend.workflow.dto.request;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.Map;
@Data
public class WorkflowStartRequest {
@NotBlank(message = "工作流编码")
private String workflowCode;
@NotBlank(message = "业务标识")
private String businessKey;
@NotBlank(message = "环境变量")
private Map<String, Object> variables;
}

View File

@ -1,24 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.config;
import com.qqchen.deploy.backend.workflow.engine.executor.INodeExecutor;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
@Configuration
public class WorkflowEngineConfig {
@Bean
public Map<NodeTypeEnum, INodeExecutor> nodeExecutorMap(List<INodeExecutor> executors) {
return executors.stream()
.collect(Collectors.toMap(
INodeExecutor::getNodeType,
Function.identity()
));
}
}

View File

@ -1,77 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.definition;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class NodeExecutorDefinition {
@NotBlank(message = "执行器编码不能为空")
private String code;
/**
* 执行器名称
* 用于前端展示应该是用户友好的名称
* 例如
* - Shell脚本执行器
* - Jenkins构建执行器
* - HTTP接口调用执行器
*/
@NotBlank(message = "执行器名称不能为空")
private String name;
/**
* 执行器描述
* 详细说明执行器的
* 1. 主要功能和使用场景
* 2. 配置参数说明
* 3. 注意事项和限制条件
*/
private String description;
/**
* 执行器配置模式
* 使用JSON Schema格式定义执行器的配置结构
* 用于
* 1. 前端动态渲染配置表单
* 2. 后端验证配置参数
*
* 示例 - Shell执行器配置模式
* {
* "type": "object",
* "required": ["script"],
* "properties": {
* "script": {
* "type": "string",
* "title": "脚本内容",
* "format": "shell",
* "description": "需要执行的Shell脚本内容"
* },
* "timeout": {
* "type": "number",
* "title": "超时时间",
* "description": "脚本执行的最大时间(秒)",
* "minimum": 1,
* "maximum": 3600,
* "default": 300
* },
* "workingDir": {
* "type": "string",
* "title": "工作目录",
* "description": "脚本执行的工作目录",
* "default": "/tmp"
* }
* }
* }
*/
private String configSchema;
/**
* 默认配置
* 符合configSchema定义的默认配置值
* 用于
* 1. 新建节点时的默认值
* 2. 重置配置时的参考值
*/
private String defaultConfig;
}

View File

@ -1,19 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.exception;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
public class WorkflowEngineException extends BusinessException {
public WorkflowEngineException(ResponseCode code) {
super(code);
}
public WorkflowEngineException(ResponseCode code, String... args) {
super(code, args);
}
public WorkflowEngineException(ResponseCode code, Throwable cause) {
super(code, new Object[]{cause.getMessage()});
}
}

View File

@ -1,28 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.exception;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import lombok.Getter;
/**
* 工作流节点相关异常
*/
@Getter
public class WorkflowNodeException extends BusinessException {
private final String nodeId;
private final String nodeName;
public WorkflowNodeException(ResponseCode code, String nodeId, String nodeName) {
super(code, new Object[]{nodeId, nodeName});
this.nodeId = nodeId;
this.nodeName = nodeName;
}
public WorkflowNodeException(ResponseCode code, String nodeId, String nodeName, Throwable cause) {
super(code, new Object[]{nodeId, nodeName, cause.getMessage()});
this.nodeId = nodeId;
this.nodeName = nodeName;
}
}

View File

@ -1,164 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.model.NodeConfig;
import com.qqchen.deploy.backend.workflow.engine.model.WorkflowGraph;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
/**
* 抽象节点执行器
*/
@Slf4j
public abstract class AbstractNodeExecutor implements INodeExecutor {
@Resource
protected ObjectMapper objectMapper;
@Resource
protected INodeInstanceRepository nodeInstanceRepository;
@Resource
protected IWorkflowLogService workflowLogService;
@Resource
@Lazy
private Map<NodeTypeEnum, INodeExecutor> nodeExecutors;
@Override
public void execute(WorkflowInstance workflowInstance, WorkflowGraph graph, NodeConfig currentNode) {
NodeInstance nodeInstance = null;
try {
nodeInstance = createNodeInstance(workflowInstance, currentNode);
} catch (JsonProcessingException e) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
}
try {
// 1. 创建节点实例
// 1. 前置处理
beforeExecute(workflowInstance, nodeInstance);
// 2. 执行节点逻辑
doExecute(workflowInstance, nodeInstance);
// 3. 后置处理
afterExecute(workflowInstance, nodeInstance);
// 4. 更新节点状态
updateNodeStatus(nodeInstance, true);
// 4. 获取并执行下一个节点
List<NodeConfig> nextNodes = graph.getNextNodes(currentNode.getNodeId());
executeNextNodes(workflowInstance, graph, nextNodes);
} catch (Exception e) {
// 5. 异常处理
handleExecutionError(workflowInstance, nodeInstance, e);
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_EXECUTION_FAILED, e);
}
}
/**
* 执行后续节点
*/
protected void executeNextNodes(WorkflowInstance workflowInstance, WorkflowGraph graph, List<NodeConfig> nextNodes) {
for (NodeConfig nextNode : nextNodes) {
INodeExecutor executor = nodeExecutors.get(nextNode.getType());
executor.execute(workflowInstance, graph, nextNode);
}
}
/**
* 创建节点实例
*/
protected NodeInstance createNodeInstance(WorkflowInstance workflowInstance, NodeConfig config) throws JsonProcessingException {
NodeInstance node = new NodeInstance();
node.setWorkflowInstance(workflowInstance);
node.setNodeId(config.getNodeId());
node.setNodeType(config.getType());
node.setName(config.getName());
node.setConfig(objectMapper.writeValueAsString(config.getConfig()));
node.setStatus(NodeStatusEnum.RUNNING);
node.setCreateTime(LocalDateTime.now());
node.setStartTime(LocalDateTime.now());
return nodeInstanceRepository.save(node);
}
/**
* 执行节点逻辑
*/
protected abstract void doExecute(WorkflowInstance workflowInstance, NodeInstance nodeInstance);
/**
* 前置处理
*/
protected void beforeExecute(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
}
/**
* 后置处理
*/
protected void afterExecute(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
logNodeComplete(nodeInstance);
}
/**
* 处理执行异常
*/
protected void handleExecutionError(WorkflowInstance workflowInstance, NodeInstance nodeInstance, Exception e) {
log.error("Node execution failed. nodeInstance: {}, error: {}", nodeInstance.getId(), e.getMessage(), e);
logSystem(nodeInstance, LogLevelEnum.ERROR, String.format("节点执行失败: %s[%s]", nodeInstance.getName(), nodeInstance.getNodeId()), e.getMessage());
logNodeError(nodeInstance, e.getMessage());
updateNodeStatus(nodeInstance, false);
}
/**
* 更新节点状态
*/
private void updateNodeStatus(NodeInstance nodeInstance, boolean success) {
nodeInstance.setStatus(success ? NodeStatusEnum.COMPLETED : NodeStatusEnum.FAILED);
if (success) {
nodeInstance.setEndTime(LocalDateTime.now());
}
nodeInstanceRepository.save(nodeInstance);
}
/**
* 记录系统日志
*/
protected void logSystem(NodeInstance nodeInstance, LogLevelEnum level, String message, String detail) {
workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(), level, message, detail);
}
/**
* 记录节点完成日志
*/
protected void logNodeComplete(NodeInstance nodeInstance) {
workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(), LogLevelEnum.INFO, String.format("节点执行完成: %s", nodeInstance.getName()), null);
}
/**
* 记录节点错误日志
*/
protected void logNodeError(NodeInstance nodeInstance, String error) {
workflowLogService.log(nodeInstance.getWorkflowInstance(), nodeInstance.getNodeId(), LogLevelEnum.ERROR, String.format("节点执行失败: %s", nodeInstance.getName()), error);
nodeInstance.setError(error);
}
}

View File

@ -1,38 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 结束节点执行器
* 负责完成工作流实例的执行
*/
@Slf4j
@Component("endNodeExecutor")
public class EndNodeExecutor extends AbstractNodeExecutor {
@Override
protected void doExecute(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
}
@Override
public NodeTypeEnum getNodeType() {
return NodeTypeEnum.END;
}
@Override
public void validate(String config) {
}
@Override
public void terminate(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
}
}

View File

@ -1,27 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor;
import com.qqchen.deploy.backend.workflow.engine.model.NodeConfig;
import com.qqchen.deploy.backend.workflow.engine.model.WorkflowGraph;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
/**
* 节点执行器接口
*/
public interface INodeExecutor {
/**
* 获取支持的节点类型
*/
NodeTypeEnum getNodeType();
void execute(WorkflowInstance workflowInstance, WorkflowGraph graph, NodeConfig currentNode);
/**
* 验证节点配置
*/
void validate(String config);
void terminate(WorkflowInstance workflowInstance, NodeInstance nodeInstance);
}

View File

@ -1,36 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 脚本节点执行器
* 支持多种脚本语言Python, Shell, JavaScript等
*/
@Slf4j
@Component("scriptNodeExecutor")
public class ScriptNodeExecutor extends AbstractNodeExecutor {
@Override
protected void doExecute(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
}
@Override
public NodeTypeEnum getNodeType() {
return NodeTypeEnum.SCRIPT;
}
@Override
public void validate(String config) {
}
@Override
public void terminate(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
}
}

View File

@ -1,37 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 开始节点执行器
* 负责工作流实例的初始化工作
*/
@Slf4j
@Component("startNodeExecutor")
public class StartNodeExecutor extends AbstractNodeExecutor {
@Override
protected void doExecute(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
}
@Override
public NodeTypeEnum getNodeType() {
return NodeTypeEnum.START;
}
@Override
public void validate(String config) {
}
@Override
public void terminate(WorkflowInstance workflowInstance, NodeInstance nodeInstance) {
}
}

View File

@ -1,39 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 审批节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ApprovalNodeExecutorConfig extends NodeExecutorConfig {
/**
* 审批人类型USER-指定用户ROLE-指定角色LEADER-直属领导
*/
private String approverType;
/**
* 审批人ID列表用户ID或角色ID
*/
private List<Long> approverIds;
/**
* 审批策略ALL-全部通过ANY-任意通过
*/
private String strategy;
/**
* 审批超时自动处理PASS-通过REJECT-拒绝
*/
private String timeoutAction;
/**
* 审批表单定义
*/
private String formDefinition;
}

View File

@ -1,46 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 条件节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ConditionNodeExecutorConfig extends NodeExecutorConfig {
/**
* 条件表达式使用SpEL表达式
*/
private String expression;
/**
* 条件分支列表
*/
private List<Branch> branches;
/**
* 默认分支节点ID
*/
private String defaultBranchNodeId;
@Data
public static class Branch {
/**
* 分支名称
*/
private String name;
/**
* 条件表达式
*/
private String condition;
/**
* 下一个节点ID
*/
private String nextNodeId;
}
}

View File

@ -1,52 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* Git节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class GitNodeExecutorConfig extends NodeExecutorConfig {
/**
* Git仓库URL
*/
private String repositoryUrl;
/**
* 分支名称
*/
private String branch;
/**
* 标签名称
*/
private String tag;
/**
* Commit ID
*/
private String commitId;
/**
* 操作类型CLONE, PULL, PUSH, TAG, MERGE
*/
private String operation;
/**
* 目标分支合并时使用
*/
private String targetBranch;
/**
* 工作目录
*/
private String workingDirectory;
/**
* 凭证ID
*/
private Long credentialId;
}

View File

@ -1,58 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Map;
/**
* HTTP节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class HttpNodeExecutorConfig extends NodeExecutorConfig {
/**
* 请求URL
*/
private String url;
/**
* 请求方法GET, POST, PUT, DELETE
*/
private String method;
/**
* 请求头
*/
private Map<String, String> headers;
/**
* 请求参数
*/
private Map<String, String> parameters;
/**
* 请求体
*/
private String body;
/**
* 请求体格式JSON, XML, FORM
*/
private String bodyFormat;
/**
* 成功状态码
*/
private Integer successCode;
/**
* 成功响应匹配规则JSON Path表达式
*/
private String successResponse;
/**
* 凭证ID
*/
private Long credentialId;
}

View File

@ -1,43 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Map;
/**
* Jenkins节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class JenkinsNodeExecutorConfig extends NodeExecutorConfig {
/**
* Jenkins服务器ID
*/
private Long jenkinsServerId;
/**
* Jenkins任务名称
*/
private String jobName;
/**
* 构建参数
*/
private Map<String, String> parameters;
/**
* 等待构建完成
*/
private Boolean waitForComplete;
/**
* 构建超时时间分钟
*/
private Integer buildTimeout;
/**
* 构建成功条件BUILD_RESULT-构建结果TEST_RESULT-测试结果QUALITY_GATE-质量门禁
*/
private String successCondition;
}

View File

@ -1,58 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Map;
/**
* Nacos节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NacosNodeExecutorConfig extends NodeExecutorConfig {
/**
* Nacos服务器ID
*/
private Long nacosServerId;
/**
* 命名空间
*/
private String namespace;
/**
* 分组
*/
private String group;
/**
* 数据ID
*/
private String dataId;
/**
* 操作类型PUBLISH-发布配置ROLLBACK-回滚配置
*/
private String operation;
/**
* 配置内容
*/
private String content;
/**
* 配置格式TEXT, JSON, XML, YAML, PROPERTIES
*/
private String format;
/**
* 回滚版本ID回滚时使用
*/
private Long historyId;
/**
* 标签列表
*/
private Map<String, String> tags;
}

View File

@ -1,30 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.Data;
/**
* 节点配置基类
*/
public class NodeExecutorConfig {
/**
* 节点ID
*/
private String id;
/**
* 节点名称
*/
private String name;
/**
* 节点类型
*/
private NodeTypeEnum type;
/**
* 描述
*/
private String description;
}

View File

@ -1,54 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
import java.util.Map;
/**
* 通知节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NotifyNodeExecutorConfig extends NodeExecutorConfig {
/**
* 通知类型EMAIL-邮件SMS-短信DINGTALK-钉钉WEIXIN-企业微信
*/
private String notifyType;
/**
* 通知人类型USER-指定用户ROLE-指定角色LEADER-直属领导
*/
private String receiverType;
/**
* 通知人ID列表用户ID或角色ID
*/
private List<Long> receiverIds;
/**
* 通知标题
*/
private String title;
/**
* 通知内容
*/
private String content;
/**
* 通知模板ID
*/
private Long templateId;
/**
* 模板参数
*/
private Map<String, String> templateParams;
/**
* 附件列表
*/
private List<String> attachments;
}

View File

@ -1,51 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 并行节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ParallelNodeExecutorConfig extends NodeExecutorConfig {
/**
* 并行分支列表
*/
private List<Branch> branches;
/**
* 汇聚策略ALL-全部完成ANY-任一完成N-N个完成
*/
private String joinStrategy;
/**
* 需要完成的分支数量N个完成时使用
*/
private Integer requiredCount;
/**
* 下一个节点ID
*/
private String nextNodeId;
@Data
public static class Branch {
/**
* 分支名称
*/
private String name;
/**
* 起始节点ID
*/
private String startNodeId;
/**
* 结束节点ID
*/
private String endNodeId;
}
}

View File

@ -1,66 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.executor.config;
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Map;
/**
* 脚本节点配置
* 支持多种脚本语言Python, Shell, JavaScript等
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ScriptNodeExecutorConfig extends NodeExecutorConfig {
/**
* 脚本内容
*/
private String script;
/**
* 脚本语言python/shell/javascript
*/
private ScriptLanguageEnum language;
/**
* 解释器路径可选
* 例如/usr/local/bin/python3
*/
private String interpreter;
/**
* 工作目录
*/
private String workingDirectory;
/**
* 超时时间
*/
private Integer timeout;
/**
* 重试次数
*/
private Integer retryTimes;
/**
* 重试间隔
*/
private Integer retryInterval;
/**
* 环境变量
*/
private Map<String, String> environment;
/**
* 成功退出码
*/
private Integer successExitCode = 0;
/**
* 失败是否继续
*/
private Boolean continueOnFailed;
}

View File

@ -1,33 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.model;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.Data;
import java.util.Map;
/**
* 节点配置
* 用于解析工作流定义中的节点配置JSON
*/
@Data
public class NodeConfig {
/**
* 节点ID
*/
private String nodeId;
/**
* 节点类型
*/
private NodeTypeEnum type;
/**
* 节点名称
*/
private String name;
/**
* 节点配置不同类型的节点有不同的配置项
*/
private Map<String, Object> config;
}

View File

@ -1,35 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.model;
import lombok.Data;
/**
* 流转配置
* 用于解析工作流定义中的流转配置JSON
*/
@Data
public class TransitionConfig {
/**
* 来源节点ID
*/
private String from;
/**
* 目标节点ID
*/
private String to;
/**
* 流转条件
*/
private String condition;
/**
* 流转描述
*/
private String description;
/**
* 优先级数字越小优先级越高
*/
private Integer priority;
}

View File

@ -1,42 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.model;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.Builder;
import lombok.Data;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@Data
@Builder
public class WorkflowGraph {
private List<NodeConfig> nodes;
private List<TransitionConfig> transitions;
public NodeConfig getStartNode() {
return nodes.stream()
.filter(node -> node.getType() == NodeTypeEnum.START)
.findFirst()
.orElseThrow(() -> new WorkflowEngineException(ResponseCode.WORKFLOW_NOT_FOUND));
}
public List<NodeConfig> getNextNodes(String nodeId) {
return transitions.stream()
.filter(t -> t.getFrom().equals(nodeId))
.map(t -> findNodeById(t.getTo()))
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
private NodeConfig findNodeById(String nodeId) {
return nodes.stream()
.filter(n -> n.getNodeId().equals(nodeId))
.findFirst()
.orElse(null);
}
}

View File

@ -1,78 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.parser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.model.NodeConfig;
import com.qqchen.deploy.backend.workflow.engine.model.TransitionConfig;
import com.qqchen.deploy.backend.workflow.engine.model.WorkflowGraph;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 工作流定义解析器
*/
@Component
@Slf4j
public class WorkflowDefinitionParser {
private final ObjectMapper objectMapper;
public WorkflowDefinitionParser(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
public WorkflowGraph parse(WorkflowDefinition definition) {
try {
// 解析节点配置
JsonNode rootNode = objectMapper.readTree(definition.getNodeConfig());
List<NodeConfig> nodes = parseNodes(rootNode.get("nodes"));
// 解析流转配置
JsonNode transitionRoot = objectMapper.readTree(definition.getTransitionConfig());
List<TransitionConfig> transitions = parseTransitions(transitionRoot.get("transitions"));
return WorkflowGraph.builder()
.nodes(nodes)
.transitions(transitions)
.build();
} catch (Exception e) {
log.error("Failed to parse workflow definition", e);
throw new WorkflowEngineException(ResponseCode.WORKFLOW_CONFIG_ERROR);
}
}
private List<NodeConfig> parseNodes(JsonNode nodesNode) {
List<NodeConfig> nodes = new ArrayList<>();
for (JsonNode node : nodesNode) {
NodeConfig config = new NodeConfig();
config.setNodeId(node.get("id").asText());
config.setType(NodeTypeEnum.valueOf(node.get("type").asText()));
config.setName(node.get("name").asText());
config.setConfig(objectMapper.convertValue(node.get("config"), new TypeReference<Map<String, Object>>() {}));
nodes.add(config);
}
return nodes;
}
private List<TransitionConfig> parseTransitions(JsonNode transitionsNode) {
List<TransitionConfig> transitions = new ArrayList<>();
for (JsonNode transition : transitionsNode) {
TransitionConfig config = new TransitionConfig();
config.setFrom(transition.get("from").asText());
config.setTo(transition.get("to").asText());
config.setCondition(transition.get("condition").asText());
config.setPriority(transition.get("priority").asInt());
transitions.add(config);
}
return transitions;
}
}

View File

@ -1,17 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.script.command;
import com.qqchen.deploy.backend.workflow.engine.executor.config.ScriptNodeExecutorConfig;
import java.util.List;
/**
* 脚本命令构建接口
*/
public interface ScriptCommand {
/**
* 构建脚本执行命令
* @param config 脚本配置
* @return 命令行参数列表
*/
List<String> buildCommand(ScriptNodeExecutorConfig config);
}

View File

@ -1,17 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.script.command;
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
import java.lang.annotation.*;
/**
* 脚本语言支持注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ScriptLanguageSupport {
/**
* 支持的脚本语言类型
*/
ScriptLanguageEnum value();
}

View File

@ -1,31 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.script.command.impl;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.executor.config.ScriptNodeExecutorConfig;
import com.qqchen.deploy.backend.workflow.engine.script.command.ScriptCommand;
import com.qqchen.deploy.backend.workflow.engine.script.command.ScriptLanguageSupport;
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* Python脚本命令构建实现
*/
@Component
@ScriptLanguageSupport(ScriptLanguageEnum.PYTHON)
public class PythonScriptCommand implements ScriptCommand {
@Override
public List<String> buildCommand(ScriptNodeExecutorConfig config) {
if (config.getInterpreter() == null || config.getInterpreter().trim().isEmpty()) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
"Python interpreter path must be specified");
}
return Arrays.asList(
config.getInterpreter(),
"-c",
config.getScript()
);
}
}

View File

@ -1,31 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.script.command.impl;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.executor.config.ScriptNodeExecutorConfig;
import com.qqchen.deploy.backend.workflow.engine.script.command.ScriptCommand;
import com.qqchen.deploy.backend.workflow.engine.script.command.ScriptLanguageSupport;
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
/**
* Shell脚本命令构建实现
*/
@Component
@ScriptLanguageSupport(ScriptLanguageEnum.SHELL)
public class ShellScriptCommand implements ScriptCommand {
@Override
public List<String> buildCommand(ScriptNodeExecutorConfig config) {
if (config.getInterpreter() == null || config.getInterpreter().trim().isEmpty()) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
"Shell interpreter path must be specified");
}
return Arrays.asList(
config.getInterpreter(),
"-c",
config.getScript()
);
}
}

View File

@ -1,57 +0,0 @@
package com.qqchen.deploy.backend.workflow.engine.script.registry;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.script.command.ScriptCommand;
import com.qqchen.deploy.backend.workflow.engine.script.command.ScriptLanguageSupport;
import com.qqchen.deploy.backend.workflow.enums.ScriptLanguageEnum;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 脚本命令注册表
*/
@Component
public class ScriptCommandRegistry {
private final Map<ScriptLanguageEnum, ScriptCommand> commands = new ConcurrentHashMap<>();
@Autowired
private List<ScriptCommand> scriptCommands;
@PostConstruct
public void init() {
scriptCommands.forEach(command -> {
ScriptLanguageSupport support = command.getClass().getAnnotation(ScriptLanguageSupport.class);
if (support != null) {
registerCommand(support.value(), command);
}
});
}
/**
* 注册脚本命令实现
* @param language 脚本语言
* @param command 命令实现
*/
public void registerCommand(ScriptLanguageEnum language, ScriptCommand command) {
commands.put(language, command);
}
/**
* 获取脚本命令实现
* @param language 脚本语言
* @return 命令实现
*/
public ScriptCommand getCommand(ScriptLanguageEnum language) {
ScriptCommand command = commands.get(language);
if (command == null) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
"No command implementation found for language: " + language.getDescription());
}
return command;
}
}

View File

@ -1,136 +0,0 @@
package com.qqchen.deploy.backend.workflow.entity;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.utils.SpringUtils;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.executor.config.NodeExecutorConfig;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
*
*/
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "wf_node_instance")
@LogicDelete
public class NodeInstance extends Entity<Long> {
/**
*
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "workflow_instance_id", nullable = false)
private WorkflowInstance workflowInstance;
/**
* ID
*/
@Column(nullable = false)
private String nodeId;
/**
*
*/
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private NodeTypeEnum nodeType;
/**
*
*/
@Column(nullable = false)
private String name;
/**
*
*/
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private NodeStatusEnum status;
/**
*
*/
private LocalDateTime startTime;
/**
*
*/
private LocalDateTime endTime;
/**
* (JSON)
*/
@Column(columnDefinition = "TEXT")
private String config;
/**
* (JSON)
*/
@Column(columnDefinition = "TEXT")
private String input;
/**
* (JSON)
*/
@Column(columnDefinition = "TEXT")
private String output;
/**
*
*/
@Column(columnDefinition = "TEXT")
private String error;
/**
* ID
*/
private String preNodeId;
@Transient
private NodeExecutorConfig configObject;
private static ObjectMapper getObjectMapper() {
return SpringUtils.getBean(ObjectMapper.class);
}
public String getConfig() {
return config;
}
public String getPreNodeId() {
return preNodeId;
}
public <T extends NodeExecutorConfig> T getConfigObject(Class<T> configClass) {
if (configObject == null && config != null) {
try {
configObject = getObjectMapper().readValue(this.config, configClass);
} catch (Exception e) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
String.format("Failed to parse config for node %s: %s", name, e.getMessage()));
}
}
return (T) configObject;
}
public void setConfigObject(NodeExecutorConfig config) {
this.configObject = config;
try {
this.config = getObjectMapper().writeValueAsString(config);
} catch (Exception e) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NODE_CONFIG_ERROR,
String.format("Failed to serialize config for node %s: %s", name, e.getMessage()));
}
}
}

View File

@ -1,81 +0,0 @@
package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.workflow.enums.NodeCategoryEnum;
import jakarta.persistence.Column;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 节点类型实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "wf_node_type")
@LogicDelete
public class NodeType extends Entity<Long> {
/**
* 节点类型编码
*/
@Column(nullable = false, unique = true)
private String code;
/**
* 节点类型名称
*/
@Column(nullable = false)
private String name;
/**
* 节点类型描述
*/
@Column(columnDefinition = "TEXT")
private String description;
/**
* 节点类型分类
*/
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private NodeCategoryEnum category;
/**
* 节点图标
*/
private String icon;
/**
* 节点颜色
*/
private String color;
/**
* 执行器列表(JSON)
*/
@Column(name = "executors", columnDefinition = "TEXT")
private String executors;
/**
* 节点配置模式(JSON)
*/
@Column(name = "config_schema", columnDefinition = "TEXT")
private String configSchema;
/**
* 默认配置(JSON)
*/
@Column(name = "default_config", columnDefinition = "TEXT")
private String defaultConfig;
/**
* 是否启用
*/
@Column(nullable = false)
private Boolean enabled = true;
}

View File

@ -1,161 +1,52 @@
package com.qqchen.deploy.backend.workflow.entity; package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity; import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum; import jakarta.persistence.Column;
import jakarta.persistence.*; import jakarta.persistence.Table;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.ArrayList;
import java.util.List;
/** /**
* 工作流定义实体 * 工作流定义实体
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @Table(name = "workflow_definition")
@jakarta.persistence.Entity @jakarta.persistence.Entity
@Table(name = "wf_workflow_definition") @EqualsAndHashCode(callSuper = true)
@LogicDelete
public class WorkflowDefinition extends Entity<Long> { public class WorkflowDefinition extends Entity<Long> {
/** /**
* 工作流编码唯一标识 * 流程名称
*/
@Column(nullable = false, unique = true)
private String code;
/**
* 工作流名称
*/ */
@Column(nullable = false) @Column(nullable = false)
private String name; private String name;
/** /**
* 工作流描述 * 流程标识
*/
@Column(nullable = false)
private String key;
/**
* 流程版本
*/
@Column(nullable = false)
private Integer version;
/**
* BPMN XML内容
*/
@Column(name = "bpmn_xml", columnDefinition = "TEXT")
private String bpmnXml;
/**
* 流程图JSON数据
*/
@Column(name = "graph_json", columnDefinition = "TEXT")
private String graphJson;
/**
* 流程描述
*/ */
@Column(columnDefinition = "TEXT")
private String description; private String description;
/**
* 工作流状态
*/
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false)
private WorkflowDefinitionStatusEnum status = WorkflowDefinitionStatusEnum.DRAFT;
/**
* 是否启用
*/
@Column(nullable = false)
private Boolean enabled = true;
/**
* 节点配置(JSON)
*/
@Column(name = "node_config", columnDefinition = "TEXT")
private String nodeConfig;
/**
* 流转配置(JSON)
*/
@Column(name = "transition_config", columnDefinition = "TEXT")
private String transitionConfig;
/**
* 表单定义(JSON)
*/
@Column(name = "form_definition", columnDefinition = "TEXT")
private String formDefinition;
/**
* 图形信息(JSON)
*/
@Column(name = "graph_definition", columnDefinition = "TEXT")
private String graphDefinition;
/**
* 版本号
*/
@Column(nullable = false)
private Integer version = 1;
/**
* 获取流转配置
*/
public String getTransitionConfig() {
return transitionConfig;
}
/**
* 检查是否可以发布
*/
public boolean canPublish() {
return status == WorkflowDefinitionStatusEnum.DRAFT;
}
/**
* 检查是否可以禁用
*/
public boolean canDisable() {
return status == WorkflowDefinitionStatusEnum.PUBLISHED;
}
/**
* 检查是否可以启用
*/
public boolean canEnable() {
return status == WorkflowDefinitionStatusEnum.DISABLED;
}
/**
* 检查是否可以更新
*/
public boolean canUpdate() {
return status == WorkflowDefinitionStatusEnum.DRAFT;
}
/**
* 发布工作流定义
*/
public void publish() {
if (!canPublish()) {
throw new IllegalStateException("Cannot publish workflow definition in " + status + " status");
}
this.status = WorkflowDefinitionStatusEnum.PUBLISHED;
}
/**
* 禁用工作流定义
*/
public void disable() {
if (!canDisable()) {
throw new IllegalStateException("Cannot disable workflow definition in " + status + " status");
}
this.status = WorkflowDefinitionStatusEnum.DISABLED;
}
/**
* 启用工作流定义
*/
public void enable() {
if (!canEnable()) {
throw new IllegalStateException("Cannot enable workflow definition in " + status + " status");
}
this.status = WorkflowDefinitionStatusEnum.PUBLISHED;
}
/**
* 创建新版本
*/
public WorkflowDefinition createNewVersion() {
WorkflowDefinition newVersion = new WorkflowDefinition();
newVersion.setCode(this.code);
newVersion.setName(this.name);
newVersion.setDescription(this.description);
newVersion.setVersion(this.version + 1);
newVersion.setStatus(WorkflowDefinitionStatusEnum.DRAFT);
return newVersion;
}
} }

View File

@ -1,59 +1,56 @@
package com.qqchen.deploy.backend.workflow.entity; package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity; import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnum; import jakarta.persistence.Column;
import jakarta.persistence.*; import jakarta.persistence.Table;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.time.LocalDateTime; import java.time.LocalDateTime;
/** /**
* 工作流实例实体 * 工作流实例实体
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @Table(name = "workflow_instance")
@jakarta.persistence.Entity @jakarta.persistence.Entity
@Table(name = "wf_workflow_instance") @EqualsAndHashCode(callSuper = true)
@LogicDelete
public class WorkflowInstance extends Entity<Long> { public class WorkflowInstance extends Entity<Long> {
/** /**
* 工作流定义 * 流程实例ID
*/ */
@ManyToOne(fetch = FetchType.LAZY) @Column(name = "process_instance_id", nullable = false)
@JoinColumn(name = "workflow_definition_id", nullable = false) private String processInstanceId;
private WorkflowDefinition workflowDefinition;
/** /**
* 工作流定义ID用于查询优化 * 定义ID
*/ */
@Column(name = "workflow_definition_id", insertable = false, updatable = false) @Column(name = "process_definition_id", nullable = false)
private Long workflowDefinitionId; private Long processDefinitionId;
/**
* 项目环境ID
*/
@Column(name = "project_env_id")
private Long projectEnvId;
/** /**
* 业务标识 * 业务标识
*/ */
@Column(name = "business_key", nullable = false) @Column(name = "business_key")
private String businessKey; private String businessKey;
/** /**
* 工作流状态 * 实例状态
*/ */
@Enumerated(EnumType.ORDINAL)
@Column(nullable = false) @Column(nullable = false)
private WorkflowInstanceStatusEnum status = WorkflowInstanceStatusEnum.CREATED; private String status;
/**
* 流程变量(JSON)
*/
@Column(columnDefinition = "TEXT")
private String variables;
/** /**
* 开始时间 * 开始时间
*/ */
@Column(name = "start_time") @Column(name = "start_time", nullable = false)
private LocalDateTime startTime; private LocalDateTime startTime;
/** /**
@ -61,179 +58,4 @@ public class WorkflowInstance extends Entity<Long> {
*/ */
@Column(name = "end_time") @Column(name = "end_time")
private LocalDateTime endTime; private LocalDateTime endTime;
/**
* 错误信息
*/
@Column(columnDefinition = "TEXT")
private String error;
/**
* 设置工作流定义
*/
public void setDefinition(WorkflowDefinition definition) {
this.workflowDefinition = definition;
}
/**
* 检查是否可以启动
*/
public boolean canStart() {
return status == WorkflowInstanceStatusEnum.CREATED || status == WorkflowInstanceStatusEnum.PENDING;
}
/**
* 检查是否可以暂停
*/
public boolean canPause() {
return status == WorkflowInstanceStatusEnum.RUNNING;
}
/**
* 检查是否可以恢复
*/
public boolean canResume() {
return status == WorkflowInstanceStatusEnum.PAUSED;
}
/**
* 检查是否可以终止
*/
public boolean canTerminate() {
return status == WorkflowInstanceStatusEnum.RUNNING ||
status == WorkflowInstanceStatusEnum.PAUSED;
}
/**
* 检查是否可以完成
*/
public boolean canComplete() {
return status == WorkflowInstanceStatusEnum.RUNNING;
}
/**
* 检查是否可以执行节点
*/
public boolean canExecuteNode() {
return status == WorkflowInstanceStatusEnum.RUNNING;
}
/**
* 启动工作流实例
*/
public void start() {
if (!canStart()) {
throw new IllegalStateException("Cannot start workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.RUNNING;
this.startTime = LocalDateTime.now();
}
/**
* 暂停工作流实例
*/
public void pause() {
if (!canPause()) {
throw new IllegalStateException("Cannot pause workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.PAUSED;
}
/**
* 恢复工作流实例
*/
public void resume() {
if (!canResume()) {
throw new IllegalStateException("Cannot resume workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.RUNNING;
}
/**
* 终止工作流实例
*/
public void terminate(String reason) {
if (!canTerminate()) {
throw new IllegalStateException("Cannot terminate workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.TERMINATED;
this.endTime = LocalDateTime.now();
this.error = reason;
}
/**
* 标记工作流实例为失败
*/
public void fail(String error) {
this.status = WorkflowInstanceStatusEnum.FAILED;
this.endTime = LocalDateTime.now();
this.error = error;
}
/**
* 检查工作流实例是否处于活动状态
*/
public boolean isActive() {
return status == WorkflowInstanceStatusEnum.RUNNING ||
status == WorkflowInstanceStatusEnum.PENDING ||
status == WorkflowInstanceStatusEnum.PAUSED;
}
/**
* 检查工作流实例是否已经结束
*/
public boolean isEnded() {
return status == WorkflowInstanceStatusEnum.COMPLETED ||
status == WorkflowInstanceStatusEnum.TERMINATED ||
status == WorkflowInstanceStatusEnum.FAILED;
}
/**
* 检查工作流实例是否可以重试
*/
public boolean canRetry() {
return status == WorkflowInstanceStatusEnum.FAILED;
}
/**
* 重试工作流实例
*/
public void retry() {
if (!canRetry()) {
throw new IllegalStateException("Cannot retry workflow instance in " + status + " status");
}
this.status = WorkflowInstanceStatusEnum.PENDING;
this.error = null;
}
/**
* 获取工作流实例的运行时长毫秒
*/
public long getDuration() {
if (startTime == null) {
return 0;
}
LocalDateTime end = endTime != null ? endTime : LocalDateTime.now();
return java.time.Duration.between(startTime, end).toMillis();
}
/**
* 检查工作流实例是否超时
* @param timeoutMillis 超时时间毫秒
*/
public boolean isTimeout(long timeoutMillis) {
return startTime != null && getDuration() > timeoutMillis;
}
/**
* 更新工作流实例的错误信息
*/
public void updateError(String error) {
this.error = error;
if (this.status == WorkflowInstanceStatusEnum.RUNNING) {
this.status = WorkflowInstanceStatusEnum.FAILED;
this.endTime = LocalDateTime.now();
}
}
} }

View File

@ -1,12 +1,7 @@
package com.qqchen.deploy.backend.workflow.entity; package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity; import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -15,28 +10,38 @@ import lombok.EqualsAndHashCode;
* 工作流日志实体 * 工作流日志实体
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @Table(name = "workflow_log")
@Table(name = "wf_log")
@LogicDelete
@jakarta.persistence.Entity @jakarta.persistence.Entity
@EqualsAndHashCode(callSuper = true)
public class WorkflowLog extends Entity<Long> { public class WorkflowLog extends Entity<Long> {
@ManyToOne(fetch = FetchType.LAZY) /**
@JoinColumn(name = "workflow_instance_id", nullable = false) * 工作流实例ID
private WorkflowInstance workflowInstance; */
@Column(name = "workflow_instance_id")
private Long workflowInstanceId;
@Column(name = "node_id") /**
private String nodeId; * 节点实例ID
*/
@Column(name = "node_instance_id")
private Long nodeInstanceId;
/**
* 日志类型
*/
@Column(name = "log_type", nullable = false)
private String logType;
/** /**
* 日志级别 * 日志级别
*/ */
@Column(name = "level") @Column(name = "log_level", nullable = false)
private LogLevelEnum level; private String logLevel;
@Column(name = "message", nullable = false, columnDefinition = "TEXT") /**
private String message; * 日志内容
*/
@Column(name = "detail", columnDefinition = "TEXT") @Column(columnDefinition = "TEXT", nullable = false)
private String detail; private String content;
} }

View File

@ -0,0 +1,73 @@
package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.domain.Entity;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 工作流节点实例实体
*/
@Data
@Table(name = "workflow_node_instance")
@jakarta.persistence.Entity
@EqualsAndHashCode(callSuper = true)
public class WorkflowNodeInstance extends Entity<Long> {
/**
* 工作流实例ID
*/
@Column(name = "workflow_instance_id", nullable = false)
private Long workflowInstanceId;
/**
* 节点ID
*/
@Column(name = "node_id", nullable = false)
private String nodeId;
/**
* 节点名称
*/
@Column(name = "node_name", nullable = false)
private String nodeName;
/**
* 节点类型
*/
@Column(name = "node_type", nullable = false)
private String nodeType;
/**
* 节点状态
*/
@Column(nullable = false)
private String status;
/**
* 开始时间
*/
@Column(name = "start_time", nullable = false)
private LocalDateTime startTime;
/**
* 结束时间
*/
@Column(name = "end_time")
private LocalDateTime endTime;
/**
* 节点变量(JSON)
*/
@Column(columnDefinition = "TEXT")
private String variables;
/**
* 错误信息
*/
@Column(name = "error_message", columnDefinition = "TEXT")
private String errorMessage;
}

View File

@ -1,58 +0,0 @@
package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
import com.qqchen.deploy.backend.workflow.enums.VariableTypeEnum;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 工作流变量实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "wf_workflow_variable")
@LogicDelete
public class WorkflowVariable extends Entity<Long> {
/**
* 工作流实例
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "workflow_instance_id", nullable = false)
private WorkflowInstance workflowInstance;
/**
* 变量名称
*/
@Column(nullable = false)
private String name;
/**
* 变量值(JSON)
*/
@Column(columnDefinition = "TEXT")
private String value;
/**
* 变量类型
*/
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private VariableTypeEnum type;
/**
* 变量作用域
*/
@Column(nullable = false)
private String scope = VariableScopeEnum.GLOBAL.name();
/**
* 节点IDscope为NODE时必填
*/
@Column(name = "node_id")
private String nodeId;
}

View File

@ -1,16 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum GatewayTypeEnum {
EXCLUSIVE("EXCLUSIVE", "排他网关"),
PARALLEL("PARALLEL", "并行网关"),
INCLUSIVE("INCLUSIVE", "包容网关");
private final String code;
private final String description;
}

View File

@ -1,38 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.Getter;
/**
* 节点分类枚举
*/
@Getter
public enum NodeCategoryEnum {
/**
* 基础节点如开始结束节点
*/
BASIC("基础节点"),
/**
* 任务节点如ShellJenkins等执行器节点
*/
TASK("任务节点"),
/**
* 网关节点如并行排他包容网关
*/
GATEWAY("网关节点"),
/**
* 事件节点如定时器消息等事件
*/
EVENT("事件节点");
/**
* 分类名称
*/
private final String name;
NodeCategoryEnum(String name) {
this.name = name;
}
}

View File

@ -1,25 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 节点状态枚举
*/
@Getter
@AllArgsConstructor
public enum NodeStatusEnum {
PENDING("PENDING", "等待执行"),
RUNNING("RUNNING", "执行中"),
PAUSED("PAUSED", "已暂停"),
COMPLETED("COMPLETED", "已完成"),
FAILED("FAILED", "执行失败"),
CANCELLED("CANCELLED", "已取消"),
SKIPPED("SKIPPED", "已跳过"),
SUSPENDED("SUSPENDED", "已暂停"),
TERMINATED("TERMINATED", "已终止");
private final String code;
private final String description;
}

View File

@ -1,32 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 节点类型枚举
*/
@Getter
@AllArgsConstructor
public enum NodeTypeEnum {
START(0, "START", "开始节点"),
END(1, "END", "结束节点"),
TASK(2, "TASK", "任务节点"),
GATEWAY(3, "GATEWAY", "网关节点"),
SUB_PROCESS(4, "SUB_PROCESS", "子流程节点"),
SHELL(5, "SHELL", "Shell脚本节点"),
SCRIPT(6, "SCRIPT", "脚本节点"),
APPROVAL(7, "APPROVAL", "审批节点"),
JENKINS(8, "JENKINS", "Jenkins任务节点"),
GIT(9, "GIT", "Git操作节点"),
CONDITION(10, "CONDITION", "条件节点"),
PARALLEL(11, "PARALLEL", "并行节点"),
NACOS(12, "NACOS", "Nacos配置节点"),
HTTP(13, "HTTP", "HTTP请求节点"),
NOTIFY(14, "NOTIFY", "通知节点");
private final int value;
private final String code;
private final String description;
}

View File

@ -1,31 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* 脚本语言类型枚举
*/
@Getter
@RequiredArgsConstructor
public enum ScriptLanguageEnum {
SHELL("shell", "Shell脚本"),
PYTHON("python", "Python脚本"),
JAVASCRIPT("javascript", "JavaScript脚本"),
GROOVY("groovy", "Groovy脚本");
@JsonValue
private final String code;
private final String description;
public static ScriptLanguageEnum fromCode(String code) {
for (ScriptLanguageEnum language : values()) {
if (language.getCode().equals(code)) {
return language;
}
}
throw new IllegalArgumentException("Unknown script language code: " + code);
}
}

View File

@ -1,15 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum TaskTypeEnum {
HTTP("HTTP", "HTTP请求"),
JAVA("JAVA", "Java方法");
private final String code;
private final String description;
}

View File

@ -1,19 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 变量作用域枚举
*/
@Getter
@AllArgsConstructor
public enum VariableScopeEnum {
INSTANCE("INSTANCE", "工作流实例"),
NODE("NODE", "节点实例"),
GLOBAL("GLOBAL", "全局");
private final String code;
private final String description;
}

View File

@ -1,22 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
/**
* 变量类型枚举
*/
public enum VariableTypeEnum {
STRING("字符串"),
NUMBER("数字"),
BOOLEAN("布尔值"),
OBJECT("对象"),
ARRAY("数组");
private final String description;
VariableTypeEnum(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}

View File

@ -1,29 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 工作流定义状态枚举
*/
@Getter
@AllArgsConstructor
public enum WorkflowDefinitionStatusEnum {
DRAFT(0, "DRAFT", "草稿"),
PUBLISHED(1, "PUBLISHED", "已发布"),
DISABLED(2, "DISABLED", "已禁用");
private final int value;
private final String code;
private final String description;
public static WorkflowDefinitionStatusEnum fromValue(int value) {
for (WorkflowDefinitionStatusEnum status : values()) {
if (status.getValue() == value) {
return status;
}
}
throw new IllegalArgumentException("No matching WorkflowDefinitionStatusEnum for value: " + value);
}
}

View File

@ -1,69 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 工作流实例状态枚举
*/
@Getter
@AllArgsConstructor
public enum WorkflowInstanceStatusEnum {
CREATED(0, "CREATED", "已创建"),
PENDING(1, "PENDING", "等待执行"),
RUNNING(2, "RUNNING", "执行中"),
COMPLETED(3, "COMPLETED", "已完成"),
FAILED(4, "FAILED", "执行失败"),
CANCELLED(5, "CANCELLED", "已取消"),
PAUSED(6, "PAUSED", "已暂停"),
TERMINATED(7, "TERMINATED", "已终止");
private final int value;
private final String code;
private final String description;
public static WorkflowInstanceStatusEnum fromValue(int value) {
for (WorkflowInstanceStatusEnum status : values()) {
if (status.getValue() == value) {
return status;
}
}
throw new IllegalArgumentException("No matching WorkflowInstanceStatusEnum for value: " + value);
}
/**
* 判断是否为终态
*/
public boolean isFinalStatus() {
return this == COMPLETED || this == FAILED || this == CANCELLED || this == TERMINATED;
}
/**
* 判断是否为运行状态
*/
public boolean isRunning() {
return this == RUNNING;
}
/**
* 判断是否可以暂停
*/
public boolean canPause() {
return this == RUNNING;
}
/**
* 判断是否可以恢复
*/
public boolean canResume() {
return this == PAUSED;
}
/**
* 判断是否可以取消
*/
public boolean canCancel() {
return this == RUNNING || this == PAUSED || this == PENDING;
}
}

View File

@ -1,18 +0,0 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 工作流类型枚举
*/
@Getter
@AllArgsConstructor
public enum WorkflowTypeEnum {
DEPLOY("DEPLOY", "部署工作流"),
CONFIG_SYNC("CONFIG_SYNC", "配置同步工作流");
private final String code;
private final String description;
}

View File

@ -1,29 +0,0 @@
package com.qqchen.deploy.backend.workflow.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class NodeInstanceQuery extends BaseQuery {
@QueryField(field = "workflowInstanceId")
private Long workflowInstanceId;
@QueryField(field = "nodeId")
private String nodeId;
@QueryField(field = "nodeType")
private NodeTypeEnum nodeType;
@QueryField(field = "name", type = QueryType.LIKE)
private String name;
@QueryField(field = "status")
private NodeStatusEnum status;
}

View File

@ -3,23 +3,31 @@ package com.qqchen.deploy.backend.workflow.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField; import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType; import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery; import com.qqchen.deploy.backend.framework.query.BaseQuery;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/**
* 工作流定义查询对象
*/
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class WorkflowDefinitionQuery extends BaseQuery { public class WorkflowDefinitionQuery extends BaseQuery {
/**
* 流程名称
*/
@QueryField(field = "name", type = QueryType.LIKE) @QueryField(field = "name", type = QueryType.LIKE)
private String name; private String name;
@QueryField(field = "code", type = QueryType.LIKE) /**
private String code; * 流程标识
*/
@QueryField(field = "key", type = QueryType.EQUAL)
private String key;
/** /**
* 工作流状态 * 流程版本
*/ */
@QueryField(field = "status") @QueryField(field = "version", type = QueryType.EQUAL)
private WorkflowDefinitionStatusEnum status; private Integer version;
} }

View File

@ -1,25 +0,0 @@
package com.qqchen.deploy.backend.workflow.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnum;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowInstanceQuery extends BaseQuery {
@QueryField(field = "businessKey", type = QueryType.LIKE)
private String businessKey;
@QueryField(field = "definitionId")
private Long definitionId;
/**
* 工作流状态
*/
@QueryField(field = "status")
private WorkflowInstanceStatusEnum status;
}

View File

@ -1,25 +0,0 @@
package com.qqchen.deploy.backend.workflow.query;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowLogQuery extends BaseQuery {
@QueryField(field = "workflowInstanceId")
private Long workflowInstanceId;
@QueryField(field = "nodeId")
private String nodeId;
@QueryField(field = "level")
private LogLevelEnum level;
@QueryField(field = "message", type = QueryType.LIKE)
private String message;
}

View File

@ -1,51 +0,0 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface INodeInstanceRepository extends IBaseRepository<NodeInstance, Long> {
/**
* 查询工作流实例的所有节点
*/
@Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance = :instance AND n.deleted = false ORDER BY n.createTime")
List<NodeInstance> findByWorkflowInstanceOrderByCreateTime(@Param("instance") WorkflowInstance instance);
/**
* 根据工作流实例ID查询所有节点
*/
@Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance.id = :workflowInstanceId AND n.deleted = false ORDER BY n.createTime")
List<NodeInstance> findByWorkflowInstanceId(@Param("workflowInstanceId") Long workflowInstanceId);
/**
* 根据工作流实例ID和状态查询节点
*/
@Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance.id = :workflowInstanceId AND n.status = :status AND n.deleted = false ORDER BY n.createTime")
List<NodeInstance> findByWorkflowInstanceIdAndStatus(
@Param("workflowInstanceId") Long workflowInstanceId,
@Param("status") NodeStatusEnum status);
/**
* 查询指定状态的节点实例
*/
List<NodeInstance> findByWorkflowInstanceAndStatusAndDeletedFalse(WorkflowInstance instance, NodeStatusEnum status);
List<NodeInstance> findByWorkflowInstanceAndStatus(WorkflowInstance instance, NodeStatusEnum status);
List<NodeInstance> findByWorkflowInstance(WorkflowInstance instance);
void deleteByWorkflowInstance(WorkflowInstance instance);
/**
* 查询不是指定状态的节点实例
*/
List<NodeInstance> findByWorkflowInstanceAndStatusNot(WorkflowInstance instance, NodeStatusEnum status);
}

View File

@ -1,30 +0,0 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.NodeType;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* 节点类型仓库接口
*/
@Repository
public interface INodeTypeRepository extends IBaseRepository<NodeType, Long> {
/**
* 根据编码查询未删除的节点类型
*
* @param code 节点类型编码
* @return 节点类型
*/
Optional<NodeType> findByCodeAndDeletedFalse(String code);
/**
* 检查编码是否已存在排除已删除的
*
* @param code 节点类型编码
* @return 是否存在
*/
boolean existsByCodeAndDeletedFalse(String code);
}

View File

@ -2,75 +2,20 @@ package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository; import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition; import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/** /**
* 工作流定义仓库接口 * 工作流定义仓库
*/ */
@Repository @Repository
public interface IWorkflowDefinitionRepository extends IBaseRepository<WorkflowDefinition, Long> { public interface IWorkflowDefinitionRepository extends IBaseRepository<WorkflowDefinition, Long> {
/**
* 根据流程标识和版本查询
*/
WorkflowDefinition findByKeyAndVersionAndDeletedFalse(String key, Integer version);
/** /**
* 根据编码查询最新版本的工作流定义 * 检查流程标识是否存在
*
* @param code 工作流编码
* @return 工作流定义
*/ */
@Query("SELECT w FROM WorkflowDefinition w WHERE w.code = :code AND w.deleted = false ORDER BY w.version DESC") boolean existsByKeyAndDeletedFalse(String key);
Optional<WorkflowDefinition> findLatestByCode(@Param("code") String code);
/**
* 根据编码和版本号查询工作流定义
*
* @param code 工作流编码
* @param version 版本号
* @return 工作流定义
*/
Optional<WorkflowDefinition> findByCodeAndVersionAndDeletedFalse(String code, Integer version);
/**
* 根据编码查询所有版本的工作流定义
*
* @param code 工作流编码
* @return 工作流定义列表
*/
@Query("SELECT w FROM WorkflowDefinition w WHERE w.code = :code AND w.deleted = false ORDER BY w.version DESC")
List<WorkflowDefinition> findAllVersionsByCode(@Param("code") String code);
/**
* 查询指定状态的工作流定义
*/
@Query("SELECT d FROM WorkflowDefinition d WHERE d.status = :status AND d.deleted = false")
List<WorkflowDefinition> findByStatus(@Param("status") WorkflowDefinitionStatusEnum status);
/**
* 检查编码是否已存在
*
* @param code 工作流编码
* @return 是否存在
*/
boolean existsByCodeAndDeletedFalse(String code);
/**
* 获取工作流定义的最新版本号
*
* @param code 工作流编码
* @return 最新版本号
*/
@Query("SELECT MAX(w.version) FROM WorkflowDefinition w WHERE w.code = :code AND w.deleted = false")
Integer findLatestVersionByCode(@Param("code") String code);
/**
* 根据编码查询工作流定义
*
* @param code 工作流编码
* @return 工作流定义
*/
WorkflowDefinition findByCodeAndDeletedFalse(String code);
} }

View File

@ -1,61 +0,0 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnum;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
/**
* 工作流实例仓库接口
*/
@Repository
public interface IWorkflowInstanceRepository extends IBaseRepository<WorkflowInstance, Long> {
/**
* 查询指定状态的工作流实例
*/
@Query("SELECT i FROM WorkflowInstance i WHERE i.status IN :statuses AND i.deleted = false")
List<WorkflowInstance> findByStatusIn(@Param("statuses") List<WorkflowInstanceStatusEnum> statuses);
/**
* 根据工作流定义ID和状态查询工作流实例
*/
List<WorkflowInstance> findByWorkflowDefinitionIdAndStatus(Long workflowDefinitionId, WorkflowInstanceStatusEnum status);
/**
* 根据工作流定义ID和状态统计工作流实例数量
*/
Long countByWorkflowDefinitionIdAndStatus(Long workflowDefinitionId, WorkflowInstanceStatusEnum status);
/**
* 查询项目环境的工作流实例
*/
List<WorkflowInstance> findByProjectEnvIdAndDeletedFalseOrderByCreateTimeDesc(Long projectEnvId);
/**
* 查找超过指定时间未完成的工作流实例
*/
@Query("SELECT wi FROM WorkflowInstance wi WHERE wi.status = :status AND wi.startTime < :beforeTime")
List<WorkflowInstance> findTimeoutInstances(@Param("status") WorkflowInstanceStatusEnum status,
@Param("beforeTime") LocalDateTime beforeTime);
/**
* 查找指定工作流定义下所有未结束的实例
*/
@Query("SELECT wi FROM WorkflowInstance wi WHERE wi.workflowDefinitionId = :definitionId AND wi.status IN :activeStatuses")
List<WorkflowInstance> findActiveInstancesByDefinitionId(@Param("definitionId") Long definitionId,
@Param("activeStatuses") Collection<WorkflowInstanceStatusEnum> activeStatuses);
/**
* 统计指定工作流定义下所有未结束的实例数量
*/
@Query("SELECT COUNT(wi) FROM WorkflowInstance wi WHERE wi.workflowDefinitionId = :definitionId AND wi.status IN :activeStatuses")
Long countActiveInstancesByDefinitionId(@Param("definitionId") Long definitionId,
@Param("activeStatuses") Collection<WorkflowInstanceStatusEnum> activeStatuses);
}

View File

@ -1,34 +0,0 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 工作流日志仓库接口
*/
@Repository
public interface IWorkflowLogRepository extends IBaseRepository<WorkflowLog, Long> {
/**
* 查询工作流实例的所有日志
*/
@Query("SELECT l FROM WorkflowLog l WHERE l.workflowInstance.id = :instanceId AND l.deleted = false ORDER BY l.createTime")
List<WorkflowLog> findByWorkflowInstanceId(@Param("instanceId") Long instanceId);
/**
* 查询节点实例的所有日志
*/
@Query("SELECT l FROM WorkflowLog l WHERE l.workflowInstance.id = :instanceId AND l.nodeId = :nodeId AND l.deleted = false ORDER BY l.createTime")
List<WorkflowLog> findByWorkflowInstanceIdAndNodeId(@Param("instanceId") Long instanceId, @Param("nodeId") String nodeId);
/**
* 删除工作流实例的所有日志
*/
void deleteByWorkflowInstance(WorkflowInstance instance);
}

View File

@ -1,97 +0,0 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable;
import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 工作流变量仓库接口
*/
@Repository
public interface IWorkflowVariableRepository extends IBaseRepository<WorkflowVariable, Long> {
/**
* 查询工作流实例的所有变量
*/
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.deleted = false")
List<WorkflowVariable> findByWorkflowInstance(@Param("instance") WorkflowInstance instance);
/**
* 查询工作流实例的指定作用域的变量
*/
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.scope = :scope AND v.deleted = false")
List<WorkflowVariable> findByWorkflowInstanceAndScope(@Param("instance") WorkflowInstance instance, @Param("scope") String scope);
/**
* 查询工作流实例的指定变量
*/
Optional<WorkflowVariable> findByWorkflowInstanceAndName(WorkflowInstance instance, String name);
/**
* 删除工作流实例的所有变量
*/
void deleteByWorkflowInstance(WorkflowInstance instance);
/**
* 查询工作流实例的指定作用域和节点的变量
*/
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.scope = :scope AND v.nodeId = :nodeId AND v.deleted = false")
List<WorkflowVariable> findByWorkflowInstanceAndScopeAndNodeId(
@Param("instance") WorkflowInstance instance,
@Param("scope") String scope,
@Param("nodeId") String nodeId);
/**
* 查询工作流实例的指定名称和作用域的变量
*/
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance = :instance AND v.name = :name AND v.scope = :scope AND v.deleted = false")
Optional<WorkflowVariable> findByWorkflowInstanceAndNameAndScope(
@Param("instance") WorkflowInstance instance,
@Param("name") String name,
@Param("scope") String scope);
/**
* 根据工作流实例ID查询所有变量
*
* @param workflowInstanceId 工作流实例ID
* @return 变量列表
*/
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance.id = :workflowInstanceId AND v.deleted = false")
List<WorkflowVariable> findByWorkflowInstanceId(@Param("workflowInstanceId") Long workflowInstanceId);
/**
* 根据工作流实例ID和作用域查询变量列表
*
* @param workflowInstanceId 工作流实例ID
* @param scope 变量作用域
* @return 变量列表
*/
@Query("SELECT v FROM WorkflowVariable v WHERE v.workflowInstance.id = :workflowInstanceId AND v.scope = :scope AND v.deleted = false")
List<WorkflowVariable> findByWorkflowInstanceIdAndScope(
@Param("workflowInstanceId") Long workflowInstanceId,
@Param("scope") VariableScopeEnum scope);
/**
* 删除工作流实例变量
*
* @param workflowInstanceId 工作流实例ID
*/
@Modifying
@Query("DELETE FROM WorkflowVariable v WHERE v.workflowInstance.id = :workflowInstanceId")
void deleteWorkflowInstanceVariable(@Param("workflowInstanceId") Long workflowInstanceId);
/**
* 删除工作流实例的所有变量逻辑删除
*/
@Modifying
@Query("UPDATE WorkflowVariable v SET v.deleted = true, v.updateTime = CURRENT_TIMESTAMP WHERE v.workflowInstance.id = :workflowInstanceId AND v.deleted = false")
void deleteByWorkflowInstanceId(@Param("workflowInstanceId") Long workflowInstanceId);
}

View File

@ -1,41 +0,0 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.NodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import java.util.List;
/**
* 节点实例服务接口
*/
public interface INodeInstanceService extends IBaseService<NodeInstance, NodeInstanceDTO, Long> {
/**
* 根据工作流实例ID查询节点列表
*
* @param workflowInstanceId 工作流实例ID
* @return 节点列表
*/
List<NodeInstanceDTO> findByWorkflowInstanceId(Long workflowInstanceId);
/**
* 根据工作流实例ID和状态查询节点列表
*
* @param workflowInstanceId 工作流实例ID
* @param status 状态
* @return 节点列表
*/
List<NodeInstanceDTO> findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, NodeStatusEnum status);
/**
* 更新节点状态
*
* @param id 节点ID
* @param status 状态
* @param output 输出结果
* @param error 错误信息
*/
void updateStatus(Long id, NodeStatusEnum status, String output, String error);
}

View File

@ -1,34 +0,0 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.NodeTypeDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeType;
/**
* 节点类型服务接口
*/
public interface INodeTypeService extends IBaseService<NodeType, NodeTypeDTO, Long> {
/**
* 根据编码查询节点类型
*
* @param code 节点类型编码
* @return 节点类型
*/
NodeTypeDTO findByCode(String code);
/**
* 启用节点类型
*
* @param id 节点类型ID
*/
void enable(Long id);
/**
* 禁用节点类型
*
* @param id 节点类型ID
*/
void disable(Long id);
}

View File

@ -3,8 +3,9 @@ package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService; import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO; import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition; import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import java.util.List; import java.util.Map;
/** /**
* 工作流定义服务接口 * 工作流定义服务接口
@ -12,84 +13,34 @@ import java.util.List;
public interface IWorkflowDefinitionService extends IBaseService<WorkflowDefinition, WorkflowDefinitionDTO, Long> { public interface IWorkflowDefinitionService extends IBaseService<WorkflowDefinition, WorkflowDefinitionDTO, Long> {
/** /**
* 创建工作流定义 * 部署工作流
* *
* @param dto 工作流定义DTO * @param dto 工作流定义DTO
* @return 工作流定义 * @return 工作流定义DTO
*/ */
WorkflowDefinitionDTO create(WorkflowDefinitionDTO dto); WorkflowDefinitionDTO deployWorkflow(WorkflowDefinitionDTO dto);
/** /**
* 更新工作流定义 * 启动工作流实例
* *
* @param id 工作流定义ID * @param processKey 流程标识
* @param dto 工作流定义DTO * @param businessKey 业务标识
* @return 工作流定义 * @param variables 流程变量
* @return 流程实例
*/ */
WorkflowDefinitionDTO update(Long id, WorkflowDefinitionDTO dto); ProcessInstance startWorkflow(String processKey, String businessKey, Map<String, Object> variables);
/** /**
* 发布工作流定义 * 挂起工作流实例
* *
* @param id 工作流定义ID * @param processInstanceId 流程实例ID
* @return 工作流定义
*/ */
WorkflowDefinitionDTO publish(Long id); void suspendWorkflow(String processInstanceId);
/** /**
* 禁用工作流定义 * 恢复工作流实例
* *
* @param id 工作流定义ID * @param processInstanceId 流程实例ID
* @return 工作流定义
*/ */
WorkflowDefinitionDTO disable(Long id); void resumeWorkflow(String processInstanceId);
/**
* 创建新版本
*
* @param id 工作流定义ID
* @return 工作流定义
*/
WorkflowDefinitionDTO createNewVersion(Long id);
/**
* 根据编码查询最新版本
*
* @param code 工作流编码
* @return 工作流定义
*/
WorkflowDefinitionDTO findLatestByCode(String code);
/**
* 根据编码和版本号查询
*
* @param code 工作流编码
* @param version 版本号
* @return 工作流定义
*/
WorkflowDefinitionDTO findByCodeAndVersion(String code, Integer version);
/**
* 查询所有版本
*
* @param code 工作流编码
* @return 工作流定义列表
*/
List<WorkflowDefinitionDTO> findAllVersions(String code);
/**
* 验证工作流定义
*
* @param id 工作流定义ID
* @return 验证结果
*/
boolean validate(Long id);
/**
* 启用工作流定义
*
* @param id 工作流定义ID
* @return 工作流定义
*/
WorkflowDefinitionDTO enable(Long id);
} }

View File

@ -0,0 +1,17 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDesignDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.query.WorkflowDefinitionQuery;
/**
* 工作流设计服务接口
*/
public interface IWorkflowDesignService extends IBaseService<WorkflowDefinition, WorkflowDesignDTO, Long> {
/**
* 保存工作流设计
*/
WorkflowDesignDTO saveWorkflowDesign(WorkflowDesignDTO dto) throws Exception;
}

View File

@ -1,16 +0,0 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.workflow.dto.request.WorkflowStartRequest;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
/**
* 工作流执行引擎接口
*/
public interface IWorkflowEngineService {
/**
* 启动工作流实例
*/
WorkflowInstance startWorkflow(WorkflowStartRequest request);
}

View File

@ -1,56 +0,0 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import java.util.Map;
/**
* 工作流实例服务接口
*/
public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, Long> {
/**
* 创建工作流实例
*
* @param definitionId 工作流定义ID
* @param businessKey 业务标识
* @param variables 初始变量
* @return 工作流实例
*/
WorkflowInstanceDTO createInstance(Long definitionId, String businessKey, Map<String, Object> variables);
/**
* 启动工作流实例
*
* @param id 实例ID
* @return 工作流实例
*/
WorkflowInstanceDTO startInstance(Long id);
/**
* 暂停工作流实例
*
* @param id 实例ID
* @return 工作流实例
*/
WorkflowInstanceDTO pauseInstance(Long id);
/**
* 恢复工作流实例
*
* @param id 实例ID
* @return 工作流实例
*/
WorkflowInstanceDTO resumeInstance(Long id);
/**
* 终止工作流实例
*
* @param id 实例ID
* @param reason 终止原因
* @return 工作流实例
*/
WorkflowInstanceDTO terminateInstance(Long id, String reason);
}

View File

@ -1,62 +0,0 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.system.enums.LogLevelEnum;
import com.qqchen.deploy.backend.workflow.dto.WorkflowLogDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
import java.util.List;
/**
* 工作流日志服务接口
*/
public interface IWorkflowLogService extends IBaseService<WorkflowLog, WorkflowLogDTO, Long> {
/**
* 记录日志 (内部使用由工作流引擎调用)
*
* @param instance 工作流实例
* @param nodeId 节点ID
* @param level 日志级别
* @param message 日志内容
* @param detail 详细信息
* @return 工作流日志
*/
WorkflowLog log(WorkflowInstance instance, String nodeId, LogLevelEnum level, String message, String detail);
/**
* 记录日志 (外部接口使用)
*
* @param workflowInstanceId 工作流实例ID
* @param nodeId 节点ID
* @param message 日志内容
* @param level 日志级别
* @param detail 详细信息
*/
void recordLog(Long workflowInstanceId, String nodeId, String message, LogLevelEnum level, String detail);
/**
* 查询工作流实例的所有日志
*
* @param workflowInstanceId 工作流实例ID
* @return 日志列表
*/
List<WorkflowLogDTO> getLogs(Long workflowInstanceId);
/**
* 查询节点实例的所有日志
*
* @param workflowInstanceId 工作流实例ID
* @param nodeId 节点ID
* @return 日志列表
*/
List<WorkflowLogDTO> getNodeLogs(Long workflowInstanceId, String nodeId);
/**
* 删除工作流实例的所有日志
*
* @param workflowInstance 工作流实例
*/
void deleteLogs(WorkflowInstance workflowInstance);
}

View File

@ -1,43 +0,0 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.WorkflowVariableDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable;
import com.qqchen.deploy.backend.workflow.enums.VariableScopeEnum;
import java.util.Map;
/**
* 工作流变量服务接口
*/
public interface IWorkflowVariableService extends IBaseService<WorkflowVariable, WorkflowVariableDTO, Long> {
/**
* 设置变量
*
* @param variables 变量映射
*/
void setVariables(Long workflowInstanceId, Map<String, Object> variables);
/**
* 获取所有变量
*
* @return 变量映射
*/
Map<String, Object> getVariables(Long workflowInstanceId);
/**
* 获取指定作用域的变量
*
* @param scope 变量作用域
* @return 变量映射
*/
Map<String, Object> getVariablesByScope(Long workflowInstanceId, VariableScopeEnum scope);
/**
* 清除实例的所有变量
*
*/
void clearVariables(Long workflowInstanceId);
}

View File

@ -1,68 +0,0 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.converter.NodeInstanceConverter;
import com.qqchen.deploy.backend.workflow.dto.NodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.INodeInstanceService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* 节点实例服务实现类
*/
@Service
public class NodeInstanceServiceImpl extends BaseServiceImpl<NodeInstance, NodeInstanceDTO, Long> implements INodeInstanceService {
@Resource
private INodeInstanceRepository nodeInstanceRepository;
@Resource
private NodeInstanceConverter nodeInstanceConverter;
@Override
public List<NodeInstanceDTO> findByWorkflowInstanceId(Long workflowInstanceId) {
return nodeInstanceRepository.findByWorkflowInstanceId(workflowInstanceId)
.stream()
.map(nodeInstanceConverter::toDto)
.collect(Collectors.toList());
}
@Override
public List<NodeInstanceDTO> findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, NodeStatusEnum status) {
return nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(workflowInstanceId, status)
.stream()
.map(nodeInstanceConverter::toDto)
.collect(Collectors.toList());
}
@Override
@Transactional
public void updateStatus(Long id, NodeStatusEnum status, String output, String error) {
NodeInstance node = nodeInstanceRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NODE_NOT_FOUND));
node.setStatus(status);
node.setOutput(output);
node.setError(error);
if (NodeStatusEnum.RUNNING.equals(status)) {
node.setStartTime(LocalDateTime.now());
} else if (NodeStatusEnum.COMPLETED.equals(status) ||
NodeStatusEnum.FAILED.equals(status) ||
NodeStatusEnum.CANCELLED.equals(status) ||
NodeStatusEnum.SKIPPED.equals(status)) {
node.setEndTime(LocalDateTime.now());
}
nodeInstanceRepository.save(node);
}
}

View File

@ -1,55 +0,0 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.converter.JsonConverter;
import com.qqchen.deploy.backend.workflow.converter.NodeTypeConverter;
import com.qqchen.deploy.backend.workflow.dto.NodeTypeDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeType;
import com.qqchen.deploy.backend.workflow.repository.INodeTypeRepository;
import com.qqchen.deploy.backend.workflow.service.INodeTypeService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 节点类型服务实现类
*/
@Service
public class NodeTypeServiceImpl extends BaseServiceImpl<NodeType, NodeTypeDTO, Long> implements INodeTypeService {
@Resource
private INodeTypeRepository nodeTypeRepository;
@Resource
private NodeTypeConverter nodeTypeConverter;
@Resource
private JsonConverter jsonConverter;
@Override
public NodeTypeDTO findByCode(String code) {
return nodeTypeRepository.findByCodeAndDeletedFalse(code)
.map(nodeTypeConverter::toDto)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NODE_TYPE_NOT_FOUND));
}
@Override
@Transactional
public void enable(Long id) {
NodeType nodeType = nodeTypeRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NODE_TYPE_NOT_FOUND));
nodeType.setEnabled(true);
nodeTypeRepository.save(nodeType);
}
@Override
@Transactional
public void disable(Long id) {
NodeType nodeType = nodeTypeRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NODE_TYPE_NOT_FOUND));
nodeType.setEnabled(false);
nodeTypeRepository.save(nodeType);
}
}

View File

@ -1,22 +1,19 @@
package com.qqchen.deploy.backend.workflow.service.impl; package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl; import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.converter.WorkflowDefinitionConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO; import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition; import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowDefinitionService; import com.qqchen.deploy.backend.workflow.service.IWorkflowDefinitionService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/** /**
* 工作流定义服务实现 * 工作流定义服务实现
@ -27,203 +24,67 @@ public class WorkflowDefinitionServiceImpl extends BaseServiceImpl<WorkflowDefin
implements IWorkflowDefinitionService { implements IWorkflowDefinitionService {
@Resource @Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository; private RepositoryService repositoryService;
@Resource @Resource
private WorkflowDefinitionConverter workflowDefinitionConverter; private RuntimeService runtimeService;
@Override @Override
@Transactional @Transactional(rollbackFor = Exception.class)
public WorkflowDefinitionDTO create(WorkflowDefinitionDTO dto) { public WorkflowDefinitionDTO deployWorkflow(WorkflowDefinitionDTO dto) {
// 检查编码是否已存在 try {
if (workflowDefinitionRepository.existsByCodeAndDeletedFalse(dto.getCode())) { // 部署流程
throw new BusinessException(ResponseCode.WORKFLOW_CODE_EXISTS); Deployment deployment = repositoryService.createDeployment()
.addString(dto.getName() + ".bpmn20.xml", dto.getBpmnXml())
.name(dto.getName())
.deploy();
log.info("Successfully deployed workflow: {}", dto.getName());
// 保存流程定义
return super.create(dto);
} catch (Exception e) {
log.error("Failed to deploy workflow: {}", dto.getName(), e);
throw new RuntimeException("Failed to deploy workflow", e);
} }
// 设置初始状态
dto.setVersion(1);
dto.setStatus(WorkflowDefinitionStatusEnum.DRAFT);
dto.setEnabled(true);
// 保存工作流定义
WorkflowDefinition workflowDefinition = workflowDefinitionConverter.toEntity(dto);
workflowDefinition = workflowDefinitionRepository.save(workflowDefinition);
return workflowDefinitionConverter.toDto(workflowDefinition);
} }
@Override @Override
@Transactional @Transactional(rollbackFor = Exception.class)
public WorkflowDefinitionDTO update(Long id, WorkflowDefinitionDTO dto) { public ProcessInstance startWorkflow(String processKey, String businessKey, Map<String, Object> variables) {
// 获取当前工作流定义 try {
WorkflowDefinition current = workflowDefinitionRepository.findById(id) ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND)); processKey,
businessKey,
// 只有草稿状态可以修改 variables
if (current.getStatus() != WorkflowDefinitionStatusEnum.DRAFT) { );
throw new BusinessException(ResponseCode.WORKFLOW_NOT_DRAFT); log.info("Started workflow instance: {}", processInstance.getId());
return processInstance;
} catch (Exception e) {
log.error("Failed to start workflow: {}", processKey, e);
throw new RuntimeException("Failed to start workflow", e);
} }
// 检查编码是否已存在排除自身
if (!Objects.equals(current.getCode(), dto.getCode()) &&
workflowDefinitionRepository.existsByCodeAndDeletedFalse(dto.getCode())) {
throw new BusinessException(ResponseCode.WORKFLOW_CODE_EXISTS);
}
// 更新基本信息
current.setName(dto.getName());
current.setDescription(dto.getDescription());
current.setNodeConfig(dto.getNodeConfig());
current.setTransitionConfig(dto.getTransitionConfig());
current.setFormDefinition(dto.getFormDefinition());
current.setGraphDefinition(dto.getGraphDefinition());
// 保存更新
current = workflowDefinitionRepository.save(current);
return workflowDefinitionConverter.toDto(current);
} }
@Override @Override
@Transactional public void suspendWorkflow(String processInstanceId) {
public WorkflowDefinitionDTO publish(Long id) { try {
// 获取当前工作流定义 runtimeService.suspendProcessInstanceById(processInstanceId);
WorkflowDefinition current = workflowDefinitionRepository.findById(id) log.info("Suspended workflow instance: {}", processInstanceId);
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND)); } catch (Exception e) {
log.error("Failed to suspend workflow instance: {}", processInstanceId, e);
// 验证当前状态 throw new RuntimeException("Failed to suspend workflow instance", e);
if (current.getStatus() != WorkflowDefinitionStatusEnum.DRAFT) {
throw new BusinessException(ResponseCode.WORKFLOW_NOT_DRAFT);
} }
// 验证工作流定义
if (!validate(id)) {
throw new BusinessException(ResponseCode.WORKFLOW_CONFIG_INVALID);
}
// 更新状态
current.setStatus(WorkflowDefinitionStatusEnum.PUBLISHED);
current = workflowDefinitionRepository.save(current);
return workflowDefinitionConverter.toDto(current);
} }
@Override @Override
@Transactional public void resumeWorkflow(String processInstanceId) {
public WorkflowDefinitionDTO disable(Long id) { try {
// 获取当前工作流定义 runtimeService.activateProcessInstanceById(processInstanceId);
WorkflowDefinition current = workflowDefinitionRepository.findById(id) log.info("Resumed workflow instance: {}", processInstanceId);
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND)); } catch (Exception e) {
log.error("Failed to resume workflow instance: {}", processInstanceId, e);
// 验证当前状态 throw new RuntimeException("Failed to resume workflow instance", e);
if (current.getStatus() != WorkflowDefinitionStatusEnum.PUBLISHED) { }
throw new BusinessException(ResponseCode.WORKFLOW_NOT_PUBLISHED);
}
// 更新状态
current.setStatus(WorkflowDefinitionStatusEnum.DISABLED);
current = workflowDefinitionRepository.save(current);
return workflowDefinitionConverter.toDto(current);
}
@Override
@Transactional
public WorkflowDefinitionDTO enable(Long id) {
// 获取当前工作流定义
WorkflowDefinition current = workflowDefinitionRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
// 验证当前状态
if (current.getStatus() != WorkflowDefinitionStatusEnum.DISABLED) {
throw new BusinessException(ResponseCode.WORKFLOW_NOT_DISABLED);
}
// 更新状态
current.setStatus(WorkflowDefinitionStatusEnum.PUBLISHED);
current = workflowDefinitionRepository.save(current);
return workflowDefinitionConverter.toDto(current);
}
@Override
public WorkflowDefinitionDTO findLatestByCode(String code) {
return workflowDefinitionRepository.findLatestByCode(code)
.map(workflowDefinitionConverter::toDto)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
}
@Override
public List<WorkflowDefinitionDTO> findAllVersions(String code) {
return workflowDefinitionRepository.findAllVersionsByCode(code)
.stream()
.map(workflowDefinitionConverter::toDto)
.collect(Collectors.toList());
}
@Override
public boolean validate(Long id) {
WorkflowDefinition definition = workflowDefinitionRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
// 1. 验证节点配置的完整性
if (definition.getNodeConfig() == null || definition.getNodeConfig().isEmpty()) {
throw new BusinessException(ResponseCode.WORKFLOW_NODE_CONFIG_EMPTY);
}
// 2. 验证流转配置的完整性
if (definition.getTransitionConfig() == null || definition.getTransitionConfig().isEmpty()) {
throw new BusinessException(ResponseCode.WORKFLOW_TRANSITION_CONFIG_EMPTY);
}
// 3. 验证表单定义的完整性
if (definition.getFormDefinition() == null || definition.getFormDefinition().isEmpty()) {
throw new BusinessException(ResponseCode.WORKFLOW_FORM_CONFIG_EMPTY);
}
// 4. 验证图形信息的完整性
if (definition.getGraphDefinition() == null || definition.getGraphDefinition().isEmpty()) {
throw new BusinessException(ResponseCode.WORKFLOW_GRAPH_CONFIG_EMPTY);
}
// TODO: 实现更详细的验证逻辑
// 1. 验证节点之间的连接关系
// 2. 验证表单字段的合法性
// 3. 验证图形布局的合理性
return true;
}
@Override
@Transactional(readOnly = true)
public WorkflowDefinitionDTO findByCodeAndVersion(String code, Integer version) {
return workflowDefinitionConverter.toDto(workflowDefinitionRepository.findByCodeAndVersionAndDeletedFalse(code, version)
.orElse(null));
}
@Override
@Transactional
public WorkflowDefinitionDTO createNewVersion(Long id) {
// 获取原工作流定义
WorkflowDefinition oldDefinition = workflowDefinitionRepository.findById(id)
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
// 获取最新版本号
Integer latestVersion = workflowDefinitionRepository.findLatestVersionByCode(oldDefinition.getCode());
Integer newVersion = latestVersion != null ? latestVersion + 1 : 1;
// 创建新版本
WorkflowDefinition newDefinition = new WorkflowDefinition();
newDefinition.setCode(oldDefinition.getCode());
newDefinition.setName(oldDefinition.getName());
newDefinition.setDescription(oldDefinition.getDescription());
newDefinition.setStatus(WorkflowDefinitionStatusEnum.DRAFT);
newDefinition.setEnabled(true);
newDefinition.setNodeConfig(oldDefinition.getNodeConfig());
newDefinition.setTransitionConfig(oldDefinition.getTransitionConfig());
newDefinition.setFormDefinition(oldDefinition.getFormDefinition());
newDefinition.setGraphDefinition(oldDefinition.getGraphDefinition());
// 保存新版本
return workflowDefinitionConverter.toDto(workflowDefinitionRepository.save(newDefinition));
} }
} }

View File

@ -0,0 +1,47 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.dto.WorkflowDesignDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.query.WorkflowDefinitionQuery;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowDefinitionService;
import com.qqchen.deploy.backend.workflow.service.IWorkflowDesignService;
import com.qqchen.deploy.backend.workflow.util.BpmnConverter;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 工作流设计服务实现
*/
@Service
public class WorkflowDesignServiceImpl extends BaseServiceImpl<WorkflowDefinition, WorkflowDesignDTO, Long> implements IWorkflowDesignService {
@Resource
private IWorkflowDefinitionService workflowDefinitionService;
@Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Resource
private BpmnConverter bpmnConverter;
@Override
@Transactional(rollbackFor = Exception.class)
public WorkflowDesignDTO saveWorkflowDesign(WorkflowDesignDTO dto) throws Exception {
// 转换图形JSON为BPMN XML
String bpmnXml = bpmnConverter.convertToBpmnXml(dto.getGraphJson(), dto.getKey());
// 创建工作流定义
WorkflowDefinition definition = new WorkflowDefinition();
definition.setName(dto.getName());
definition.setKey(dto.getKey());
definition.setDescription(dto.getDescription());
definition.setGraphJson(dto.getGraphJson());
definition.setBpmnXml(bpmnXml);
// 保存工作流定义
return super.converter.toDto(workflowDefinitionRepository.save(definition));
}
}

View File

@ -1,84 +0,0 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.workflow.dto.request.WorkflowStartRequest;
import com.qqchen.deploy.backend.workflow.engine.exception.WorkflowEngineException;
import com.qqchen.deploy.backend.workflow.engine.executor.INodeExecutor;
import com.qqchen.deploy.backend.workflow.engine.model.NodeConfig;
import com.qqchen.deploy.backend.workflow.engine.model.WorkflowGraph;
import com.qqchen.deploy.backend.workflow.engine.parser.WorkflowDefinitionParser;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeTypeEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowEngineService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.Map;
@Slf4j
@Component
public class WorkflowEngineServiceImpl implements IWorkflowEngineService {
@Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Resource
private IWorkflowInstanceRepository workflowInstanceRepository;
@Resource
private INodeInstanceRepository nodeInstanceRepository;
@Resource
@Lazy
private Map<NodeTypeEnum, INodeExecutor> nodeExecutors;
@Resource
private ObjectMapper objectMapper;
@Resource
private WorkflowDefinitionParser workflowDefinitionParser;
@Override
@Transactional
public WorkflowInstance startWorkflow(WorkflowStartRequest request) {
// 1. 获取工作流定义
WorkflowDefinition definition = workflowDefinitionRepository.findByCodeAndDeletedFalse(request.getWorkflowCode());
if (definition == null) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NOT_FOUND);
}
// 2. 检查工作流定义状态
if (definition.getStatus() != WorkflowDefinitionStatusEnum.PUBLISHED) {
throw new WorkflowEngineException(ResponseCode.WORKFLOW_NOT_PUBLISHED);
}
// 3. 创建工作流实例
WorkflowInstance workflowInstance = new WorkflowInstance();
workflowInstance.setWorkflowDefinition(definition);
workflowInstance.setBusinessKey(request.getBusinessKey());
workflowInstance.setStatus(WorkflowInstanceStatusEnum.RUNNING);
workflowInstance.setCreateTime(LocalDateTime.now());
workflowInstanceRepository.save(workflowInstance);
// 5. 解析工作流配置
WorkflowGraph graph = workflowDefinitionParser.parse(definition);
// 6. 获取并执行开始节点
NodeConfig startNode = graph.getStartNode();
INodeExecutor executor = nodeExecutors.get(startNode.getType());
executor.execute(workflowInstance, graph, startNode);
return workflowInstance;
}
}

Some files were not shown because too many files have changed in this diff Show More