可正常启动
This commit is contained in:
parent
568859ce35
commit
33448f5ac5
548
backend/docs/workflow-development.md
Normal file
548
backend/docs/workflow-development.md
Normal file
@ -0,0 +1,548 @@
|
||||
# 工作流引擎功能开发文档
|
||||
|
||||
## 已实现功能
|
||||
|
||||
### 1. 核心功能
|
||||
- [x] 工作流定义管理
|
||||
- 工作流定义的CRUD操作
|
||||
- 工作流状态管理(草稿、发布、禁用)
|
||||
- 工作流版本控制
|
||||
|
||||
- [x] 工作流实例管理
|
||||
- 工作流实例的创建和执行
|
||||
- 实例状态管理(运行、暂停、完成、失败、取消)
|
||||
- 实例执行历史记录
|
||||
|
||||
- [x] 节点实例管理
|
||||
- 节点实例的创建和执行
|
||||
- 节点状态管理(等待、运行、完成、失败、取消)
|
||||
- 节点执行历史记录
|
||||
|
||||
### 2. 节点类型
|
||||
- [x] Shell节点
|
||||
- 支持Windows/Unix跨平台
|
||||
- 工作目录配置
|
||||
- 环境变量支持
|
||||
- 超时处理
|
||||
- 输出流日志记录
|
||||
|
||||
### 3. 日志管理
|
||||
- [x] 日志记录
|
||||
- 工作流级别日志
|
||||
- 节点级别日志
|
||||
- 系统级别日志
|
||||
- 变量更新日志
|
||||
|
||||
- [x] 日志查询
|
||||
- 工作流日志查询
|
||||
- 节点日志查询
|
||||
- 支持分页和条件过滤
|
||||
|
||||
## 待实现功能
|
||||
|
||||
### 1. 节点类型扩展
|
||||
#### 1.1 Jenkins节点
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加Jenkins客户端依赖
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.offbytwo.jenkins</groupId>
|
||||
<artifactId>jenkins-client</artifactId>
|
||||
<version>0.3.8</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
2. 实现Jenkins节点执行器
|
||||
```java
|
||||
@Component
|
||||
public class JenkinsNodeExecutor extends AbstractNodeExecutor {
|
||||
@Override
|
||||
protected boolean doExecute(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) {
|
||||
JenkinsNodeConfig jenkinsConfig = (JenkinsNodeConfig) config;
|
||||
// 1. 连接Jenkins服务器
|
||||
// 2. 触发构建任务
|
||||
// 3. 等待构建完成
|
||||
// 4. 获取构建结果
|
||||
// 5. 处理构建产物
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. 添加构建结果处理
|
||||
- 支持构建状态判断
|
||||
- 支持测试结果解析
|
||||
- 支持构建产物下载
|
||||
```
|
||||
|
||||
#### 1.2 Git节点
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加JGit依赖
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jgit</groupId>
|
||||
<artifactId>org.eclipse.jgit</artifactId>
|
||||
<version>6.5.0.202303070854-r</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
2. 实现Git节点执行器
|
||||
```java
|
||||
@Component
|
||||
public class GitNodeExecutor extends AbstractNodeExecutor {
|
||||
@Override
|
||||
protected boolean doExecute(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) {
|
||||
GitNodeConfig gitConfig = (GitNodeConfig) config;
|
||||
// 1. 克隆/拉取代码
|
||||
// 2. 切换分支/标签
|
||||
// 3. 执行Git操作
|
||||
// 4. 提交更改
|
||||
// 5. 推送远程
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. 添加Git操作支持
|
||||
- 分支管理
|
||||
- 标签管理
|
||||
- 代码合并
|
||||
- 冲突处理
|
||||
```
|
||||
|
||||
### 2. 日志管理增强
|
||||
|
||||
#### 2.1 日志导出功能
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加Excel导出依赖
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>5.2.3</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
2. 实现日志导出服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowLogExportService {
|
||||
public void exportToExcel(Long workflowInstanceId, OutputStream output) {
|
||||
// 1. 查询日志数据
|
||||
// 2. 创建Excel工作簿
|
||||
// 3. 写入日志数据
|
||||
// 4. 格式化和样式设置
|
||||
// 5. 输出文件
|
||||
}
|
||||
|
||||
public void exportToCsv(Long workflowInstanceId, Writer writer) {
|
||||
// 1. 查询日志数据
|
||||
// 2. 写入CSV头
|
||||
// 3. 写入日志数据
|
||||
// 4. 刷新输出
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. 添加导出接口
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/workflow")
|
||||
public class WorkflowLogController {
|
||||
@GetMapping("/{instanceId}/logs/export")
|
||||
public void exportLogs(@PathVariable Long instanceId,
|
||||
@RequestParam String format,
|
||||
HttpServletResponse response) {
|
||||
// 1. 设置响应头
|
||||
// 2. 获取输出流
|
||||
// 3. 调用导出服务
|
||||
// 4. 处理异常
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 日志清理功能
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加定时任务配置
|
||||
```java
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
public class WorkflowLogCleanupConfig {
|
||||
@Scheduled(cron = "${workflow.log.cleanup.cron}")
|
||||
public void cleanupLogs() {
|
||||
// 1. 获取清理策略
|
||||
// 2. 查询过期日志
|
||||
// 3. 批量删除日志
|
||||
// 4. 记录清理结果
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现日志清理服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowLogCleanupService {
|
||||
public void cleanupByTime(LocalDateTime before) {
|
||||
// 1. 分批查询日志
|
||||
// 2. 物理删除日志
|
||||
// 3. 记录清理统计
|
||||
}
|
||||
|
||||
public void cleanupByInstance(Long workflowInstanceId) {
|
||||
// 1. 查询实例日志
|
||||
// 2. 物理删除日志
|
||||
// 3. 记录清理结果
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 工作流监控
|
||||
|
||||
#### 3.1 执行监控
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加监控指标收集
|
||||
```java
|
||||
@Component
|
||||
public class WorkflowMetricsCollector {
|
||||
private final MeterRegistry registry;
|
||||
|
||||
public void recordExecutionTime(String workflowName, long timeMs) {
|
||||
// 记录执行时长
|
||||
}
|
||||
|
||||
public void recordNodeExecutionTime(String nodeName, long timeMs) {
|
||||
// 记录节点执行时长
|
||||
}
|
||||
|
||||
public void incrementExecutionCounter(String workflowName, String status) {
|
||||
// 增加执行计数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现监控数据存储
|
||||
```java
|
||||
@Entity
|
||||
@Table(name = "wf_workflow_metrics")
|
||||
public class WorkflowMetrics {
|
||||
// 执行时长
|
||||
private Long executionTime;
|
||||
// 成功率
|
||||
private Double successRate;
|
||||
// 失败次数
|
||||
private Integer failureCount;
|
||||
// 平均节点执行时长
|
||||
private Long avgNodeExecutionTime;
|
||||
}
|
||||
```
|
||||
|
||||
3. 添加监控接口
|
||||
```java
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/workflow/metrics")
|
||||
public class WorkflowMetricsController {
|
||||
@GetMapping("/overview")
|
||||
public WorkflowMetricsOverview getOverview() {
|
||||
// 返回整体监控数据
|
||||
}
|
||||
|
||||
@GetMapping("/{workflowId}")
|
||||
public WorkflowMetricsDetail getDetail(@PathVariable Long workflowId) {
|
||||
// 返回具体工作流监控数据
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 告警通知
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 定义告警规则
|
||||
```java
|
||||
@Entity
|
||||
@Table(name = "wf_alert_rule")
|
||||
public class AlertRule {
|
||||
// 告警类型
|
||||
private AlertType type;
|
||||
// 告警阈值
|
||||
private String threshold;
|
||||
// 告警级别
|
||||
private AlertLevel level;
|
||||
// 通知方式
|
||||
private List<NotifyChannel> channels;
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现告警服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowAlertService {
|
||||
public void checkAndAlert(WorkflowInstance instance) {
|
||||
// 1. 获取告警规则
|
||||
// 2. 检查是否触发
|
||||
// 3. 生成告警信息
|
||||
// 4. 发送通知
|
||||
}
|
||||
|
||||
public void sendAlert(Alert alert) {
|
||||
// 1. 获取通知渠道
|
||||
// 2. 发送通知
|
||||
// 3. 记录通知结果
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 工作流调度
|
||||
|
||||
#### 4.1 定时调度
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加调度配置
|
||||
```java
|
||||
@Entity
|
||||
@Table(name = "wf_workflow_schedule")
|
||||
public class WorkflowSchedule {
|
||||
// 调度类型
|
||||
private ScheduleType type;
|
||||
// Cron表达式
|
||||
private String cronExpression;
|
||||
// 生效时间
|
||||
private LocalDateTime effectiveTime;
|
||||
// 失效时间
|
||||
private LocalDateTime expireTime;
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现调度服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowScheduleService {
|
||||
public void schedule(WorkflowSchedule schedule) {
|
||||
// 1. 验证调度配置
|
||||
// 2. 创建调度任务
|
||||
// 3. 注册到调度器
|
||||
// 4. 记录调度状态
|
||||
}
|
||||
|
||||
public void executeScheduledWorkflow(Long scheduleId) {
|
||||
// 1. 获取调度配置
|
||||
// 2. 创建工作流实例
|
||||
// 3. 执行工作流
|
||||
// 4. 记录执行结果
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 依赖调度
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加依赖配置
|
||||
```java
|
||||
@Entity
|
||||
@Table(name = "wf_workflow_dependency")
|
||||
public class WorkflowDependency {
|
||||
// 上游工作流
|
||||
private Long upstreamWorkflowId;
|
||||
// 依赖类型
|
||||
private DependencyType type;
|
||||
// 触发条件
|
||||
private String condition;
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现依赖服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowDependencyService {
|
||||
public void handleWorkflowComplete(Long workflowInstanceId) {
|
||||
// 1. 查询依赖配置
|
||||
// 2. 检查触发条件
|
||||
// 3. 触发下游工作流
|
||||
// 4. 记录依赖执行
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 工作流权限管理
|
||||
|
||||
#### 5.1 权限模型
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 定义权限模型
|
||||
```java
|
||||
@Entity
|
||||
@Table(name = "wf_permission")
|
||||
public class WorkflowPermission {
|
||||
// 权限类型
|
||||
private PermissionType type;
|
||||
// 权限范围
|
||||
private PermissionScope scope;
|
||||
// 权限值
|
||||
private String value;
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现权限服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowPermissionService {
|
||||
public boolean hasPermission(Long userId, Long workflowId, PermissionType type) {
|
||||
// 1. 查询用户权限
|
||||
// 2. 检查继承权限
|
||||
// 3. 验证权限范围
|
||||
// 4. 返回结果
|
||||
}
|
||||
|
||||
public void grantPermission(PermissionGrant grant) {
|
||||
// 1. 验证授权者权限
|
||||
// 2. 创建权限记录
|
||||
// 3. 记录授权日志
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 工作流版本管理
|
||||
|
||||
#### 6.1 版本控制
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加版本模型
|
||||
```java
|
||||
@Entity
|
||||
@Table(name = "wf_workflow_version")
|
||||
public class WorkflowVersion {
|
||||
// 版本号
|
||||
private String version;
|
||||
// 变更内容
|
||||
private String changelog;
|
||||
// 工作流定义
|
||||
private String definition;
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现版本服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowVersionService {
|
||||
public void createVersion(Long workflowId) {
|
||||
// 1. 生成版本号
|
||||
// 2. 保存当前定义
|
||||
// 3. 记录变更日志
|
||||
}
|
||||
|
||||
public void rollback(Long workflowId, String version) {
|
||||
// 1. 验证版本有效性
|
||||
// 2. 恢复历史版本
|
||||
// 3. 记录回滚操作
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 工作流测试
|
||||
|
||||
#### 7.1 模拟执行
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 实现测试环境
|
||||
```java
|
||||
@Component
|
||||
public class WorkflowTestEnvironment {
|
||||
public WorkflowContext createTestContext() {
|
||||
// 1. 创建测试上下文
|
||||
// 2. 初始化测试数据
|
||||
// 3. 配置测试参数
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现测试服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowTestService {
|
||||
public TestResult simulateExecution(Long workflowId) {
|
||||
// 1. 准备测试环境
|
||||
// 2. 执行工作流
|
||||
// 3. 收集测试结果
|
||||
// 4. 生成测试报告
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8. 工作流模板
|
||||
|
||||
#### 8.1 模板管理
|
||||
```java
|
||||
// 实现思路:
|
||||
1. 添加模板模型
|
||||
```java
|
||||
@Entity
|
||||
@Table(name = "wf_workflow_template")
|
||||
public class WorkflowTemplate {
|
||||
// 模板名称
|
||||
private String name;
|
||||
// 模板描述
|
||||
private String description;
|
||||
// 模板定义
|
||||
private String definition;
|
||||
// 参数定义
|
||||
private List<TemplateParameter> parameters;
|
||||
}
|
||||
```
|
||||
|
||||
2. 实现模板服务
|
||||
```java
|
||||
@Service
|
||||
public class WorkflowTemplateService {
|
||||
public Long createFromTemplate(Long templateId, Map<String, Object> parameters) {
|
||||
// 1. 加载模板定义
|
||||
// 2. 替换模板参数
|
||||
// 3. 创建工作流定义
|
||||
// 4. 返回工作流ID
|
||||
}
|
||||
|
||||
public void saveAsTemplate(Long workflowId) {
|
||||
// 1. 提取工作流定义
|
||||
// 2. 提取参数定义
|
||||
// 3. 保存为模板
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 下一步开发计划
|
||||
|
||||
1. 优先实现日志管理增强功能
|
||||
- 日志导出功能
|
||||
- 日志清理功能
|
||||
- 日志查询性能优化
|
||||
|
||||
2. 实现工作流监控功能
|
||||
- 执行监控
|
||||
- 告警通知
|
||||
- 监控大屏
|
||||
|
||||
3. 实现剩余节点类型
|
||||
- Jenkins节点
|
||||
- Git节点
|
||||
- HTTP节点
|
||||
- 通知节点
|
||||
|
||||
4. 实现工作流调度功能
|
||||
- 定时调度
|
||||
- 依赖调度
|
||||
- 调度监控
|
||||
|
||||
## 技术栈
|
||||
|
||||
- 后端框架:Spring Boot 3.x
|
||||
- 数据库:MySQL 8.x
|
||||
- ORM框架:Spring Data JPA
|
||||
- 任务调度:Quartz
|
||||
- 监控:Micrometer + Prometheus
|
||||
- 日志:Logback
|
||||
- 工具库:
|
||||
- Apache Commons
|
||||
- Guava
|
||||
- MapStruct
|
||||
- Jackson
|
||||
@ -0,0 +1,26 @@
|
||||
package com.qqchen.deploy.backend.enums;
|
||||
|
||||
/**
|
||||
* 日志级别枚举
|
||||
*/
|
||||
public enum LogLevelEnum {
|
||||
/**
|
||||
* 调试
|
||||
*/
|
||||
DEBUG,
|
||||
|
||||
/**
|
||||
* 信息
|
||||
*/
|
||||
INFO,
|
||||
|
||||
/**
|
||||
* 警告
|
||||
*/
|
||||
WARN,
|
||||
|
||||
/**
|
||||
* 错误
|
||||
*/
|
||||
ERROR
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package com.qqchen.deploy.backend.enums;
|
||||
|
||||
/**
|
||||
* 日志类型枚举
|
||||
*/
|
||||
public enum LogTypeEnum {
|
||||
/**
|
||||
* 工作流开始
|
||||
*/
|
||||
WORKFLOW_START,
|
||||
|
||||
/**
|
||||
* 工作流完成
|
||||
*/
|
||||
WORKFLOW_COMPLETE,
|
||||
|
||||
/**
|
||||
* 工作流暂停
|
||||
*/
|
||||
WORKFLOW_PAUSE,
|
||||
|
||||
/**
|
||||
* 工作流恢复
|
||||
*/
|
||||
WORKFLOW_RESUME,
|
||||
|
||||
/**
|
||||
* 工作流取消
|
||||
*/
|
||||
WORKFLOW_CANCEL,
|
||||
|
||||
/**
|
||||
* 工作流错误
|
||||
*/
|
||||
WORKFLOW_ERROR,
|
||||
|
||||
/**
|
||||
* 节点开始
|
||||
*/
|
||||
NODE_START,
|
||||
|
||||
/**
|
||||
* 节点完成
|
||||
*/
|
||||
NODE_COMPLETE,
|
||||
|
||||
/**
|
||||
* 节点错误
|
||||
*/
|
||||
NODE_ERROR,
|
||||
|
||||
/**
|
||||
* 节点重试
|
||||
*/
|
||||
NODE_RETRY,
|
||||
|
||||
/**
|
||||
* 节点跳过
|
||||
*/
|
||||
NODE_SKIP,
|
||||
|
||||
/**
|
||||
* 变量更新
|
||||
*/
|
||||
VARIABLE_UPDATE,
|
||||
|
||||
/**
|
||||
* 系统日志
|
||||
*/
|
||||
SYSTEM
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.qqchen.deploy.backend.enums;
|
||||
|
||||
/**
|
||||
* 权限类型枚举
|
||||
*/
|
||||
public enum PermissionTypeEnum {
|
||||
/**
|
||||
* 查看权限
|
||||
*/
|
||||
VIEW,
|
||||
|
||||
/**
|
||||
* 执行权限
|
||||
*/
|
||||
EXECUTE,
|
||||
|
||||
/**
|
||||
* 管理权限
|
||||
*/
|
||||
MANAGE
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
package com.qqchen.deploy.backend.workflow.api;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.enums.LogTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.api.Response;
|
||||
import com.qqchen.deploy.backend.workflow.api.dto.NodeInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.api.request.WorkflowLogQueryRequest;
|
||||
import com.qqchen.deploy.backend.workflow.api.request.WorkflowStartRequest;
|
||||
import com.qqchen.deploy.backend.workflow.api.response.WorkflowLogDTO;
|
||||
import com.qqchen.deploy.backend.workflow.converter.WorkflowLogConverter;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
||||
import com.qqchen.deploy.backend.workflow.service.INodeInstanceService;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
|
||||
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.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 工作流API控制器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/workflow")
|
||||
@Tag(name = "工作流API", description = "工作流相关API接口")
|
||||
public class WorkflowApiController {
|
||||
|
||||
@Resource
|
||||
private IWorkflowInstanceService workflowInstanceService;
|
||||
|
||||
@Resource
|
||||
private INodeInstanceService nodeInstanceService;
|
||||
|
||||
@Resource
|
||||
private IWorkflowLogService workflowLogService;
|
||||
|
||||
@Resource
|
||||
private WorkflowLogConverter workflowLogConverter;
|
||||
|
||||
@Operation(summary = "启动工作流")
|
||||
@PostMapping("/start")
|
||||
public Response<WorkflowInstanceDTO> startWorkflow(@Valid @RequestBody WorkflowStartRequest request) {
|
||||
return Response.success(workflowInstanceService.start(
|
||||
request.getDefinitionId(),
|
||||
request.getProjectEnvId(),
|
||||
request.getVariables() != null ? request.getVariables().toString() : null
|
||||
));
|
||||
}
|
||||
|
||||
@Operation(summary = "取消工作流")
|
||||
@PostMapping("/{instanceId}/cancel")
|
||||
public Response<Boolean> cancelWorkflow(
|
||||
@Parameter(description = "工作流实例ID", required = true) @PathVariable Long instanceId
|
||||
) {
|
||||
return Response.success(workflowInstanceService.cancel(instanceId));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取工作流实例详情")
|
||||
@GetMapping("/{instanceId}")
|
||||
public Response<WorkflowInstanceDTO> getWorkflowInstance(
|
||||
@Parameter(description = "工作流实例ID", required = true) @PathVariable Long instanceId
|
||||
) {
|
||||
return Response.success(workflowInstanceService.findById(instanceId));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取工作流节点列表")
|
||||
@GetMapping("/{instanceId}/nodes")
|
||||
public Response<List<NodeInstanceDTO>> getWorkflowNodes(
|
||||
@Parameter(description = "工作流实例ID", required = true) @PathVariable Long instanceId
|
||||
) {
|
||||
return Response.success(nodeInstanceService.findByWorkflowInstanceId(instanceId));
|
||||
}
|
||||
|
||||
@Operation(summary = "重试工作流节点")
|
||||
@PostMapping("/node/{nodeId}/retry")
|
||||
public Response<Boolean> retryNode(
|
||||
@Parameter(description = "节点实例ID", required = true) @PathVariable Long nodeId
|
||||
) {
|
||||
// TODO: 实现节点重试逻辑
|
||||
return Response.success(true);
|
||||
}
|
||||
|
||||
@Operation(summary = "跳过工作流节点")
|
||||
@PostMapping("/node/{nodeId}/skip")
|
||||
public Response<Boolean> skipNode(
|
||||
@Parameter(description = "节点实例ID", required = true) @PathVariable Long nodeId
|
||||
) {
|
||||
// TODO: 实现节点跳过逻辑
|
||||
return Response.success(true);
|
||||
}
|
||||
|
||||
@Operation(summary = "查询工作流日志")
|
||||
@PostMapping("/logs")
|
||||
public Response<Page<WorkflowLogDTO>> getLogs(@Valid @RequestBody WorkflowLogQueryRequest request) {
|
||||
Page<WorkflowLog> logs = workflowLogService.findLogs(
|
||||
request.getWorkflowInstanceId(),
|
||||
request.getType(),
|
||||
request.getLevel(),
|
||||
PageRequest.of(request.getPageNum() - 1, request.getPageSize())
|
||||
);
|
||||
return Response.success(logs.map(workflowLogConverter::toDto));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询节点日志")
|
||||
@GetMapping("/node/{nodeId}/logs")
|
||||
public Response<List<WorkflowLogDTO>> getNodeLogs(
|
||||
@Parameter(description = "节点实例ID", required = true) @PathVariable Long nodeId,
|
||||
@Parameter(description = "日志类型") @RequestParam(required = false) LogTypeEnum type,
|
||||
@Parameter(description = "日志级别") @RequestParam(required = false) LogLevelEnum level
|
||||
) {
|
||||
List<WorkflowLog> logs = workflowLogService.findNodeLogs(nodeId, type, level);
|
||||
return Response.success(logs.stream()
|
||||
.map(workflowLogConverter::toDto)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Operation(summary = "分页查询节点日志")
|
||||
@GetMapping("/node/{nodeId}/logs/page")
|
||||
public Response<Page<WorkflowLogDTO>> getNodeLogsPage(
|
||||
@Parameter(description = "节点实例ID", required = true) @PathVariable Long nodeId,
|
||||
@Parameter(description = "日志类型") @RequestParam(required = false) LogTypeEnum type,
|
||||
@Parameter(description = "日志级别") @RequestParam(required = false) LogLevelEnum level,
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@Parameter(description = "每页大小") @RequestParam(defaultValue = "20") Integer pageSize
|
||||
) {
|
||||
Page<WorkflowLog> logs = workflowLogService.findNodeLogs(
|
||||
nodeId, type, level, PageRequest.of(pageNum - 1, pageSize)
|
||||
);
|
||||
return Response.success(logs.map(workflowLogConverter::toDto));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.qqchen.deploy.backend.workflow.api.request;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.enums.LogTypeEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 工作流日志查询请求
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "工作流日志查询请求")
|
||||
public class WorkflowLogQueryRequest {
|
||||
|
||||
/**
|
||||
* 工作流实例ID
|
||||
*/
|
||||
@NotNull(message = "工作流实例ID不能为空")
|
||||
@Schema(description = "工作流实例ID", required = true)
|
||||
private Long workflowInstanceId;
|
||||
|
||||
/**
|
||||
* 节点实例ID
|
||||
*/
|
||||
@Schema(description = "节点实例ID")
|
||||
private Long nodeInstanceId;
|
||||
|
||||
/**
|
||||
* 日志类型
|
||||
*/
|
||||
@Schema(description = "日志类型")
|
||||
private LogTypeEnum type;
|
||||
|
||||
/**
|
||||
* 日志级别
|
||||
*/
|
||||
@Schema(description = "日志级别")
|
||||
private LogLevelEnum level;
|
||||
|
||||
/**
|
||||
* 页码
|
||||
*/
|
||||
@Schema(description = "页码", defaultValue = "1")
|
||||
private Integer pageNum = 1;
|
||||
|
||||
/**
|
||||
* 每页大小
|
||||
*/
|
||||
@Schema(description = "每页大小", defaultValue = "20")
|
||||
private Integer pageSize = 20;
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.qqchen.deploy.backend.workflow.api.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工作流启动请求
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "工作流启动请求")
|
||||
public class WorkflowStartRequest {
|
||||
|
||||
/**
|
||||
* 工作流定义ID
|
||||
*/
|
||||
@NotNull(message = "工作流定义ID不能为空")
|
||||
@Schema(description = "工作流定义ID", required = true)
|
||||
private Long definitionId;
|
||||
|
||||
/**
|
||||
* 项目环境ID
|
||||
*/
|
||||
@NotNull(message = "项目环境ID不能为空")
|
||||
@Schema(description = "项目环境ID", required = true)
|
||||
private Long projectEnvId;
|
||||
|
||||
/**
|
||||
* 工作流变量
|
||||
*/
|
||||
@Schema(description = "工作流变量")
|
||||
private Map<String, Object> variables;
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package com.qqchen.deploy.backend.workflow.api.response;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.enums.LogTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 工作流日志DTO
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "工作流日志")
|
||||
public class WorkflowLogDTO extends BaseDTO {
|
||||
|
||||
/**
|
||||
* 日志ID
|
||||
*/
|
||||
@Schema(description = "日志ID")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 工作流实例ID
|
||||
*/
|
||||
@Schema(description = "工作流实例ID")
|
||||
private Long workflowInstanceId;
|
||||
|
||||
/**
|
||||
* 节点实例ID
|
||||
*/
|
||||
@Schema(description = "节点实例ID")
|
||||
private Long nodeInstanceId;
|
||||
|
||||
/**
|
||||
* 日志类型
|
||||
*/
|
||||
@Schema(description = "日志类型")
|
||||
private LogTypeEnum type;
|
||||
|
||||
/**
|
||||
* 日志级别
|
||||
*/
|
||||
@Schema(description = "日志级别")
|
||||
private LogLevelEnum level;
|
||||
|
||||
/**
|
||||
* 日志内容
|
||||
*/
|
||||
@Schema(description = "日志内容")
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 详细信息
|
||||
*/
|
||||
@Schema(description = "详细信息")
|
||||
private String detail;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package com.qqchen.deploy.backend.workflow.converter;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
|
||||
import com.qqchen.deploy.backend.workflow.api.response.WorkflowLogDTO;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
||||
import org.mapstruct.Mapper;
|
||||
|
||||
/**
|
||||
* 工作流日志转换器
|
||||
*/
|
||||
@Mapper(config = BaseConverter.class)
|
||||
public interface WorkflowLogConverter extends BaseConverter<WorkflowLog, WorkflowLogDTO> {
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package com.qqchen.deploy.backend.workflow.engine;
|
||||
|
||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 工作流上下文
|
||||
*/
|
||||
@Data
|
||||
public class WorkflowContext {
|
||||
|
||||
/**
|
||||
* 工作流实例
|
||||
*/
|
||||
private WorkflowInstance workflowInstance;
|
||||
|
||||
/**
|
||||
* 当前节点实例
|
||||
*/
|
||||
private NodeInstance currentNode;
|
||||
|
||||
/**
|
||||
* 所有节点实例
|
||||
*/
|
||||
private List<NodeInstance> allNodes;
|
||||
|
||||
/**
|
||||
* 工作流变量
|
||||
*/
|
||||
private Map<String, Object> variables;
|
||||
|
||||
/**
|
||||
* 临时变量(节点间传递)
|
||||
*/
|
||||
private Map<String, Object> tempVariables;
|
||||
|
||||
public WorkflowContext() {
|
||||
this.variables = new ConcurrentHashMap<>();
|
||||
this.tempVariables = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取变量值
|
||||
*/
|
||||
public Object getVariable(String key) {
|
||||
return variables.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置变量值
|
||||
*/
|
||||
public void setVariable(String key, Object value) {
|
||||
variables.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取临时变量值
|
||||
*/
|
||||
public Object getTempVariable(String key) {
|
||||
return tempVariables.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置临时变量值
|
||||
*/
|
||||
public void setTempVariable(String key, Object value) {
|
||||
tempVariables.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除临时变量
|
||||
*/
|
||||
public void clearTempVariables() {
|
||||
tempVariables.clear();
|
||||
}
|
||||
}
|
||||
@ -1 +1,54 @@
|
||||
|
||||
package com.qqchen.deploy.backend.workflow.engine;
|
||||
|
||||
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工作流引擎接口
|
||||
*/
|
||||
public interface WorkflowEngine {
|
||||
|
||||
/**
|
||||
* 启动工作流实例
|
||||
*
|
||||
* @param instanceId 工作流实例ID
|
||||
* @param variables 工作流变量
|
||||
*/
|
||||
void startInstance(Long instanceId, Map<String, Object> variables);
|
||||
|
||||
/**
|
||||
* 取消工作流实例
|
||||
*
|
||||
* @param instanceId 工作流实例ID
|
||||
*/
|
||||
void cancelInstance(Long instanceId);
|
||||
|
||||
/**
|
||||
* 暂停工作流实例
|
||||
*
|
||||
* @param instanceId 工作流实例ID
|
||||
*/
|
||||
void pauseInstance(Long instanceId);
|
||||
|
||||
/**
|
||||
* 恢复工作流实例
|
||||
*
|
||||
* @param instanceId 工作流实例ID
|
||||
*/
|
||||
void resumeInstance(Long instanceId);
|
||||
|
||||
/**
|
||||
* 重试工作流节点
|
||||
*
|
||||
* @param nodeInstanceId 节点实例ID
|
||||
*/
|
||||
void retryNode(Long nodeInstanceId);
|
||||
|
||||
/**
|
||||
* 跳过工作流节点
|
||||
*
|
||||
* @param nodeInstanceId 节点实例ID
|
||||
*/
|
||||
void skipNode(Long nodeInstanceId);
|
||||
}
|
||||
@ -0,0 +1,241 @@
|
||||
package com.qqchen.deploy.backend.workflow.engine.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.workflow.engine.WorkflowContext;
|
||||
import com.qqchen.deploy.backend.workflow.engine.WorkflowEngine;
|
||||
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.repository.INodeInstanceRepository;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 工作流引擎默认实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DefaultWorkflowEngine implements WorkflowEngine {
|
||||
|
||||
@Resource
|
||||
private IWorkflowInstanceRepository workflowInstanceRepository;
|
||||
|
||||
@Resource
|
||||
private INodeInstanceRepository nodeInstanceRepository;
|
||||
|
||||
@Resource
|
||||
private IWorkflowVariableService workflowVariableService;
|
||||
|
||||
@Resource
|
||||
private IWorkflowLogService workflowLogService;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void startInstance(Long instanceId, Map<String, Object> variables) {
|
||||
// 获取工作流实例
|
||||
WorkflowInstance instance = getWorkflowInstance(instanceId);
|
||||
if (instance.getStatus() != WorkflowStatusEnum.CREATED) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID);
|
||||
}
|
||||
|
||||
// 初始化工作流上下文
|
||||
WorkflowContext context = initContext(instance, variables);
|
||||
|
||||
try {
|
||||
// 更新工作流状态
|
||||
instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
workflowInstanceRepository.save(instance);
|
||||
|
||||
// 保存工作流变量
|
||||
workflowVariableService.saveVariables(instanceId, variables);
|
||||
|
||||
// 记录工作流日志
|
||||
workflowLogService.logWorkflowStart(instance);
|
||||
|
||||
// 执行第一个节点
|
||||
executeNextNode(context);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to start workflow instance: {}", instanceId, e);
|
||||
instance.setStatus(WorkflowStatusEnum.FAILED);
|
||||
workflowInstanceRepository.save(instance);
|
||||
workflowLogService.logWorkflowError(instance, e.getMessage());
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_START_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelInstance(Long instanceId) {
|
||||
WorkflowInstance instance = getWorkflowInstance(instanceId);
|
||||
if (instance.getStatus() != WorkflowStatusEnum.RUNNING && instance.getStatus() != WorkflowStatusEnum.PAUSED) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID);
|
||||
}
|
||||
|
||||
instance.setStatus(WorkflowStatusEnum.CANCELLED);
|
||||
workflowInstanceRepository.save(instance);
|
||||
|
||||
// 取消所有运行中的节点
|
||||
List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
|
||||
instanceId, NodeStatusEnum.RUNNING);
|
||||
runningNodes.forEach(node -> {
|
||||
node.setStatus(NodeStatusEnum.CANCELLED);
|
||||
nodeInstanceRepository.save(node);
|
||||
});
|
||||
|
||||
workflowLogService.logWorkflowCancel(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void pauseInstance(Long instanceId) {
|
||||
WorkflowInstance instance = getWorkflowInstance(instanceId);
|
||||
if (instance.getStatus() != WorkflowStatusEnum.RUNNING) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID);
|
||||
}
|
||||
|
||||
instance.setStatus(WorkflowStatusEnum.PAUSED);
|
||||
workflowInstanceRepository.save(instance);
|
||||
|
||||
// 暂停所有运行中的节点
|
||||
List<NodeInstance> runningNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
|
||||
instanceId, NodeStatusEnum.RUNNING);
|
||||
runningNodes.forEach(node -> {
|
||||
node.setStatus(NodeStatusEnum.PAUSED);
|
||||
nodeInstanceRepository.save(node);
|
||||
});
|
||||
|
||||
workflowLogService.logWorkflowPause(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void resumeInstance(Long instanceId) {
|
||||
WorkflowInstance instance = getWorkflowInstance(instanceId);
|
||||
if (instance.getStatus() != WorkflowStatusEnum.PAUSED) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID);
|
||||
}
|
||||
|
||||
instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
workflowInstanceRepository.save(instance);
|
||||
|
||||
// 恢复所有暂停的节点
|
||||
List<NodeInstance> pausedNodes = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(
|
||||
instanceId, NodeStatusEnum.PAUSED);
|
||||
pausedNodes.forEach(node -> {
|
||||
node.setStatus(NodeStatusEnum.RUNNING);
|
||||
nodeInstanceRepository.save(node);
|
||||
});
|
||||
|
||||
workflowLogService.logWorkflowResume(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void retryNode(Long nodeInstanceId) {
|
||||
NodeInstance node = getNodeInstance(nodeInstanceId);
|
||||
if (node.getStatus() != NodeStatusEnum.FAILED) {
|
||||
throw new BusinessException(ResponseCode.NODE_STATUS_INVALID);
|
||||
}
|
||||
|
||||
WorkflowInstance instance = getWorkflowInstance(node.getWorkflowInstanceId());
|
||||
if (instance.getStatus() != WorkflowStatusEnum.FAILED) {
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_STATUS_INVALID);
|
||||
}
|
||||
|
||||
// 重置节点状态
|
||||
node.setStatus(NodeStatusEnum.RUNNING);
|
||||
nodeInstanceRepository.save(node);
|
||||
|
||||
// 重置工作流状态
|
||||
instance.setStatus(WorkflowStatusEnum.RUNNING);
|
||||
workflowInstanceRepository.save(instance);
|
||||
|
||||
// 记录重试日志
|
||||
workflowLogService.logNodeRetry(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void skipNode(Long nodeInstanceId) {
|
||||
NodeInstance node = getNodeInstance(nodeInstanceId);
|
||||
if (node.getStatus() != NodeStatusEnum.FAILED) {
|
||||
throw new BusinessException(ResponseCode.NODE_STATUS_INVALID);
|
||||
}
|
||||
|
||||
// 更新节点状态为已跳过
|
||||
node.setStatus(NodeStatusEnum.SKIPPED);
|
||||
nodeInstanceRepository.save(node);
|
||||
|
||||
// 获取工作流实例
|
||||
WorkflowInstance instance = getWorkflowInstance(node.getWorkflowInstanceId());
|
||||
|
||||
// 初始化上下文
|
||||
WorkflowContext context = initContext(instance, workflowVariableService.getVariables(instance.getId()));
|
||||
context.setCurrentNode(node);
|
||||
|
||||
// 执行下一个节点
|
||||
executeNextNode(context);
|
||||
|
||||
// 记录跳过日志
|
||||
workflowLogService.logNodeSkip(node);
|
||||
}
|
||||
|
||||
private WorkflowInstance getWorkflowInstance(Long instanceId) {
|
||||
return workflowInstanceRepository.findById(instanceId)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.WORKFLOW_NOT_FOUND));
|
||||
}
|
||||
|
||||
private NodeInstance getNodeInstance(Long nodeInstanceId) {
|
||||
return nodeInstanceRepository.findById(nodeInstanceId)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.NODE_NOT_FOUND));
|
||||
}
|
||||
|
||||
private WorkflowContext initContext(WorkflowInstance instance, Map<String, Object> variables) {
|
||||
WorkflowContext context = new WorkflowContext();
|
||||
context.setWorkflowInstance(instance);
|
||||
context.setVariables(variables);
|
||||
context.setAllNodes(nodeInstanceRepository.findByWorkflowInstanceId(instance.getId()));
|
||||
return context;
|
||||
}
|
||||
|
||||
private void executeNextNode(WorkflowContext context) {
|
||||
NodeInstance currentNode = context.getCurrentNode();
|
||||
List<NodeInstance> allNodes = context.getAllNodes();
|
||||
|
||||
// 如果当前节点为空,说明是第一个节点
|
||||
Optional<NodeInstance> nextNode;
|
||||
if (currentNode == null) {
|
||||
nextNode = allNodes.stream()
|
||||
.filter(node -> node.getPreNodeId() == null)
|
||||
.findFirst();
|
||||
} else {
|
||||
nextNode = allNodes.stream()
|
||||
.filter(node -> currentNode.getId().equals(node.getPreNodeId()))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
if (nextNode.isPresent()) {
|
||||
NodeInstance node = nextNode.get();
|
||||
node.setStatus(NodeStatusEnum.RUNNING);
|
||||
nodeInstanceRepository.save(node);
|
||||
workflowLogService.logNodeStart(node);
|
||||
// TODO: 实际节点执行逻辑
|
||||
} else {
|
||||
// 没有下一个节点,工作流完成
|
||||
WorkflowInstance instance = context.getWorkflowInstance();
|
||||
instance.setStatus(WorkflowStatusEnum.COMPLETED);
|
||||
workflowInstanceRepository.save(instance);
|
||||
workflowLogService.logWorkflowComplete(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,115 @@
|
||||
package com.qqchen.deploy.backend.workflow.engine.node;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.workflow.engine.WorkflowContext;
|
||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 抽象节点执行器
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class AbstractNodeExecutor implements NodeExecutor {
|
||||
|
||||
@Resource
|
||||
protected IWorkflowLogService workflowLogService;
|
||||
|
||||
@Resource
|
||||
protected ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public boolean execute(NodeInstance nodeInstance, WorkflowContext context) {
|
||||
try {
|
||||
// 解析节点配置
|
||||
NodeConfig config = parseConfig(nodeInstance.getConfig());
|
||||
|
||||
// 执行前处理
|
||||
beforeExecute(nodeInstance, context, config);
|
||||
|
||||
// 执行节点
|
||||
boolean result = doExecute(nodeInstance, context, config);
|
||||
|
||||
// 执行后处理
|
||||
afterExecute(nodeInstance, context, config, result);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("Execute node failed: {}", e.getMessage(), e);
|
||||
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.ERROR,
|
||||
"Execute node failed: " + e.getMessage(), e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(NodeInstance nodeInstance, WorkflowContext context) {
|
||||
try {
|
||||
// 解析节点配置
|
||||
NodeConfig config = parseConfig(nodeInstance.getConfig());
|
||||
|
||||
// 执行取消操作
|
||||
doCancel(nodeInstance, context, config);
|
||||
} catch (Exception e) {
|
||||
log.error("Cancel node failed: {}", e.getMessage(), e);
|
||||
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.ERROR,
|
||||
"Cancel node failed: " + e.getMessage(), e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析节点配置
|
||||
*
|
||||
* @param configJson 配置JSON
|
||||
* @return 节点配置
|
||||
*/
|
||||
protected abstract NodeConfig parseConfig(String configJson) throws Exception;
|
||||
|
||||
/**
|
||||
* 执行前处理
|
||||
*
|
||||
* @param nodeInstance 节点实例
|
||||
* @param context 工作流上下文
|
||||
* @param config 节点配置
|
||||
*/
|
||||
protected void beforeExecute(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) {
|
||||
workflowLogService.logNodeStart(nodeInstance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行节点
|
||||
*
|
||||
* @param nodeInstance 节点实例
|
||||
* @param context 工作流上下文
|
||||
* @param config 节点配置
|
||||
* @return 执行结果
|
||||
*/
|
||||
protected abstract boolean doExecute(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) throws Exception;
|
||||
|
||||
/**
|
||||
* 执行后处理
|
||||
*
|
||||
* @param nodeInstance 节点实例
|
||||
* @param context 工作流上下文
|
||||
* @param config 节点配置
|
||||
* @param success 是否成功
|
||||
*/
|
||||
protected void afterExecute(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config, boolean success) {
|
||||
if (success) {
|
||||
workflowLogService.logNodeComplete(nodeInstance);
|
||||
} else {
|
||||
workflowLogService.logNodeError(nodeInstance, "Node execution failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行取消操作
|
||||
*
|
||||
* @param nodeInstance 节点实例
|
||||
* @param context 工作流上下文
|
||||
* @param config 节点配置
|
||||
*/
|
||||
protected abstract void doCancel(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) throws Exception;
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.qqchen.deploy.backend.workflow.engine.node;
|
||||
|
||||
import com.qqchen.deploy.backend.workflow.engine.WorkflowContext;
|
||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||
|
||||
/**
|
||||
* 节点执行器接口
|
||||
*/
|
||||
public interface NodeExecutor {
|
||||
|
||||
/**
|
||||
* 执行节点
|
||||
*
|
||||
* @param nodeInstance 节点实例
|
||||
* @param context 工作流上下文
|
||||
* @return 执行结果
|
||||
*/
|
||||
boolean execute(NodeInstance nodeInstance, WorkflowContext context);
|
||||
|
||||
/**
|
||||
* 取消节点执行
|
||||
*
|
||||
* @param nodeInstance 节点实例
|
||||
* @param context 工作流上下文
|
||||
*/
|
||||
void cancel(NodeInstance nodeInstance, WorkflowContext context);
|
||||
|
||||
/**
|
||||
* 获取支持的节点类型
|
||||
*
|
||||
* @return 节点类型
|
||||
*/
|
||||
NodeType getNodeType();
|
||||
}
|
||||
@ -0,0 +1,118 @@
|
||||
package com.qqchen.deploy.backend.workflow.engine.node.executor;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.workflow.engine.WorkflowContext;
|
||||
import com.qqchen.deploy.backend.workflow.engine.node.AbstractNodeExecutor;
|
||||
import com.qqchen.deploy.backend.workflow.engine.node.NodeConfig;
|
||||
import com.qqchen.deploy.backend.workflow.engine.node.NodeType;
|
||||
import com.qqchen.deploy.backend.workflow.engine.node.ScriptNodeConfig;
|
||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Shell节点执行器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ShellNodeExecutor extends AbstractNodeExecutor {
|
||||
|
||||
private static final int DEFAULT_TIMEOUT = 3600;
|
||||
private Process currentProcess;
|
||||
|
||||
@Override
|
||||
public NodeType getNodeType() {
|
||||
return NodeType.SHELL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NodeConfig parseConfig(String configJson) throws Exception {
|
||||
return objectMapper.readValue(configJson, ScriptNodeConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doExecute(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) throws Exception {
|
||||
ScriptNodeConfig shellConfig = (ScriptNodeConfig) config;
|
||||
|
||||
// 构建命令
|
||||
List<String> command = buildCommand(shellConfig);
|
||||
|
||||
// 创建进程构建器
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
processBuilder.redirectErrorStream(true);
|
||||
|
||||
// 设置工作目录
|
||||
if (shellConfig.getWorkingDirectory() != null) {
|
||||
processBuilder.directory(new java.io.File(shellConfig.getWorkingDirectory()));
|
||||
}
|
||||
|
||||
// 设置环境变量
|
||||
if (shellConfig.getEnvironment() != null) {
|
||||
processBuilder.environment().putAll(shellConfig.getEnvironment());
|
||||
}
|
||||
|
||||
// 启动进程
|
||||
currentProcess = processBuilder.start();
|
||||
|
||||
// 读取输出
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(currentProcess.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.INFO, line, null);
|
||||
}
|
||||
}
|
||||
|
||||
// 等待进程完成
|
||||
boolean completed = currentProcess.waitFor(shellConfig.getTimeout() != null ? shellConfig.getTimeout() : DEFAULT_TIMEOUT, TimeUnit.SECONDS);
|
||||
if (!completed) {
|
||||
currentProcess.destroyForcibly();
|
||||
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.ERROR, "Shell execution timeout", null);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查退出码
|
||||
int exitCode = currentProcess.exitValue();
|
||||
if (exitCode != 0) {
|
||||
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.ERROR,
|
||||
"Shell execution failed with exit code: " + exitCode, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doCancel(NodeInstance nodeInstance, WorkflowContext context, NodeConfig config) throws Exception {
|
||||
if (currentProcess != null && currentProcess.isAlive()) {
|
||||
currentProcess.destroyForcibly();
|
||||
workflowLogService.logSystem(context.getWorkflowInstance(), LogLevelEnum.WARN,
|
||||
"Shell execution cancelled", null);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> buildCommand(ScriptNodeConfig config) {
|
||||
List<String> command = new ArrayList<>();
|
||||
|
||||
// 添加Shell解释器
|
||||
if (System.getProperty("os.name").toLowerCase().contains("windows")) {
|
||||
command.add("cmd");
|
||||
command.add("/c");
|
||||
} else {
|
||||
command.add("sh");
|
||||
command.add("-c");
|
||||
}
|
||||
|
||||
// 添加脚本内容
|
||||
command.add(config.getScript());
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.qqchen.deploy.backend.workflow.entity;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.enums.LogTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.domain.Entity;
|
||||
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_workflow_log")
|
||||
public class WorkflowLog extends Entity<Long> {
|
||||
|
||||
/**
|
||||
* 工作流实例ID
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long workflowInstanceId;
|
||||
|
||||
/**
|
||||
* 节点实例ID
|
||||
*/
|
||||
private Long nodeInstanceId;
|
||||
|
||||
/**
|
||||
* 日志类型
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
private LogTypeEnum type;
|
||||
|
||||
/**
|
||||
* 日志级别
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
private LogLevelEnum level;
|
||||
|
||||
/**
|
||||
* 日志内容
|
||||
*/
|
||||
@Column(columnDefinition = "TEXT", nullable = false)
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 详细信息
|
||||
*/
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String detail;
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.qqchen.deploy.backend.workflow.entity;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.PermissionTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.domain.Entity;
|
||||
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_workflow_permission")
|
||||
public class WorkflowPermission extends Entity<Long> {
|
||||
|
||||
/**
|
||||
* 工作流定义ID
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long workflowDefinitionId;
|
||||
|
||||
/**
|
||||
* 权限类型
|
||||
*/
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
private PermissionTypeEnum type;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 角色ID
|
||||
*/
|
||||
private Long roleId;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
private Long departmentId;
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 工作流变量
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@jakarta.persistence.Entity
|
||||
@Table(name = "wf_workflow_variable")
|
||||
public class WorkflowVariable extends Entity<Long> {
|
||||
|
||||
/**
|
||||
* 工作流实例ID
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private Long workflowInstanceId;
|
||||
|
||||
/**
|
||||
* 变量名
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 变量值
|
||||
*/
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String value;
|
||||
|
||||
/**
|
||||
* 变量类型
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String type;
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package com.qqchen.deploy.backend.workflow.repository;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.enums.LogTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
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> {
|
||||
|
||||
/**
|
||||
* 查询节点日志
|
||||
*
|
||||
* @param nodeInstanceId 节点实例ID
|
||||
* @param type 日志类型
|
||||
* @param level 日志级别
|
||||
* @return 日志列表
|
||||
*/
|
||||
@Query("SELECT l FROM WorkflowLog l WHERE l.nodeInstanceId = :nodeInstanceId " +
|
||||
"AND (:type IS NULL OR l.type = :type) " +
|
||||
"AND (:level IS NULL OR l.level = :level) " +
|
||||
"AND l.deleted = false " +
|
||||
"ORDER BY l.createTime DESC")
|
||||
List<WorkflowLog> findNodeLogs(
|
||||
@Param("nodeInstanceId") Long nodeInstanceId,
|
||||
@Param("type") LogTypeEnum type,
|
||||
@Param("level") LogLevelEnum level
|
||||
);
|
||||
|
||||
/**
|
||||
* 分页查询节点日志
|
||||
*
|
||||
* @param nodeInstanceId 节点实例ID
|
||||
* @param type 日志类型
|
||||
* @param level 日志级别
|
||||
* @param pageable 分页参数
|
||||
* @return 日志分页
|
||||
*/
|
||||
@Query("SELECT l FROM WorkflowLog l WHERE l.nodeInstanceId = :nodeInstanceId " +
|
||||
"AND (:type IS NULL OR l.type = :type) " +
|
||||
"AND (:level IS NULL OR l.level = :level) " +
|
||||
"AND l.deleted = false " +
|
||||
"ORDER BY l.createTime DESC")
|
||||
Page<WorkflowLog> findNodeLogs(
|
||||
@Param("nodeInstanceId") Long nodeInstanceId,
|
||||
@Param("type") LogTypeEnum type,
|
||||
@Param("level") LogLevelEnum level,
|
||||
Pageable pageable
|
||||
);
|
||||
|
||||
/**
|
||||
* 分页查询工作流日志
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param type 日志类型
|
||||
* @param level 日志级别
|
||||
* @param pageable 分页参数
|
||||
* @return 日志分页
|
||||
*/
|
||||
@Query("SELECT l FROM WorkflowLog l WHERE l.workflowInstanceId = :workflowInstanceId " +
|
||||
"AND (:type IS NULL OR l.type = :type) " +
|
||||
"AND (:level IS NULL OR l.level = :level) " +
|
||||
"AND l.deleted = false " +
|
||||
"ORDER BY l.createTime DESC")
|
||||
Page<WorkflowLog> findByWorkflowInstanceIdAndTypeAndLevel(
|
||||
@Param("workflowInstanceId") Long workflowInstanceId,
|
||||
@Param("type") LogTypeEnum type,
|
||||
@Param("level") LogLevelEnum level,
|
||||
Pageable pageable
|
||||
);
|
||||
|
||||
/**
|
||||
* 删除工作流实例的所有日志
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
*/
|
||||
void deleteByWorkflowInstanceId(Long workflowInstanceId);
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
package com.qqchen.deploy.backend.workflow.repository;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.PermissionTypeEnum;
|
||||
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowPermission;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 工作流权限仓库接口
|
||||
*/
|
||||
@Repository
|
||||
public interface IWorkflowPermissionRepository extends IBaseRepository<WorkflowPermission, Long> {
|
||||
|
||||
/**
|
||||
* 根据工作流定义ID查询权限列表
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @return 权限列表
|
||||
*/
|
||||
List<WorkflowPermission> findByWorkflowDefinitionId(Long workflowDefinitionId);
|
||||
|
||||
/**
|
||||
* 根据工作流定义ID和权限类型分页查询权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param type 权限类型
|
||||
* @param pageable 分页参数
|
||||
* @return 权限分页
|
||||
*/
|
||||
Page<WorkflowPermission> findByWorkflowDefinitionIdAndType(Long workflowDefinitionId, PermissionTypeEnum type, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查询用户权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param userId 用户ID
|
||||
* @param type 权限类型
|
||||
* @return 权限
|
||||
*/
|
||||
Optional<WorkflowPermission> findByWorkflowDefinitionIdAndUserIdAndType(Long workflowDefinitionId, Long userId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 查询角色权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param roleId 角色ID
|
||||
* @param type 权限类型
|
||||
* @return 权限
|
||||
*/
|
||||
Optional<WorkflowPermission> findByWorkflowDefinitionIdAndRoleIdAndType(Long workflowDefinitionId, Long roleId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 查询部门权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param departmentId 部门ID
|
||||
* @param type 权限类型
|
||||
* @return 权限
|
||||
*/
|
||||
Optional<WorkflowPermission> findByWorkflowDefinitionIdAndDepartmentIdAndType(Long workflowDefinitionId, Long departmentId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 检查用户权限是否存在
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param userId 用户ID
|
||||
* @param type 权限类型
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByWorkflowDefinitionIdAndUserIdAndType(Long workflowDefinitionId, Long userId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 检查角色权限是否存在
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param roleId 角色ID
|
||||
* @param type 权限类型
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByWorkflowDefinitionIdAndRoleIdAndType(Long workflowDefinitionId, Long roleId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 检查部门权限是否存在
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param departmentId 部门ID
|
||||
* @param type 权限类型
|
||||
* @return 是否存在
|
||||
*/
|
||||
boolean existsByWorkflowDefinitionIdAndDepartmentIdAndType(Long workflowDefinitionId, Long departmentId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 删除用户权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param userId 用户ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void deleteByWorkflowDefinitionIdAndUserIdAndType(Long workflowDefinitionId, Long userId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 删除角色权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param roleId 角色ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void deleteByWorkflowDefinitionIdAndRoleIdAndType(Long workflowDefinitionId, Long roleId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 删除部门权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param departmentId 部门ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void deleteByWorkflowDefinitionIdAndDepartmentIdAndType(Long workflowDefinitionId, Long departmentId, PermissionTypeEnum type);
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.qqchen.deploy.backend.workflow.repository;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 工作流变量仓库接口
|
||||
*/
|
||||
@Repository
|
||||
public interface IWorkflowVariableRepository extends IBaseRepository<WorkflowVariable, Long> {
|
||||
|
||||
/**
|
||||
* 根据工作流实例ID查询变量列表
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @return 变量列表
|
||||
*/
|
||||
List<WorkflowVariable> findByWorkflowInstanceId(Long workflowInstanceId);
|
||||
|
||||
/**
|
||||
* 根据工作流实例ID和变量名查询变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param name 变量名
|
||||
* @return 变量
|
||||
*/
|
||||
Optional<WorkflowVariable> findByWorkflowInstanceIdAndName(Long workflowInstanceId, String name);
|
||||
|
||||
/**
|
||||
* 根据工作流实例ID和变量名删除变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param name 变量名
|
||||
*/
|
||||
void deleteByWorkflowInstanceIdAndName(Long workflowInstanceId, String name);
|
||||
|
||||
/**
|
||||
* 根据工作流实例ID删除所有变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
*/
|
||||
void deleteByWorkflowInstanceId(Long workflowInstanceId);
|
||||
}
|
||||
@ -0,0 +1,147 @@
|
||||
package com.qqchen.deploy.backend.workflow.service;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.enums.LogTypeEnum;
|
||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流日志服务接口
|
||||
*/
|
||||
public interface IWorkflowLogService {
|
||||
|
||||
/**
|
||||
* 记录工作流开始日志
|
||||
*
|
||||
* @param instance 工作流实例
|
||||
*/
|
||||
void logWorkflowStart(WorkflowInstance instance);
|
||||
|
||||
/**
|
||||
* 记录工作流完成日志
|
||||
*
|
||||
* @param instance 工作流实例
|
||||
*/
|
||||
void logWorkflowComplete(WorkflowInstance instance);
|
||||
|
||||
/**
|
||||
* 记录工作流暂停日志
|
||||
*
|
||||
* @param instance 工作流实例
|
||||
*/
|
||||
void logWorkflowPause(WorkflowInstance instance);
|
||||
|
||||
/**
|
||||
* 记录工作流恢复日志
|
||||
*
|
||||
* @param instance 工作流实例
|
||||
*/
|
||||
void logWorkflowResume(WorkflowInstance instance);
|
||||
|
||||
/**
|
||||
* 记录工作流取消日志
|
||||
*
|
||||
* @param instance 工作流实例
|
||||
*/
|
||||
void logWorkflowCancel(WorkflowInstance instance);
|
||||
|
||||
/**
|
||||
* 记录工作流错误日志
|
||||
*
|
||||
* @param instance 工作流实例
|
||||
* @param error 错误信息
|
||||
*/
|
||||
void logWorkflowError(WorkflowInstance instance, String error);
|
||||
|
||||
/**
|
||||
* 记录节点开始日志
|
||||
*
|
||||
* @param node 节点实例
|
||||
*/
|
||||
void logNodeStart(NodeInstance node);
|
||||
|
||||
/**
|
||||
* 记录节点完成日志
|
||||
*
|
||||
* @param node 节点实例
|
||||
*/
|
||||
void logNodeComplete(NodeInstance node);
|
||||
|
||||
/**
|
||||
* 记录节点错误日志
|
||||
*
|
||||
* @param node 节点实例
|
||||
* @param error 错误信息
|
||||
*/
|
||||
void logNodeError(NodeInstance node, String error);
|
||||
|
||||
/**
|
||||
* 记录节点重试日志
|
||||
*
|
||||
* @param node 节点实例
|
||||
*/
|
||||
void logNodeRetry(NodeInstance node);
|
||||
|
||||
/**
|
||||
* 记录节点跳过日志
|
||||
*
|
||||
* @param node 节点实例
|
||||
*/
|
||||
void logNodeSkip(NodeInstance node);
|
||||
|
||||
/**
|
||||
* 记录变量更新日志
|
||||
*
|
||||
* @param instance 工作流实例
|
||||
* @param variableName 变量名
|
||||
* @param value 变量值
|
||||
*/
|
||||
void logVariableUpdate(WorkflowInstance instance, String variableName, Object value);
|
||||
|
||||
/**
|
||||
* 记录系统日志
|
||||
*
|
||||
* @param instance 工作流实例
|
||||
* @param level 日志级别
|
||||
* @param content 日志内容
|
||||
* @param detail 详细信息
|
||||
*/
|
||||
void logSystem(WorkflowInstance instance, LogLevelEnum level, String content, String detail);
|
||||
|
||||
/**
|
||||
* 分页查询工作流日志
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param type 日志类型
|
||||
* @param level 日志级别
|
||||
* @param pageable 分页参数
|
||||
* @return 日志分页
|
||||
*/
|
||||
Page<WorkflowLog> findLogs(Long workflowInstanceId, LogTypeEnum type, LogLevelEnum level, Pageable pageable);
|
||||
|
||||
/**
|
||||
* 查询节点日志
|
||||
*
|
||||
* @param nodeInstanceId 节点实例ID
|
||||
* @param type 日志类型
|
||||
* @param level 日志级别
|
||||
* @return 日志列表
|
||||
*/
|
||||
List<WorkflowLog> findNodeLogs(Long nodeInstanceId, LogTypeEnum type, LogLevelEnum level);
|
||||
|
||||
/**
|
||||
* 分页查询节点日志
|
||||
*
|
||||
* @param nodeInstanceId 节点实例ID
|
||||
* @param type 日志类型
|
||||
* @param level 日志级别
|
||||
* @param pageable 分页参数
|
||||
* @return 日志分页
|
||||
*/
|
||||
Page<WorkflowLog> findNodeLogs(Long nodeInstanceId, LogTypeEnum type, LogLevelEnum level, Pageable pageable);
|
||||
}
|
||||
@ -0,0 +1,96 @@
|
||||
package com.qqchen.deploy.backend.workflow.service;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.PermissionTypeEnum;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowPermission;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流权限服务接口
|
||||
*/
|
||||
public interface IWorkflowPermissionService {
|
||||
|
||||
/**
|
||||
* 添加用户权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param userId 用户ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void addUserPermission(Long workflowDefinitionId, Long userId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 添加角色权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param roleId 角色ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void addRolePermission(Long workflowDefinitionId, Long roleId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 添加部门权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param departmentId 部门ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void addDepartmentPermission(Long workflowDefinitionId, Long departmentId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 移除用户权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param userId 用户ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void removeUserPermission(Long workflowDefinitionId, Long userId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 移除角色权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param roleId 角色ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void removeRolePermission(Long workflowDefinitionId, Long roleId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 移除部门权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param departmentId 部门ID
|
||||
* @param type 权限类型
|
||||
*/
|
||||
void removeDepartmentPermission(Long workflowDefinitionId, Long departmentId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 检查用户是否有指定权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param userId 用户ID
|
||||
* @param type 权限类型
|
||||
* @return 是否有权限
|
||||
*/
|
||||
boolean hasPermission(Long workflowDefinitionId, Long userId, PermissionTypeEnum type);
|
||||
|
||||
/**
|
||||
* 获取工作流定义的所有权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @return 权限列表
|
||||
*/
|
||||
List<WorkflowPermission> getPermissions(Long workflowDefinitionId);
|
||||
|
||||
/**
|
||||
* 分页查询工作流权限
|
||||
*
|
||||
* @param workflowDefinitionId 工作流定义ID
|
||||
* @param type 权限类型
|
||||
* @param pageable 分页参数
|
||||
* @return 权限分页
|
||||
*/
|
||||
Page<WorkflowPermission> findPermissions(Long workflowDefinitionId, PermissionTypeEnum type, Pageable pageable);
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package com.qqchen.deploy.backend.workflow.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工作流变量服务接口
|
||||
*/
|
||||
public interface IWorkflowVariableService {
|
||||
|
||||
/**
|
||||
* 保存工作流变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param variables 变量Map
|
||||
*/
|
||||
void saveVariables(Long workflowInstanceId, Map<String, Object> variables);
|
||||
|
||||
/**
|
||||
* 获取工作流变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @return 变量Map
|
||||
*/
|
||||
Map<String, Object> getVariables(Long workflowInstanceId);
|
||||
|
||||
/**
|
||||
* 获取工作流变量值
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param name 变量名
|
||||
* @return 变量值
|
||||
*/
|
||||
Object getVariable(Long workflowInstanceId, String name);
|
||||
|
||||
/**
|
||||
* 设置工作流变量值
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param name 变量名
|
||||
* @param value 变量值
|
||||
*/
|
||||
void setVariable(Long workflowInstanceId, String name, Object value);
|
||||
|
||||
/**
|
||||
* 删除工作流变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
* @param name 变量名
|
||||
*/
|
||||
void deleteVariable(Long workflowInstanceId, String name);
|
||||
|
||||
/**
|
||||
* 清空工作流变量
|
||||
*
|
||||
* @param workflowInstanceId 工作流实例ID
|
||||
*/
|
||||
void clearVariables(Long workflowInstanceId);
|
||||
}
|
||||
@ -0,0 +1,181 @@
|
||||
package com.qqchen.deploy.backend.workflow.service.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.LogLevelEnum;
|
||||
import com.qqchen.deploy.backend.enums.LogTypeEnum;
|
||||
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowLog;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowLogRepository;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowLogService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 工作流日志服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WorkflowLogServiceImpl implements IWorkflowLogService {
|
||||
|
||||
@Resource
|
||||
private IWorkflowLogRepository workflowLogRepository;
|
||||
|
||||
@Override
|
||||
public List<WorkflowLog> findNodeLogs(Long nodeInstanceId, LogTypeEnum type, LogLevelEnum level) {
|
||||
return workflowLogRepository.findNodeLogs(nodeInstanceId, type, level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<WorkflowLog> findNodeLogs(Long nodeInstanceId, LogTypeEnum type, LogLevelEnum level, Pageable pageable) {
|
||||
return workflowLogRepository.findNodeLogs(nodeInstanceId, type, level, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<WorkflowLog> findLogs(Long workflowInstanceId, LogTypeEnum type, LogLevelEnum level, Pageable pageable) {
|
||||
return workflowLogRepository.findByWorkflowInstanceIdAndTypeAndLevel(workflowInstanceId, type, level, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWorkflowStart(WorkflowInstance instance) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(instance.getId());
|
||||
log.setType(LogTypeEnum.WORKFLOW_START);
|
||||
log.setLevel(LogLevelEnum.INFO);
|
||||
log.setContent("工作流开始执行");
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWorkflowComplete(WorkflowInstance instance) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(instance.getId());
|
||||
log.setType(LogTypeEnum.WORKFLOW_COMPLETE);
|
||||
log.setLevel(LogLevelEnum.INFO);
|
||||
log.setContent("工作流执行完成");
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWorkflowPause(WorkflowInstance instance) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(instance.getId());
|
||||
log.setType(LogTypeEnum.WORKFLOW_PAUSE);
|
||||
log.setLevel(LogLevelEnum.INFO);
|
||||
log.setContent("工作流已暂停");
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWorkflowResume(WorkflowInstance instance) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(instance.getId());
|
||||
log.setType(LogTypeEnum.WORKFLOW_RESUME);
|
||||
log.setLevel(LogLevelEnum.INFO);
|
||||
log.setContent("工作流已恢复");
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWorkflowCancel(WorkflowInstance instance) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(instance.getId());
|
||||
log.setType(LogTypeEnum.WORKFLOW_CANCEL);
|
||||
log.setLevel(LogLevelEnum.INFO);
|
||||
log.setContent("工作流已取消");
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logWorkflowError(WorkflowInstance instance, String error) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(instance.getId());
|
||||
log.setType(LogTypeEnum.WORKFLOW_ERROR);
|
||||
log.setLevel(LogLevelEnum.ERROR);
|
||||
log.setContent("工作流执行出错");
|
||||
log.setDetail(error);
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logNodeStart(NodeInstance node) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(node.getWorkflowInstance().getId());
|
||||
log.setNodeInstanceId(node.getId());
|
||||
log.setType(LogTypeEnum.NODE_START);
|
||||
log.setLevel(LogLevelEnum.INFO);
|
||||
log.setContent(String.format("节点[%s]开始执行", node.getName()));
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logNodeComplete(NodeInstance node) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(node.getWorkflowInstance().getId());
|
||||
log.setNodeInstanceId(node.getId());
|
||||
log.setType(LogTypeEnum.NODE_COMPLETE);
|
||||
log.setLevel(LogLevelEnum.INFO);
|
||||
log.setContent(String.format("节点[%s]执行完成", node.getName()));
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logNodeError(NodeInstance node, String error) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(node.getWorkflowInstance().getId());
|
||||
log.setNodeInstanceId(node.getId());
|
||||
log.setType(LogTypeEnum.NODE_ERROR);
|
||||
log.setLevel(LogLevelEnum.ERROR);
|
||||
log.setContent(String.format("节点[%s]执行出错", node.getName()));
|
||||
log.setDetail(error);
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logNodeRetry(NodeInstance node) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(node.getWorkflowInstance().getId());
|
||||
log.setNodeInstanceId(node.getId());
|
||||
log.setType(LogTypeEnum.NODE_RETRY);
|
||||
log.setLevel(LogLevelEnum.WARN);
|
||||
log.setContent(String.format("节点[%s]重试执行", node.getName()));
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logNodeSkip(NodeInstance node) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(node.getWorkflowInstance().getId());
|
||||
log.setNodeInstanceId(node.getId());
|
||||
log.setType(LogTypeEnum.NODE_SKIP);
|
||||
log.setLevel(LogLevelEnum.WARN);
|
||||
log.setContent(String.format("节点[%s]已跳过", node.getName()));
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logVariableUpdate(WorkflowInstance instance, String variableName, Object value) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(instance.getId());
|
||||
log.setType(LogTypeEnum.VARIABLE_UPDATE);
|
||||
log.setLevel(LogLevelEnum.INFO);
|
||||
log.setContent(String.format("变量[%s]更新为[%s]", variableName, value));
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSystem(WorkflowInstance instance, LogLevelEnum level, String content, String detail) {
|
||||
WorkflowLog log = new WorkflowLog();
|
||||
log.setWorkflowInstanceId(instance.getId());
|
||||
log.setType(LogTypeEnum.SYSTEM);
|
||||
log.setLevel(level);
|
||||
log.setContent(content);
|
||||
log.setDetail(detail);
|
||||
workflowLogRepository.save(log);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package com.qqchen.deploy.backend.workflow.service.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.enums.PermissionTypeEnum;
|
||||
import com.qqchen.deploy.backend.system.entity.User;
|
||||
import com.qqchen.deploy.backend.system.repository.IUserRepository;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowPermission;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowPermissionRepository;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowPermissionService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 工作流权限服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WorkflowPermissionServiceImpl implements IWorkflowPermissionService {
|
||||
|
||||
@Resource
|
||||
private IWorkflowPermissionRepository workflowPermissionRepository;
|
||||
|
||||
@Resource
|
||||
private IUserRepository userRepository;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void addUserPermission(Long workflowDefinitionId, Long userId, PermissionTypeEnum type) {
|
||||
Optional<WorkflowPermission> existingPermission = workflowPermissionRepository
|
||||
.findByWorkflowDefinitionIdAndUserIdAndType(workflowDefinitionId, userId, type);
|
||||
if (existingPermission.isEmpty()) {
|
||||
WorkflowPermission permission = new WorkflowPermission();
|
||||
permission.setWorkflowDefinitionId(workflowDefinitionId);
|
||||
permission.setUserId(userId);
|
||||
permission.setType(type);
|
||||
workflowPermissionRepository.save(permission);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void addRolePermission(Long workflowDefinitionId, Long roleId, PermissionTypeEnum type) {
|
||||
Optional<WorkflowPermission> existingPermission = workflowPermissionRepository
|
||||
.findByWorkflowDefinitionIdAndRoleIdAndType(workflowDefinitionId, roleId, type);
|
||||
if (existingPermission.isEmpty()) {
|
||||
WorkflowPermission permission = new WorkflowPermission();
|
||||
permission.setWorkflowDefinitionId(workflowDefinitionId);
|
||||
permission.setRoleId(roleId);
|
||||
permission.setType(type);
|
||||
workflowPermissionRepository.save(permission);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void addDepartmentPermission(Long workflowDefinitionId, Long departmentId, PermissionTypeEnum type) {
|
||||
Optional<WorkflowPermission> existingPermission = workflowPermissionRepository
|
||||
.findByWorkflowDefinitionIdAndDepartmentIdAndType(workflowDefinitionId, departmentId, type);
|
||||
if (existingPermission.isEmpty()) {
|
||||
WorkflowPermission permission = new WorkflowPermission();
|
||||
permission.setWorkflowDefinitionId(workflowDefinitionId);
|
||||
permission.setDepartmentId(departmentId);
|
||||
permission.setType(type);
|
||||
workflowPermissionRepository.save(permission);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void removeUserPermission(Long workflowDefinitionId, Long userId, PermissionTypeEnum type) {
|
||||
workflowPermissionRepository.deleteByWorkflowDefinitionIdAndUserIdAndType(workflowDefinitionId, userId, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void removeRolePermission(Long workflowDefinitionId, Long roleId, PermissionTypeEnum type) {
|
||||
workflowPermissionRepository.deleteByWorkflowDefinitionIdAndRoleIdAndType(workflowDefinitionId, roleId, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void removeDepartmentPermission(Long workflowDefinitionId, Long departmentId, PermissionTypeEnum type) {
|
||||
workflowPermissionRepository.deleteByWorkflowDefinitionIdAndDepartmentIdAndType(workflowDefinitionId, departmentId, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Long workflowDefinitionId, Long userId, PermissionTypeEnum type) {
|
||||
// 检查用户直接权限
|
||||
if (workflowPermissionRepository.existsByWorkflowDefinitionIdAndUserIdAndType(workflowDefinitionId, userId, type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
Optional<User> userOpt = userRepository.findById(userId);
|
||||
if (userOpt.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
User user = userOpt.get();
|
||||
|
||||
// 检查角色权限
|
||||
if (user.getRoleId() != null && workflowPermissionRepository
|
||||
.existsByWorkflowDefinitionIdAndRoleIdAndType(workflowDefinitionId, user.getRoleId(), type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查部门权限
|
||||
return user.getDepartmentId() != null && workflowPermissionRepository
|
||||
.existsByWorkflowDefinitionIdAndDepartmentIdAndType(workflowDefinitionId, user.getDepartmentId(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WorkflowPermission> getPermissions(Long workflowDefinitionId) {
|
||||
return workflowPermissionRepository.findByWorkflowDefinitionId(workflowDefinitionId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<WorkflowPermission> findPermissions(Long workflowDefinitionId, PermissionTypeEnum type, Pageable pageable) {
|
||||
return workflowPermissionRepository.findByWorkflowDefinitionIdAndType(workflowDefinitionId, type, pageable);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
package com.qqchen.deploy.backend.workflow.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowVariable;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowVariableRepository;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowVariableService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 工作流变量服务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WorkflowVariableServiceImpl implements IWorkflowVariableService {
|
||||
|
||||
@Resource
|
||||
private IWorkflowVariableRepository workflowVariableRepository;
|
||||
|
||||
@Resource
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void saveVariables(Long workflowInstanceId, Map<String, Object> variables) {
|
||||
variables.forEach((name, value) -> setVariable(workflowInstanceId, name, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getVariables(Long workflowInstanceId) {
|
||||
List<WorkflowVariable> variables = workflowVariableRepository.findByWorkflowInstanceId(workflowInstanceId);
|
||||
Map<String, Object> result = new HashMap<>(variables.size());
|
||||
variables.forEach(variable -> {
|
||||
try {
|
||||
result.put(variable.getName(), deserializeValue(variable.getValue(), variable.getType()));
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Failed to deserialize variable value: {}", variable.getName(), e);
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_DESERIALIZE_ERROR);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getVariable(Long workflowInstanceId, String name) {
|
||||
return workflowVariableRepository.findByWorkflowInstanceIdAndName(workflowInstanceId, name)
|
||||
.map(variable -> {
|
||||
try {
|
||||
return deserializeValue(variable.getValue(), variable.getType());
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Failed to deserialize variable value: {}", name, e);
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_DESERIALIZE_ERROR);
|
||||
}
|
||||
})
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void setVariable(Long workflowInstanceId, String name, Object value) {
|
||||
try {
|
||||
WorkflowVariable variable = workflowVariableRepository
|
||||
.findByWorkflowInstanceIdAndName(workflowInstanceId, name)
|
||||
.orElseGet(() -> {
|
||||
WorkflowVariable newVariable = new WorkflowVariable();
|
||||
newVariable.setWorkflowInstanceId(workflowInstanceId);
|
||||
newVariable.setName(name);
|
||||
return newVariable;
|
||||
});
|
||||
|
||||
variable.setType(value.getClass().getName());
|
||||
variable.setValue(serializeValue(value));
|
||||
workflowVariableRepository.save(variable);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("Failed to serialize variable value: {}", name, e);
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_SERIALIZE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteVariable(Long workflowInstanceId, String name) {
|
||||
workflowVariableRepository.deleteByWorkflowInstanceIdAndName(workflowInstanceId, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void clearVariables(Long workflowInstanceId) {
|
||||
workflowVariableRepository.deleteByWorkflowInstanceId(workflowInstanceId);
|
||||
}
|
||||
|
||||
private String serializeValue(Object value) throws JsonProcessingException {
|
||||
return objectMapper.writeValueAsString(value);
|
||||
}
|
||||
|
||||
private Object deserializeValue(String value, String type) throws JsonProcessingException {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(type);
|
||||
return objectMapper.readValue(value, clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.error("Failed to find class for type: {}", type, e);
|
||||
throw new BusinessException(ResponseCode.WORKFLOW_VARIABLE_TYPE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -145,7 +145,7 @@ CREATE TABLE sys_role_tag (
|
||||
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
|
||||
|
||||
name VARCHAR(50) NOT NULL COMMENT '标签名称',
|
||||
color VARCHAR(20) NULL COMMENT '标签十六进制颜色码)'
|
||||
color VARCHAR(20) NULL COMMENT '标签<EFBFBD><EFBFBD><EFBFBD>六进制颜色码)'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色标签表';
|
||||
|
||||
-- 角色标签关联表
|
||||
@ -445,4 +445,62 @@ CREATE TABLE sys_node_instance (
|
||||
|
||||
CONSTRAINT fk_node_workflow_instance FOREIGN KEY (workflow_instance_id) REFERENCES sys_workflow_instance(id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='节点实例表';
|
||||
|
||||
-- 工作流变量表
|
||||
CREATE TABLE wf_workflow_variable (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
|
||||
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
|
||||
name VARCHAR(255) NOT NULL COMMENT '变量名',
|
||||
value TEXT COMMENT '变量值',
|
||||
type VARCHAR(255) NOT NULL COMMENT '变量类型',
|
||||
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_instance_id (workflow_instance_id),
|
||||
INDEX idx_name (name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工作流变量';
|
||||
|
||||
-- 工作流日志表
|
||||
CREATE TABLE wf_workflow_log (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
|
||||
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
|
||||
node_instance_id BIGINT COMMENT '节点实例ID',
|
||||
type VARCHAR(50) NOT NULL COMMENT '日志类型',
|
||||
level VARCHAR(20) NOT NULL COMMENT '日志级别',
|
||||
content TEXT NOT NULL COMMENT '日志内容',
|
||||
detail TEXT COMMENT '详细信息',
|
||||
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_instance_id (workflow_instance_id),
|
||||
INDEX idx_node_instance_id (node_instance_id),
|
||||
INDEX idx_type (type),
|
||||
INDEX idx_level (level)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工作流日志';
|
||||
|
||||
-- 工作流权限表
|
||||
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='工作流权限';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user