删除掉所有没用的工作流相关的类,重新开发。
This commit is contained in:
parent
216839dd59
commit
01ab6721c6
670
backend/docs/workflow-engine-design.md
Normal file
670
backend/docs/workflow-engine-design.md
Normal 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:子流程节点,5:Shell脚本节点,6:审批节点,7:Jenkins任务节点,8:Git操作节点)',
|
||||
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. 运维优化
|
||||
- 完善监控指标
|
||||
- 优化日志清理
|
||||
- 添加性能诊断工具
|
||||
@ -26,6 +26,7 @@
|
||||
<jjwt.version>0.12.3</jjwt.version>
|
||||
<springdoc.version>2.2.0</springdoc.version>
|
||||
<hutool.version>5.8.23</hutool.version>
|
||||
<flowable.version>7.0.0</flowable.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@ -201,6 +202,17 @@
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>3.1.8</version>
|
||||
</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>
|
||||
|
||||
<build>
|
||||
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -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: 实现导出功能
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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: 实现导出功能
|
||||
}
|
||||
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -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: 实现导出功能
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -8,6 +8,6 @@ import org.mapstruct.Mapper;
|
||||
/**
|
||||
* 工作流定义转换器
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
@Mapper(config = BaseConverter.class)
|
||||
public interface WorkflowDefinitionConverter extends BaseConverter<WorkflowDefinition, WorkflowDefinitionDTO> {
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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> {
|
||||
}
|
||||
@ -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> {
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -1,9 +1,6 @@
|
||||
package com.qqchen.deploy.backend.workflow.dto;
|
||||
|
||||
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.EqualsAndHashCode;
|
||||
|
||||
@ -15,60 +12,27 @@ import lombok.EqualsAndHashCode;
|
||||
public class WorkflowDefinitionDTO extends BaseDTO {
|
||||
|
||||
/**
|
||||
* 工作流编码
|
||||
* 流程名称
|
||||
*/
|
||||
@NotBlank(message = "工作流编码不能为空")
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 工作流名称
|
||||
*/
|
||||
@NotBlank(message = "工作流名称不能为空")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 工作流描述
|
||||
* 流程标识
|
||||
*/
|
||||
private String description;
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 工作流状态
|
||||
*/
|
||||
private WorkflowDefinitionStatusEnum status;
|
||||
|
||||
/**
|
||||
* 版本号
|
||||
* 流程版本
|
||||
*/
|
||||
private Integer version;
|
||||
|
||||
/**
|
||||
* 节点配置(JSON)
|
||||
* BPMN XML内容
|
||||
*/
|
||||
private String nodeConfig;
|
||||
private String bpmnXml;
|
||||
|
||||
/**
|
||||
* 流转配置(JSON)
|
||||
* 流程描述
|
||||
*/
|
||||
private String transitionConfig;
|
||||
|
||||
/**
|
||||
* 表单定义(JSON)
|
||||
*/
|
||||
private String formDefinition;
|
||||
|
||||
/**
|
||||
* 图形信息(JSON)
|
||||
*/
|
||||
private String graphDefinition;
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
@NotNull(message = "是否启用不能为空")
|
||||
private Boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
private String description;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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()
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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()});
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -1,161 +1,52 @@
|
||||
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.WorkflowDefinitionStatusEnum;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流定义实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table(name = "workflow_definition")
|
||||
@jakarta.persistence.Entity
|
||||
@Table(name = "wf_workflow_definition")
|
||||
@LogicDelete
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WorkflowDefinition extends Entity<Long> {
|
||||
|
||||
/**
|
||||
* 工作流编码,唯一标识
|
||||
*/
|
||||
@Column(nullable = false, unique = true)
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 工作流名称
|
||||
* 流程名称
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
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;
|
||||
|
||||
/**
|
||||
* 工作流状态
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@ -1,59 +1,56 @@
|
||||
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.WorkflowInstanceStatusEnum;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 工作流实例实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table(name = "workflow_instance")
|
||||
@jakarta.persistence.Entity
|
||||
@Table(name = "wf_workflow_instance")
|
||||
@LogicDelete
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WorkflowInstance extends Entity<Long> {
|
||||
|
||||
/**
|
||||
* 工作流定义
|
||||
* 流程实例ID
|
||||
*/
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "workflow_definition_id", nullable = false)
|
||||
private WorkflowDefinition workflowDefinition;
|
||||
@Column(name = "process_instance_id", nullable = false)
|
||||
private String processInstanceId;
|
||||
|
||||
/**
|
||||
* 工作流定义ID,用于查询优化
|
||||
* 流程定义ID
|
||||
*/
|
||||
@Column(name = "workflow_definition_id", insertable = false, updatable = false)
|
||||
private Long workflowDefinitionId;
|
||||
|
||||
/**
|
||||
* 项目环境ID
|
||||
*/
|
||||
@Column(name = "project_env_id")
|
||||
private Long projectEnvId;
|
||||
@Column(name = "process_definition_id", nullable = false)
|
||||
private Long processDefinitionId;
|
||||
|
||||
/**
|
||||
* 业务标识
|
||||
*/
|
||||
@Column(name = "business_key", nullable = false)
|
||||
@Column(name = "business_key")
|
||||
private String businessKey;
|
||||
|
||||
/**
|
||||
* 工作流状态
|
||||
* 实例状态
|
||||
*/
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@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;
|
||||
|
||||
/**
|
||||
@ -61,179 +58,4 @@ public class WorkflowInstance extends Entity<Long> {
|
||||
*/
|
||||
@Column(name = "end_time")
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,7 @@
|
||||
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.system.enums.LogLevelEnum;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@ -15,28 +10,38 @@ import lombok.EqualsAndHashCode;
|
||||
* 工作流日志实体
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Table(name = "wf_log")
|
||||
@LogicDelete
|
||||
@Table(name = "workflow_log")
|
||||
@jakarta.persistence.Entity
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WorkflowLog extends Entity<Long> {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "workflow_instance_id", nullable = false)
|
||||
private WorkflowInstance workflowInstance;
|
||||
/**
|
||||
* 工作流实例ID
|
||||
*/
|
||||
@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")
|
||||
private LogLevelEnum level;
|
||||
@Column(name = "log_level", nullable = false)
|
||||
private String logLevel;
|
||||
|
||||
@Column(name = "message", nullable = false, columnDefinition = "TEXT")
|
||||
private String message;
|
||||
|
||||
@Column(name = "detail", columnDefinition = "TEXT")
|
||||
private String detail;
|
||||
/**
|
||||
* 日志内容
|
||||
*/
|
||||
@Column(columnDefinition = "TEXT", nullable = false)
|
||||
private String content;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
|
||||
/**
|
||||
* 节点ID(scope为NODE时必填)
|
||||
*/
|
||||
@Column(name = "node_id")
|
||||
private String nodeId;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package com.qqchen.deploy.backend.workflow.enums;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 节点分类枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum NodeCategoryEnum {
|
||||
/**
|
||||
* 基础节点(如开始、结束节点)
|
||||
*/
|
||||
BASIC("基础节点"),
|
||||
|
||||
/**
|
||||
* 任务节点(如Shell、Jenkins等执行器节点)
|
||||
*/
|
||||
TASK("任务节点"),
|
||||
|
||||
/**
|
||||
* 网关节点(如并行、排他、包容网关)
|
||||
*/
|
||||
GATEWAY("网关节点"),
|
||||
|
||||
/**
|
||||
* 事件节点(如定时器、消息等事件)
|
||||
*/
|
||||
EVENT("事件节点");
|
||||
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
NodeCategoryEnum(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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.enums.QueryType;
|
||||
import com.qqchen.deploy.backend.framework.query.BaseQuery;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 工作流定义查询对象
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class WorkflowDefinitionQuery extends BaseQuery {
|
||||
|
||||
/**
|
||||
* 流程名称
|
||||
*/
|
||||
@QueryField(field = "name", type = QueryType.LIKE)
|
||||
private String name;
|
||||
|
||||
@QueryField(field = "code", type = QueryType.LIKE)
|
||||
private String code;
|
||||
/**
|
||||
* 流程标识
|
||||
*/
|
||||
@QueryField(field = "key", type = QueryType.EQUAL)
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 工作流状态
|
||||
* 流程版本
|
||||
*/
|
||||
@QueryField(field = "status")
|
||||
private WorkflowDefinitionStatusEnum status;
|
||||
@QueryField(field = "version", type = QueryType.EQUAL)
|
||||
private Integer version;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -2,75 +2,20 @@ package com.qqchen.deploy.backend.workflow.repository;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
|
||||
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 java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 工作流定义仓库接口
|
||||
* 工作流定义仓库
|
||||
*/
|
||||
@Repository
|
||||
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")
|
||||
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);
|
||||
boolean existsByKeyAndDeletedFalse(String key);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -3,8 +3,9 @@ package com.qqchen.deploy.backend.workflow.service;
|
||||
import com.qqchen.deploy.backend.framework.service.IBaseService;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
||||
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> {
|
||||
|
||||
/**
|
||||
* 创建工作流定义
|
||||
* 部署工作流
|
||||
*
|
||||
* @param dto 工作流定义DTO
|
||||
* @return 工作流定义
|
||||
* @return 工作流定义DTO
|
||||
*/
|
||||
WorkflowDefinitionDTO create(WorkflowDefinitionDTO dto);
|
||||
WorkflowDefinitionDTO deployWorkflow(WorkflowDefinitionDTO dto);
|
||||
|
||||
/**
|
||||
* 更新工作流定义
|
||||
* 启动工作流实例
|
||||
*
|
||||
* @param id 工作流定义ID
|
||||
* @param dto 工作流定义DTO
|
||||
* @return 工作流定义
|
||||
* @param processKey 流程标识
|
||||
* @param businessKey 业务标识
|
||||
* @param variables 流程变量
|
||||
* @return 流程实例
|
||||
*/
|
||||
WorkflowDefinitionDTO update(Long id, WorkflowDefinitionDTO dto);
|
||||
ProcessInstance startWorkflow(String processKey, String businessKey, Map<String, Object> variables);
|
||||
|
||||
/**
|
||||
* 发布工作流定义
|
||||
* 挂起工作流实例
|
||||
*
|
||||
* @param id 工作流定义ID
|
||||
* @return 工作流定义
|
||||
* @param processInstanceId 流程实例ID
|
||||
*/
|
||||
WorkflowDefinitionDTO publish(Long id);
|
||||
void suspendWorkflow(String processInstanceId);
|
||||
|
||||
/**
|
||||
* 禁用工作流定义
|
||||
* 恢复工作流实例
|
||||
*
|
||||
* @param id 工作流定义ID
|
||||
* @return 工作流定义
|
||||
* @param processInstanceId 流程实例ID
|
||||
*/
|
||||
WorkflowDefinitionDTO disable(Long id);
|
||||
|
||||
/**
|
||||
* 创建新版本
|
||||
*
|
||||
* @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);
|
||||
void resumeWorkflow(String processInstanceId);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -1,22 +1,19 @@
|
||||
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.WorkflowDefinitionConverter;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
||||
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 jakarta.annotation.Resource;
|
||||
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.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工作流定义服务实现
|
||||
@ -27,203 +24,67 @@ public class WorkflowDefinitionServiceImpl extends BaseServiceImpl<WorkflowDefin
|
||||
implements IWorkflowDefinitionService {
|
||||
|
||||
@Resource
|
||||
private IWorkflowDefinitionRepository workflowDefinitionRepository;
|
||||
private RepositoryService repositoryService;
|
||||
|
||||
@Resource
|
||||
private WorkflowDefinitionConverter workflowDefinitionConverter;
|
||||
private RuntimeService runtimeService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public WorkflowDefinitionDTO create(WorkflowDefinitionDTO dto) {
|
||||
// 检查编码是否已存在
|
||||
if (workflowDefinitionRepository.existsByCodeAndDeletedFalse(dto.getCode())) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_CODE_EXISTS);
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public WorkflowDefinitionDTO deployWorkflow(WorkflowDefinitionDTO dto) {
|
||||
try {
|
||||
// 部署流程
|
||||
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
|
||||
@Transactional
|
||||
public WorkflowDefinitionDTO update(Long id, WorkflowDefinitionDTO dto) {
|
||||
// 获取当前工作流定义
|
||||
WorkflowDefinition current = workflowDefinitionRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
|
||||
|
||||
// 只有草稿状态可以修改
|
||||
if (current.getStatus() != WorkflowDefinitionStatusEnum.DRAFT) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_NOT_DRAFT);
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public ProcessInstance startWorkflow(String processKey, String businessKey, Map<String, Object> variables) {
|
||||
try {
|
||||
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
|
||||
processKey,
|
||||
businessKey,
|
||||
variables
|
||||
);
|
||||
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
|
||||
@Transactional
|
||||
public WorkflowDefinitionDTO publish(Long id) {
|
||||
// 获取当前工作流定义
|
||||
WorkflowDefinition current = workflowDefinitionRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
|
||||
|
||||
// 验证当前状态
|
||||
if (current.getStatus() != WorkflowDefinitionStatusEnum.DRAFT) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_NOT_DRAFT);
|
||||
public void suspendWorkflow(String processInstanceId) {
|
||||
try {
|
||||
runtimeService.suspendProcessInstanceById(processInstanceId);
|
||||
log.info("Suspended workflow instance: {}", processInstanceId);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to suspend workflow instance: {}", processInstanceId, e);
|
||||
throw new RuntimeException("Failed to suspend workflow instance", e);
|
||||
}
|
||||
|
||||
// 验证工作流定义
|
||||
if (!validate(id)) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_CONFIG_INVALID);
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
current.setStatus(WorkflowDefinitionStatusEnum.PUBLISHED);
|
||||
current = workflowDefinitionRepository.save(current);
|
||||
|
||||
return workflowDefinitionConverter.toDto(current);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public WorkflowDefinitionDTO disable(Long id) {
|
||||
// 获取当前工作流定义
|
||||
WorkflowDefinition current = workflowDefinitionRepository.findById(id)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
|
||||
|
||||
// 验证当前状态
|
||||
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));
|
||||
public void resumeWorkflow(String processInstanceId) {
|
||||
try {
|
||||
runtimeService.activateProcessInstanceById(processInstanceId);
|
||||
log.info("Resumed workflow instance: {}", processInstanceId);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to resume workflow instance: {}", processInstanceId, e);
|
||||
throw new RuntimeException("Failed to resume workflow instance", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
@ -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
Loading…
Reference in New Issue
Block a user