通用解析
This commit is contained in:
parent
d048858015
commit
205c87dc5a
380
backend/docs/workflow/workflow_instance_sync.md
Normal file
380
backend/docs/workflow/workflow_instance_sync.md
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
# 工作流实例同步机制设计文档
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
1. [整体数据流向](#整体数据流向)
|
||||||
|
2. [Flowable与系统映射关系](#Flowable与系统映射关系)
|
||||||
|
3. [数据同步实现](#数据同步实现)
|
||||||
|
4. [事件监听机制](#事件监听机制)
|
||||||
|
5. [状态管理](#状态管理)
|
||||||
|
6. [最佳实践](#最佳实践)
|
||||||
|
|
||||||
|
## 整体数据流向
|
||||||
|
|
||||||
|
```
|
||||||
|
[前端流程设计器]
|
||||||
|
|
|
||||||
|
| 1. 用户拖拽设计流程
|
||||||
|
| - 选择节点类型(从workflow_node_definition获取可用节点)
|
||||||
|
| - 配置节点属性(根据节点的form_config渲染表单)
|
||||||
|
| - 连接节点、设置条件
|
||||||
|
↓
|
||||||
|
[前端数据模型]
|
||||||
|
| - 图形数据(nodes、edges)
|
||||||
|
| - 表单数据(各节点的配置信息)
|
||||||
|
| - 流程基本信息(名称、描述等)
|
||||||
|
|
|
||||||
|
| 2. 保存流程设计
|
||||||
|
↓
|
||||||
|
[后端 WorkflowDefinitionController]
|
||||||
|
|
|
||||||
|
| 3. 接收流程设计数据
|
||||||
|
↓
|
||||||
|
[后端 WorkflowDefinitionService]
|
||||||
|
|
|
||||||
|
| 4. 处理流程数据
|
||||||
|
| - 保存流程定义基本信息到 workflow_definition
|
||||||
|
| - 转换图形数据为BPMN XML
|
||||||
|
| - 解析节点配置,更新workflow_node_definition
|
||||||
|
| * 对于新的节点类型:创建新记录
|
||||||
|
| * 对于已有节点:更新配置
|
||||||
|
|
|
||||||
|
| 5. 部署到Flowable
|
||||||
|
↓
|
||||||
|
[Flowable引擎]
|
||||||
|
|
|
||||||
|
| 6. 部署流程
|
||||||
|
| - 保存BPMN XML
|
||||||
|
| - 生成流程定义ID
|
||||||
|
|
|
||||||
|
| 7. 启动流程实例
|
||||||
|
↓
|
||||||
|
[后端 WorkflowInstanceService]
|
||||||
|
|
|
||||||
|
| 8. 创建流程实例
|
||||||
|
| - 创建workflow_instance记录
|
||||||
|
| - 关联workflow_definition
|
||||||
|
|
|
||||||
|
| 9. 节点实例化
|
||||||
|
| - 创建workflow_node_instance记录
|
||||||
|
| - 关联workflow_node_definition
|
||||||
|
↓
|
||||||
|
[数据库]
|
||||||
|
|
|
||||||
|
| 实时同步的表:
|
||||||
|
| - workflow_definition(流程定义)
|
||||||
|
| - workflow_node_definition(节点定义)
|
||||||
|
| - workflow_instance(流程实例)
|
||||||
|
| - workflow_node_instance(节点实例)
|
||||||
|
|
|
||||||
|
| Flowable表:
|
||||||
|
| - ACT_RE_*(流程定义相关表)
|
||||||
|
| - ACT_RU_*(运行时数据表)
|
||||||
|
| - ACT_HI_*(历史数据表)
|
||||||
|
↓
|
||||||
|
[前端任务列表/监控页面]
|
||||||
|
|
|
||||||
|
| 10. 展示流程实例
|
||||||
|
| - 查询实例状态
|
||||||
|
| - 显示节点执行情况
|
||||||
|
| - 处理用户任务
|
||||||
|
| - 查看历史记录
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flowable与系统映射关系
|
||||||
|
|
||||||
|
### 表结构映射
|
||||||
|
|
||||||
|
#### Flowable核心表
|
||||||
|
```
|
||||||
|
[流程定义相关]
|
||||||
|
ACT_RE_DEPLOYMENT: 流程部署表
|
||||||
|
ACT_RE_PROCDEF: 流程定义表
|
||||||
|
|
||||||
|
[流程实例相关]
|
||||||
|
ACT_RU_EXECUTION: 运行时流程实例表
|
||||||
|
ACT_RU_TASK: 运行时任务表
|
||||||
|
ACT_RU_VARIABLE: 运行时变量表
|
||||||
|
|
||||||
|
[历史数据]
|
||||||
|
ACT_HI_PROCINST: 历史流程实例表
|
||||||
|
ACT_HI_TASKINST: 历史任务实例表
|
||||||
|
ACT_HI_ACTINST: 历史活动实例表
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 系统表与Flowable映射关系
|
||||||
|
```
|
||||||
|
我们的表 Flowable的表
|
||||||
|
--------------------------------------------------
|
||||||
|
workflow_definition <-> ACT_RE_PROCDEF
|
||||||
|
workflow_instance <-> ACT_RU_EXECUTION/ACT_HI_PROCINST
|
||||||
|
workflow_node_instance <-> ACT_RU_TASK/ACT_HI_TASKINST
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据同步实现
|
||||||
|
|
||||||
|
### 流程实例服务实现
|
||||||
|
```java
|
||||||
|
@Service
|
||||||
|
@Transactional
|
||||||
|
public class WorkflowInstanceService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RuntimeService runtimeService; // Flowable的运行时服务
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TaskService taskService; // Flowable的任务服务
|
||||||
|
|
||||||
|
public WorkflowInstance startProcess(WorkflowInstanceCreateDTO createDTO) {
|
||||||
|
// 1. 启动Flowable流程实例
|
||||||
|
ProcessInstance processInstance = runtimeService.startProcessInstanceById(
|
||||||
|
createDTO.getProcessDefinitionId(),
|
||||||
|
createDTO.getBusinessKey(),
|
||||||
|
createDTO.getVariables()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. 创建我们自己的流程实例记录
|
||||||
|
WorkflowInstance workflowInstance = new WorkflowInstance();
|
||||||
|
workflowInstance.setProcessInstanceId(processInstance.getId());
|
||||||
|
workflowInstance.setProcessDefinitionId(createDTO.getProcessDefinitionId());
|
||||||
|
workflowInstance.setBusinessKey(createDTO.getBusinessKey());
|
||||||
|
workflowInstance.setStatus(WorkflowInstanceStatusEnums.RUNNING);
|
||||||
|
workflowInstance.setVariables(JsonUtils.toJsonString(createDTO.getVariables()));
|
||||||
|
workflowInstance.setStartTime(LocalDateTime.now());
|
||||||
|
workflowInstanceRepository.save(workflowInstance);
|
||||||
|
|
||||||
|
// 3. 获取当前活动的任务
|
||||||
|
List<Task> tasks = taskService.createTaskQuery()
|
||||||
|
.processInstanceId(processInstance.getId())
|
||||||
|
.list();
|
||||||
|
|
||||||
|
// 4. 为每个活动的任务创建节点实例
|
||||||
|
for (Task task : tasks) {
|
||||||
|
WorkflowNodeInstance nodeInstance = new WorkflowNodeInstance();
|
||||||
|
nodeInstance.setWorkflowInstanceId(workflowInstance.getId());
|
||||||
|
nodeInstance.setNodeId(task.getTaskDefinitionKey());
|
||||||
|
nodeInstance.setNodeName(task.getName());
|
||||||
|
nodeInstance.setNodeType("userTask"); // 或者从定义中获取
|
||||||
|
nodeInstance.setStatus("ACTIVE");
|
||||||
|
nodeInstance.setStartTime(LocalDateTime.now());
|
||||||
|
workflowNodeInstanceRepository.save(nodeInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
return workflowInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据同步服务实现
|
||||||
|
```java
|
||||||
|
@Service
|
||||||
|
public class WorkflowSyncService {
|
||||||
|
|
||||||
|
@Scheduled(fixedRate = 60000) // 每分钟执行一次
|
||||||
|
public void syncWorkflowStatus() {
|
||||||
|
// 1. 查找所有运行中的实例
|
||||||
|
List<WorkflowInstance> runningInstances = workflowInstanceRepository
|
||||||
|
.findByStatus(WorkflowInstanceStatusEnums.RUNNING);
|
||||||
|
|
||||||
|
for (WorkflowInstance instance : runningInstances) {
|
||||||
|
// 2. 检查Flowable中的状态
|
||||||
|
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
|
||||||
|
.processInstanceId(instance.getProcessInstanceId())
|
||||||
|
.singleResult();
|
||||||
|
|
||||||
|
if (processInstance == null) {
|
||||||
|
// Flowable中实例已结束,更新我们的状态
|
||||||
|
HistoricProcessInstance historicInstance = historyService
|
||||||
|
.createHistoricProcessInstanceQuery()
|
||||||
|
.processInstanceId(instance.getProcessInstanceId())
|
||||||
|
.singleResult();
|
||||||
|
|
||||||
|
instance.setStatus(convertFlowableStatus(historicInstance.getEndActivityId()));
|
||||||
|
instance.setEndTime(LocalDateTime.now());
|
||||||
|
workflowInstanceRepository.save(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 同步节点实例状态
|
||||||
|
syncNodeInstances(instance.getId(), instance.getProcessInstanceId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syncNodeInstances(Long workflowInstanceId, String processInstanceId) {
|
||||||
|
// 1. 获取当前活动的任务
|
||||||
|
List<Task> activeTasks = taskService.createTaskQuery()
|
||||||
|
.processInstanceId(processInstanceId)
|
||||||
|
.list();
|
||||||
|
|
||||||
|
// 2. 获取历史任务
|
||||||
|
List<HistoricTaskInstance> historicTasks = historyService
|
||||||
|
.createHistoricTaskInstanceQuery()
|
||||||
|
.processInstanceId(processInstanceId)
|
||||||
|
.finished()
|
||||||
|
.list();
|
||||||
|
|
||||||
|
// 3. 更新节点实例状态
|
||||||
|
Set<String> processedTaskIds = new HashSet<>();
|
||||||
|
|
||||||
|
// 处理活动任务
|
||||||
|
for (Task task : activeTasks) {
|
||||||
|
WorkflowNodeInstance nodeInstance = getOrCreateNodeInstance(
|
||||||
|
workflowInstanceId, task.getTaskDefinitionKey());
|
||||||
|
updateNodeInstance(nodeInstance, task, null);
|
||||||
|
processedTaskIds.add(task.getTaskDefinitionKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理历史任务
|
||||||
|
for (HistoricTaskInstance historicTask : historicTasks) {
|
||||||
|
if (!processedTaskIds.contains(historicTask.getTaskDefinitionKey())) {
|
||||||
|
WorkflowNodeInstance nodeInstance = getOrCreateNodeInstance(
|
||||||
|
workflowInstanceId, historicTask.getTaskDefinitionKey());
|
||||||
|
updateNodeInstance(nodeInstance, null, historicTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 事件监听机制
|
||||||
|
|
||||||
|
### Flowable事件监听器
|
||||||
|
```java
|
||||||
|
@Component
|
||||||
|
public class FlowableEventListener implements TaskListener, ExecutionListener {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private WorkflowInstanceService workflowInstanceService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notify(DelegateTask task) {
|
||||||
|
// 任务事件处理
|
||||||
|
String eventName = task.getEventName();
|
||||||
|
switch (eventName) {
|
||||||
|
case "create":
|
||||||
|
workflowInstanceService.onTaskCreate(task);
|
||||||
|
break;
|
||||||
|
case "complete":
|
||||||
|
workflowInstanceService.onTaskComplete(task);
|
||||||
|
break;
|
||||||
|
case "delete":
|
||||||
|
workflowInstanceService.onTaskDelete(task);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void notify(DelegateExecution execution) {
|
||||||
|
// 流程执行事件处理
|
||||||
|
String eventName = execution.getEventName();
|
||||||
|
switch (eventName) {
|
||||||
|
case "start":
|
||||||
|
workflowInstanceService.onProcessStart(execution);
|
||||||
|
break;
|
||||||
|
case "end":
|
||||||
|
workflowInstanceService.onProcessEnd(execution);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 状态管理
|
||||||
|
|
||||||
|
### 工作流实例状态枚举
|
||||||
|
```java
|
||||||
|
public enum WorkflowInstanceStatusEnums {
|
||||||
|
NOT_STARTED, // 未开始
|
||||||
|
RUNNING, // 运行中
|
||||||
|
SUSPENDED, // 已暂停
|
||||||
|
COMPLETED, // 已完成
|
||||||
|
TERMINATED, // 已终止
|
||||||
|
FAILED; // 执行失败
|
||||||
|
|
||||||
|
public static WorkflowInstanceStatusEnums fromFlowableStatus(String flowableStatus) {
|
||||||
|
switch (flowableStatus) {
|
||||||
|
case "active":
|
||||||
|
return RUNNING;
|
||||||
|
case "suspended":
|
||||||
|
return SUSPENDED;
|
||||||
|
case "completed":
|
||||||
|
return COMPLETED;
|
||||||
|
case "terminated":
|
||||||
|
return TERMINATED;
|
||||||
|
default:
|
||||||
|
return FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
### 为什么需要自己维护实例表
|
||||||
|
|
||||||
|
1. **业务扩展性**
|
||||||
|
- 可以添加更多业务相关的字段
|
||||||
|
- 可以实现自定义的状态管理
|
||||||
|
- 可以关联更多业务数据
|
||||||
|
|
||||||
|
2. **性能优化**
|
||||||
|
- 避免频繁查询Flowable表
|
||||||
|
- 可以建立更适合业务查询的索引
|
||||||
|
- 可以实现更好的缓存策略
|
||||||
|
|
||||||
|
3. **数据完整性**
|
||||||
|
- 保存完整的业务上下文
|
||||||
|
- 记录更详细的审计信息
|
||||||
|
- 支持自定义的数据分析
|
||||||
|
|
||||||
|
### 数据一致性保证
|
||||||
|
|
||||||
|
1. **事务管理**
|
||||||
|
```java
|
||||||
|
@Transactional
|
||||||
|
public void startProcess() {
|
||||||
|
// 1. 启动Flowable流程
|
||||||
|
// 2. 创建系统流程实例
|
||||||
|
// 3. 创建节点实例
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **定时同步**
|
||||||
|
- 定期检查运行中的实例状态
|
||||||
|
- 自动修复不一致的数据
|
||||||
|
- 记录同步日志
|
||||||
|
|
||||||
|
3. **事件驱动**
|
||||||
|
- 监听Flowable事件
|
||||||
|
- 实时更新系统状态
|
||||||
|
- 保证数据实时性
|
||||||
|
|
||||||
|
### 性能优化建议
|
||||||
|
|
||||||
|
1. **索引优化**
|
||||||
|
- 为常用查询字段建立索引
|
||||||
|
- 使用复合索引优化多字段查询
|
||||||
|
- 避免过多索引影响写入性能
|
||||||
|
|
||||||
|
2. **缓存策略**
|
||||||
|
- 缓存活动的流程实例
|
||||||
|
- 缓存常用的节点定义
|
||||||
|
- 使用分布式缓存提高性能
|
||||||
|
|
||||||
|
3. **批量处理**
|
||||||
|
- 批量同步数据
|
||||||
|
- 批量更新状态
|
||||||
|
- 使用队列处理异步任务
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
1. Flowable负责流程的实际执行和调度
|
||||||
|
2. 系统维护业务层面的状态和数据
|
||||||
|
3. 通过事件监听和定时同步保证数据一致性
|
||||||
|
4. 使用状态映射处理不同系统间的状态转换
|
||||||
|
5. 通过事务确保关键操作的原子性
|
||||||
|
|
||||||
|
这样的设计可以:
|
||||||
|
- 保持与Flowable的数据同步
|
||||||
|
- 支持业务扩展
|
||||||
|
- 提供更好的性能
|
||||||
|
- 确保数据一致性
|
||||||
|
- 便于问题追踪和修复
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schema属性注解
|
||||||
|
*/
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface SchemaProperty {
|
||||||
|
/**
|
||||||
|
* 字段标题
|
||||||
|
*/
|
||||||
|
String title() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段描述
|
||||||
|
*/
|
||||||
|
String description() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否必填
|
||||||
|
*/
|
||||||
|
boolean required() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段格式
|
||||||
|
*/
|
||||||
|
String format() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认值
|
||||||
|
*/
|
||||||
|
String defaultValue() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小值(用于数值类型)
|
||||||
|
*/
|
||||||
|
int minimum() default Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大值(用于数值类型)
|
||||||
|
*/
|
||||||
|
int maximum() default Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举值(用于字符串类型)
|
||||||
|
*/
|
||||||
|
String[] enumValues() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举值显示名称
|
||||||
|
*/
|
||||||
|
String[] enumNames() default {};
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行人配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AssigneeConfig {
|
||||||
|
/**
|
||||||
|
* 执行人ID
|
||||||
|
*/
|
||||||
|
private String assignee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 候选执行人列表
|
||||||
|
*/
|
||||||
|
private List<String> candidateUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 候选组列表
|
||||||
|
*/
|
||||||
|
private List<String> candidateGroups;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行人表达式
|
||||||
|
*/
|
||||||
|
private String assigneeExpression;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 候选人表达式
|
||||||
|
*/
|
||||||
|
private String candidateExpression;
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.nodeConfig.listener.ListenerConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础节点配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class BaseNodeConfig {
|
||||||
|
/**
|
||||||
|
* 监听器配置列表
|
||||||
|
*/
|
||||||
|
private List<ListenerConfig> listeners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否异步
|
||||||
|
*/
|
||||||
|
private Boolean async;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否独占
|
||||||
|
*/
|
||||||
|
private Boolean exclusive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义属性
|
||||||
|
*/
|
||||||
|
private Map<String, Object> customProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文档说明
|
||||||
|
*/
|
||||||
|
private String documentation;
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束节点配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class EndEventConfig extends EventNodeConfig {
|
||||||
|
/**
|
||||||
|
* 结束类型(normal, terminate, error, etc.)
|
||||||
|
*/
|
||||||
|
private String terminateType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误代码(当terminateType为error时使用)
|
||||||
|
*/
|
||||||
|
private String errorCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误消息(当terminateType为error时使用)
|
||||||
|
*/
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束时的输出变量
|
||||||
|
*/
|
||||||
|
private Map<String, Object> outputVariables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否终止所有活动分支
|
||||||
|
*/
|
||||||
|
private Boolean terminateAll;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件节点(如START、END)的基础配置
|
||||||
|
*/
|
||||||
|
public class EventConfig {
|
||||||
|
@JsonProperty(required = true)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件节点基础配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class EventNodeConfig {
|
||||||
|
/**
|
||||||
|
* 节点名称
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "节点名称",
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点描述
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "节点描述"
|
||||||
|
)
|
||||||
|
private String description;
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class FormConfig {
|
||||||
|
/**
|
||||||
|
* 表单Key
|
||||||
|
*/
|
||||||
|
private String formKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单属性
|
||||||
|
*/
|
||||||
|
private Map<String, Object> properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否必填
|
||||||
|
*/
|
||||||
|
private Boolean required;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单验证规则
|
||||||
|
*/
|
||||||
|
private Map<String, Object> validations;
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Git Clone任务配置
|
||||||
|
* 继承TaskConfig以获取通用的任务配置(优先级、超时、重试等)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class GitCloneConfig extends TaskConfig {
|
||||||
|
/**
|
||||||
|
* 仓库URL
|
||||||
|
*/
|
||||||
|
private String repositoryUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分支或标签
|
||||||
|
*/
|
||||||
|
private String ref;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 目标目录
|
||||||
|
*/
|
||||||
|
private String targetDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否递归克隆子模块
|
||||||
|
*/
|
||||||
|
private Boolean recursive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 克隆深度(0表示完整克隆)
|
||||||
|
*/
|
||||||
|
private Integer depth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 身份验证类型(NONE, SSH_KEY, USERNAME_PASSWORD)
|
||||||
|
*/
|
||||||
|
private String authenticationType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSH私钥(当authenticationType为SSH_KEY时使用)
|
||||||
|
*/
|
||||||
|
private String sshPrivateKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名(当authenticationType为USERNAME_PASSWORD时使用)
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码或令牌(当authenticationType为USERNAME_PASSWORD时使用)
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Git配置
|
||||||
|
*/
|
||||||
|
private Map<String, String> gitConfig;
|
||||||
|
|
||||||
|
public GitCloneConfig() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点类型配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class NodeTypeConfig {
|
||||||
|
/**
|
||||||
|
* 节点类型代码
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "节点类型代码",
|
||||||
|
description = "节点类型的唯一标识",
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点类型名称
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "节点类型名称",
|
||||||
|
description = "节点类型的显示名称",
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点类型描述
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "节点类型描述",
|
||||||
|
description = "节点类型的详细描述"
|
||||||
|
)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点配置Schema
|
||||||
|
*/
|
||||||
|
private JsonNode configSchema;
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脚本执行器配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class ScriptExecutorConfig extends TaskConfig {
|
||||||
|
/**
|
||||||
|
* 脚本内容
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "脚本内容",
|
||||||
|
description = "需要执行的脚本内容,例如:\n#!/bin/bash\necho \"开始执行脚本\"\nls -la\necho \"脚本执行完成\"",
|
||||||
|
format = "textarea",
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
private String script;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脚本语言
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "脚本语言",
|
||||||
|
description = "脚本语言类型",
|
||||||
|
required = true,
|
||||||
|
enumValues = {"shell", "python", "javascript", "groovy"},
|
||||||
|
enumNames = {"Shell脚本 (已支持)", "Python脚本 (开发中)", "JavaScript脚本 (开发中)", "Groovy脚本 (开发中)"},
|
||||||
|
defaultValue = "shell"
|
||||||
|
)
|
||||||
|
private String language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解释器路径
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "解释器路径",
|
||||||
|
description = "脚本解释器的路径,例如:/bin/bash, /usr/bin/python3",
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
private String interpreter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作目录
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "工作目录",
|
||||||
|
description = "脚本执行的工作目录",
|
||||||
|
defaultValue = "/tmp"
|
||||||
|
)
|
||||||
|
private String workingDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 环境变量
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "环境变量",
|
||||||
|
description = "脚本执行时的环境变量"
|
||||||
|
)
|
||||||
|
private Map<String, String> environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功退出码
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "成功退出码",
|
||||||
|
description = "脚本执行成功时的退出码",
|
||||||
|
defaultValue = "0"
|
||||||
|
)
|
||||||
|
private Integer successExitCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的脚本语言列表
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "支持的脚本语言",
|
||||||
|
enumValues = {"shell", "python", "javascript", "groovy"}
|
||||||
|
)
|
||||||
|
private List<String> supportedLanguages;
|
||||||
|
|
||||||
|
public ScriptExecutorConfig() {
|
||||||
|
setTaskType("scriptTask");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始节点配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class StartEventConfig extends EventNodeConfig {
|
||||||
|
/**
|
||||||
|
* 触发器类型
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "触发器类型",
|
||||||
|
description = "工作流的触发方式"
|
||||||
|
)
|
||||||
|
private String triggerType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单Key(当需要启动表单时使用)
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "表单Key",
|
||||||
|
description = "启动表单的标识符"
|
||||||
|
)
|
||||||
|
private String formKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化变量
|
||||||
|
*/
|
||||||
|
private String initiator;
|
||||||
|
}
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.nodeConfig.listener.TaskListenerConfig;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务节点基础配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class TaskConfig extends BaseNodeConfig {
|
||||||
|
/**
|
||||||
|
* 任务类型(如:userTask, serviceTask, scriptTask等)
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "任务类型",
|
||||||
|
description = "工作流节点的任务类型",
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
private String taskType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务名称
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "任务名称",
|
||||||
|
description = "工作流节点的显示名称",
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
private String taskName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务描述
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "任务描述",
|
||||||
|
description = "工作流节点的详细描述"
|
||||||
|
)
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "是否启用",
|
||||||
|
description = "是否启用该任务节点",
|
||||||
|
defaultValue = "true"
|
||||||
|
)
|
||||||
|
private Boolean enabled = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务优先级(0-100)
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "任务优先级",
|
||||||
|
description = "工作流节点的任务优先级",
|
||||||
|
minimum = 0,
|
||||||
|
maximum = 100
|
||||||
|
)
|
||||||
|
private Integer priority;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时时间(秒)
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "超时时间",
|
||||||
|
description = "任务执行的最大时间(秒)",
|
||||||
|
minimum = 1,
|
||||||
|
maximum = 3600,
|
||||||
|
defaultValue = "300"
|
||||||
|
)
|
||||||
|
private Integer timeoutDuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时处理策略
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "超时处理策略",
|
||||||
|
description = "任务超时后的处理策略",
|
||||||
|
enumValues = {"FAIL", "CONTINUE", "RETRY"},
|
||||||
|
enumNames = {"失败", "继续", "重试"},
|
||||||
|
defaultValue = "FAIL"
|
||||||
|
)
|
||||||
|
private String timeoutStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试次数
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "重试次数",
|
||||||
|
description = "任务失败后的重试次数",
|
||||||
|
minimum = 0,
|
||||||
|
maximum = 10,
|
||||||
|
defaultValue = "0"
|
||||||
|
)
|
||||||
|
private Integer retryTimes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试间隔(秒)
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "重试间隔",
|
||||||
|
description = "两次重试之间的等待时间(秒)",
|
||||||
|
minimum = 1,
|
||||||
|
maximum = 3600,
|
||||||
|
defaultValue = "60"
|
||||||
|
)
|
||||||
|
private Integer retryInterval;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试策略
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "重试策略",
|
||||||
|
description = "任务重试的策略",
|
||||||
|
enumValues = {"FIXED", "EXPONENTIAL"},
|
||||||
|
enumNames = {"固定间隔", "指数退避"},
|
||||||
|
defaultValue = "FIXED"
|
||||||
|
)
|
||||||
|
private String retryStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行人配置
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "执行人配置",
|
||||||
|
description = "工作流节点的执行人配置"
|
||||||
|
)
|
||||||
|
private AssigneeConfig assignee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单配置
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "表单配置",
|
||||||
|
description = "工作流节点的表单配置"
|
||||||
|
)
|
||||||
|
private FormConfig form;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务输入变量
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "任务输入变量",
|
||||||
|
description = "工作流节点的任务输入变量"
|
||||||
|
)
|
||||||
|
private Map<String, Object> inputVariables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务输出变量
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "任务输出变量",
|
||||||
|
description = "工作流节点的任务输出变量"
|
||||||
|
)
|
||||||
|
private Map<String, Object> outputVariables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务特定的监听器配置
|
||||||
|
*/
|
||||||
|
@SchemaProperty(
|
||||||
|
title = "任务监听器配置",
|
||||||
|
description = "工作流节点的任务特定监听器配置"
|
||||||
|
)
|
||||||
|
private List<TaskListenerConfig> taskListeners;
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务监听器
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class TaskListener {
|
||||||
|
/**
|
||||||
|
* 监听器类型(如:class, expression, delegateExpression)
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听器实现(类名、表达式或代理表达式)
|
||||||
|
*/
|
||||||
|
private String implementation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件类型(create, assignment, complete, delete)
|
||||||
|
*/
|
||||||
|
private String event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听器字段
|
||||||
|
*/
|
||||||
|
private Map<String, Object> fields;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.listener;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础监听器配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class BaseListener {
|
||||||
|
/**
|
||||||
|
* 监听器类型(class, expression, delegateExpression)
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听器实现(类名、表达式或代理表达式)
|
||||||
|
*/
|
||||||
|
private String implementation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听器字段
|
||||||
|
*/
|
||||||
|
private Map<String, Object> fields;
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.listener;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件监听器配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class EventListener extends BaseListener {
|
||||||
|
/**
|
||||||
|
* 事件类型
|
||||||
|
* start: 事件开始
|
||||||
|
* end: 事件结束
|
||||||
|
*/
|
||||||
|
private String event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否中断事件
|
||||||
|
*/
|
||||||
|
private Boolean cancelActivity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件抛出的变量
|
||||||
|
*/
|
||||||
|
private String eventVariable;
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.listener;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件监听器配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class EventListenerConfig extends ListenerConfig {
|
||||||
|
/**
|
||||||
|
* 事件类型
|
||||||
|
* start: 事件开始
|
||||||
|
* end: 事件结束
|
||||||
|
*/
|
||||||
|
private String event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否中断事件
|
||||||
|
*/
|
||||||
|
private Boolean cancelActivity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 事件抛出的变量
|
||||||
|
*/
|
||||||
|
private String eventVariable;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.listener;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础监听器配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ListenerConfig {
|
||||||
|
/**
|
||||||
|
* 监听器类型(class, expression, delegateExpression)
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听器实现(类名、表达式或代理表达式)
|
||||||
|
*/
|
||||||
|
private String implementation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听器字段
|
||||||
|
*/
|
||||||
|
private Map<String, Object> fields;
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.listener;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务监听器配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class TaskListener extends BaseListener {
|
||||||
|
/**
|
||||||
|
* 事件类型
|
||||||
|
* create: 任务创建
|
||||||
|
* assignment: 任务分配
|
||||||
|
* complete: 任务完成
|
||||||
|
* delete: 任务删除
|
||||||
|
* update: 任务更新
|
||||||
|
* timeout: 任务超时
|
||||||
|
*/
|
||||||
|
private String event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务相关的用户或组
|
||||||
|
*/
|
||||||
|
private String assignee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务优先级
|
||||||
|
*/
|
||||||
|
private Integer priority;
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.listener;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务监听器配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class TaskListenerConfig extends ListenerConfig {
|
||||||
|
/**
|
||||||
|
* 事件类型
|
||||||
|
* create: 任务创建
|
||||||
|
* assignment: 任务分配
|
||||||
|
* complete: 任务完成
|
||||||
|
* delete: 任务删除
|
||||||
|
* update: 任务更新
|
||||||
|
* timeout: 任务超时
|
||||||
|
*/
|
||||||
|
private String event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务相关的用户或组
|
||||||
|
*/
|
||||||
|
private String assignee;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务优先级
|
||||||
|
*/
|
||||||
|
private Integer priority;
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.script;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脚本执行配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ScriptExecutionConfig {
|
||||||
|
/**
|
||||||
|
* 脚本内容
|
||||||
|
*/
|
||||||
|
private String script;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作目录
|
||||||
|
*/
|
||||||
|
private String workingDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 超时时间(秒)
|
||||||
|
*/
|
||||||
|
private Integer timeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 环境变量
|
||||||
|
*/
|
||||||
|
private Map<String, String> environment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功退出码
|
||||||
|
*/
|
||||||
|
private Integer successExitCode;
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.script;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脚本语言配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ScriptLanguageConfig {
|
||||||
|
/**
|
||||||
|
* 脚本语言类型
|
||||||
|
*/
|
||||||
|
private String language;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解释器路径
|
||||||
|
*/
|
||||||
|
private String interpreter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持的脚本语言列表
|
||||||
|
*/
|
||||||
|
private List<String> supportedLanguages;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.dto.nodeConfig.script;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脚本重试配置
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ScriptRetryConfig {
|
||||||
|
/**
|
||||||
|
* 重试次数
|
||||||
|
*/
|
||||||
|
private Integer retryTimes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重试间隔(秒)
|
||||||
|
*/
|
||||||
|
private Integer retryInterval;
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点类型枚举
|
||||||
|
*/
|
||||||
|
public enum NodeTypeEnum {
|
||||||
|
START_EVENT("START", "开始节点", "流程的开始节点"),
|
||||||
|
END_EVENT("END", "结束节点", "流程的结束节点"),
|
||||||
|
TASK("TASK", "任务节点", "通用任务节点"),
|
||||||
|
SCRIPT_TASK("SCRIPT", "脚本执行器", "支持执行多种脚本语言(Shell、Python、JavaScript等)");
|
||||||
|
|
||||||
|
private final String code;
|
||||||
|
private final String name;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
NodeTypeEnum(String code, String name, String description) {
|
||||||
|
this.code = code;
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NodeTypeEnum fromCode(String code) {
|
||||||
|
for (NodeTypeEnum type : values()) {
|
||||||
|
if (type.getCode().equals(code)) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unknown node type code: " + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,292 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.qqchen.deploy.backend.workflow.annotation.SchemaProperty;
|
||||||
|
import com.qqchen.deploy.backend.workflow.dto.nodeConfig.*;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schema生成器工具类
|
||||||
|
*/
|
||||||
|
public class SchemaGenerator {
|
||||||
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
private SchemaGenerator() {
|
||||||
|
// 私有构造函数防止实例化
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成所有节点类型的配置Schema
|
||||||
|
*/
|
||||||
|
public static JsonNode generateAllNodeTypeSchemas() {
|
||||||
|
ObjectNode schemas = mapper.createObjectNode();
|
||||||
|
|
||||||
|
// START节点
|
||||||
|
schemas.set("START", generateNodeTypeSchema("START", "开始", "开始节点", StartEventConfig.class));
|
||||||
|
|
||||||
|
// SCRIPT节点
|
||||||
|
schemas.set("SCRIPT", generateNodeTypeSchema("SCRIPT", "脚本执行", "执行脚本", ScriptExecutorConfig.class));
|
||||||
|
|
||||||
|
// GIT_CLONE节点
|
||||||
|
schemas.set("GIT_CLONE", generateNodeTypeSchema("GIT_CLONE", "Git克隆", "克隆Git仓库到指定目录", GitCloneConfig.class));
|
||||||
|
|
||||||
|
// END节点
|
||||||
|
schemas.set("END", generateNodeTypeSchema("END", "结束", "结束节点", EndEventConfig.class));
|
||||||
|
|
||||||
|
return schemas;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成节点类型的配置Schema
|
||||||
|
*/
|
||||||
|
private static JsonNode generateNodeTypeSchema(String code, String name, String description, Class<?> configClass) {
|
||||||
|
ObjectNode schema = mapper.createObjectNode();
|
||||||
|
schema.put("code", code);
|
||||||
|
schema.put("name", name);
|
||||||
|
schema.put("description", description);
|
||||||
|
schema.set("configSchema", generateNodeConfigSchema(configClass));
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成节点配置的JSON Schema
|
||||||
|
*/
|
||||||
|
public static JsonNode generateNodeConfigSchema(Class<?> configClass) {
|
||||||
|
ObjectNode schema = mapper.createObjectNode();
|
||||||
|
schema.put("type", "object");
|
||||||
|
ObjectNode properties = schema.putObject("properties");
|
||||||
|
List<String> required = new ArrayList<>();
|
||||||
|
|
||||||
|
// 只有TaskConfig的子类才添加任务相关字段
|
||||||
|
if (TaskConfig.class.isAssignableFrom(configClass)) {
|
||||||
|
processTaskConfigFields(properties, required);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理特定配置类的字段
|
||||||
|
for (Field field : configClass.getDeclaredFields()) {
|
||||||
|
SchemaProperty annotation = field.getAnnotation(SchemaProperty.class);
|
||||||
|
if (annotation != null) {
|
||||||
|
ObjectNode property = properties.putObject(field.getName());
|
||||||
|
generateFieldSchema(property, field, annotation);
|
||||||
|
if (annotation.required()) {
|
||||||
|
required.add(field.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理父类的字段(除了TaskConfig)
|
||||||
|
Class<?> superClass = configClass.getSuperclass();
|
||||||
|
while (superClass != null && superClass != TaskConfig.class && superClass != Object.class) {
|
||||||
|
for (Field field : superClass.getDeclaredFields()) {
|
||||||
|
SchemaProperty annotation = field.getAnnotation(SchemaProperty.class);
|
||||||
|
if (annotation != null) {
|
||||||
|
ObjectNode property = properties.putObject(field.getName());
|
||||||
|
generateFieldSchema(property, field, annotation);
|
||||||
|
if (annotation.required()) {
|
||||||
|
required.add(field.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
superClass = superClass.getSuperclass();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!required.isEmpty()) {
|
||||||
|
ArrayNode requiredArray = schema.putArray("required");
|
||||||
|
required.forEach(requiredArray::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理TaskConfig中的基础字段
|
||||||
|
*/
|
||||||
|
private static void processTaskConfigFields(ObjectNode properties, List<String> required) {
|
||||||
|
// 添加name字段
|
||||||
|
ObjectNode nameProperty = properties.putObject("name");
|
||||||
|
nameProperty.put("type", "string");
|
||||||
|
nameProperty.put("title", "节点名称");
|
||||||
|
required.add("name");
|
||||||
|
|
||||||
|
// 添加description字段
|
||||||
|
ObjectNode descProperty = properties.putObject("description");
|
||||||
|
descProperty.put("type", "string");
|
||||||
|
descProperty.put("title", "节点描述");
|
||||||
|
|
||||||
|
// 添加timeout字段
|
||||||
|
ObjectNode timeoutProperty = properties.putObject("timeout");
|
||||||
|
timeoutProperty.put("type", "object");
|
||||||
|
timeoutProperty.put("title", "超时配置");
|
||||||
|
|
||||||
|
ObjectNode timeoutProperties = timeoutProperty.putObject("properties");
|
||||||
|
|
||||||
|
ObjectNode durationProperty = timeoutProperties.putObject("duration");
|
||||||
|
durationProperty.put("type", "integer");
|
||||||
|
durationProperty.put("title", "超时时间");
|
||||||
|
durationProperty.put("description", "任务执行的最大时间(秒)");
|
||||||
|
durationProperty.put("minimum", 1);
|
||||||
|
durationProperty.put("maximum", 3600);
|
||||||
|
durationProperty.put("default", 300);
|
||||||
|
|
||||||
|
ObjectNode strategyProperty = timeoutProperties.putObject("strategy");
|
||||||
|
strategyProperty.put("type", "string");
|
||||||
|
strategyProperty.put("title", "超时处理策略");
|
||||||
|
strategyProperty.put("description", "任务超时后的处理策略");
|
||||||
|
ArrayNode strategyEnum = strategyProperty.putArray("enum");
|
||||||
|
strategyEnum.add("FAIL").add("CONTINUE").add("RETRY");
|
||||||
|
ArrayNode strategyEnumNames = strategyProperty.putArray("enumNames");
|
||||||
|
strategyEnumNames.add("失败").add("继续").add("重试");
|
||||||
|
strategyProperty.put("default", "FAIL");
|
||||||
|
|
||||||
|
// 添加retry字段
|
||||||
|
ObjectNode retryProperty = properties.putObject("retry");
|
||||||
|
retryProperty.put("type", "object");
|
||||||
|
retryProperty.put("title", "重试配置");
|
||||||
|
|
||||||
|
ObjectNode retryProperties = retryProperty.putObject("properties");
|
||||||
|
|
||||||
|
ObjectNode timesProperty = retryProperties.putObject("times");
|
||||||
|
timesProperty.put("type", "integer");
|
||||||
|
timesProperty.put("title", "重试次数");
|
||||||
|
timesProperty.put("description", "任务失败后的重试次数");
|
||||||
|
timesProperty.put("minimum", 0);
|
||||||
|
timesProperty.put("maximum", 10);
|
||||||
|
timesProperty.put("default", 0);
|
||||||
|
|
||||||
|
ObjectNode intervalProperty = retryProperties.putObject("interval");
|
||||||
|
intervalProperty.put("type", "integer");
|
||||||
|
intervalProperty.put("title", "重试间隔");
|
||||||
|
intervalProperty.put("description", "两次重试之间的等待时间(秒)");
|
||||||
|
intervalProperty.put("minimum", 1);
|
||||||
|
intervalProperty.put("maximum", 3600);
|
||||||
|
intervalProperty.put("default", 60);
|
||||||
|
|
||||||
|
ObjectNode retryStrategyProperty = retryProperties.putObject("strategy");
|
||||||
|
retryStrategyProperty.put("type", "string");
|
||||||
|
retryStrategyProperty.put("title", "重试策略");
|
||||||
|
retryStrategyProperty.put("description", "任务重试的策略");
|
||||||
|
ArrayNode retryStrategyEnum = retryStrategyProperty.putArray("enum");
|
||||||
|
retryStrategyEnum.add("FIXED").add("EXPONENTIAL");
|
||||||
|
ArrayNode retryStrategyEnumNames = retryStrategyProperty.putArray("enumNames");
|
||||||
|
retryStrategyEnumNames.add("固定间隔").add("指数退避");
|
||||||
|
retryStrategyProperty.put("default", "FIXED");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成字段的Schema
|
||||||
|
*/
|
||||||
|
private static void generateFieldSchema(ObjectNode property, Field field, SchemaProperty annotation) {
|
||||||
|
// 设置基本属性
|
||||||
|
if (!annotation.title().isEmpty()) {
|
||||||
|
property.put("title", annotation.title());
|
||||||
|
}
|
||||||
|
if (!annotation.description().isEmpty()) {
|
||||||
|
property.put("description", annotation.description());
|
||||||
|
}
|
||||||
|
if (!annotation.format().isEmpty()) {
|
||||||
|
property.put("format", annotation.format());
|
||||||
|
}
|
||||||
|
if (!annotation.defaultValue().isEmpty()) {
|
||||||
|
setDefaultValue(property, field, annotation.defaultValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置字段类型
|
||||||
|
setFieldType(property, field);
|
||||||
|
|
||||||
|
// 设置数值范围
|
||||||
|
if (isNumericType(field.getType()) &&
|
||||||
|
(annotation.minimum() != Integer.MIN_VALUE || annotation.maximum() != Integer.MAX_VALUE)) {
|
||||||
|
if (annotation.minimum() != Integer.MIN_VALUE) {
|
||||||
|
property.put("minimum", annotation.minimum());
|
||||||
|
}
|
||||||
|
if (annotation.maximum() != Integer.MAX_VALUE) {
|
||||||
|
property.put("maximum", annotation.maximum());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置枚举值
|
||||||
|
if (annotation.enumValues().length > 0) {
|
||||||
|
ArrayNode enumNode = property.putArray("enum");
|
||||||
|
Arrays.stream(annotation.enumValues()).forEach(enumNode::add);
|
||||||
|
|
||||||
|
if (annotation.enumNames().length > 0) {
|
||||||
|
ArrayNode enumNamesNode = property.putArray("enumNames");
|
||||||
|
Arrays.stream(annotation.enumNames()).forEach(enumNamesNode::add);
|
||||||
|
|
||||||
|
// 生成oneOf结构
|
||||||
|
ArrayNode oneOf = property.putArray("oneOf");
|
||||||
|
for (int i = 0; i < annotation.enumValues().length; i++) {
|
||||||
|
ObjectNode oneOfItem = oneOf.addObject();
|
||||||
|
oneOfItem.put("const", annotation.enumValues()[i]);
|
||||||
|
oneOfItem.put("title", annotation.enumNames()[i]);
|
||||||
|
|
||||||
|
// 对于非shell的选项,设置为只读
|
||||||
|
if (!"shell".equals(annotation.enumValues()[i])) {
|
||||||
|
oneOfItem.put("readOnly", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字段类型
|
||||||
|
*/
|
||||||
|
private static void setFieldType(ObjectNode property, Field field) {
|
||||||
|
Class<?> type = field.getType();
|
||||||
|
if (String.class.isAssignableFrom(type)) {
|
||||||
|
property.put("type", "string");
|
||||||
|
} else if (Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) {
|
||||||
|
property.put("type", "integer");
|
||||||
|
} else if (Boolean.class.isAssignableFrom(type) || boolean.class.isAssignableFrom(type)) {
|
||||||
|
property.put("type", "boolean");
|
||||||
|
} else if (List.class.isAssignableFrom(type)) {
|
||||||
|
property.put("type", "array");
|
||||||
|
// 如果需要,可以在这里添加items的schema
|
||||||
|
} else if (Map.class.isAssignableFrom(type)) {
|
||||||
|
property.put("type", "object");
|
||||||
|
ObjectNode additionalProperties = property.putObject("additionalProperties");
|
||||||
|
additionalProperties.put("type", "string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置默认值
|
||||||
|
*/
|
||||||
|
private static void setDefaultValue(ObjectNode property, Field field, String defaultValue) {
|
||||||
|
Class<?> type = field.getType();
|
||||||
|
if (String.class.isAssignableFrom(type)) {
|
||||||
|
property.put("default", defaultValue);
|
||||||
|
} else if (Integer.class.isAssignableFrom(type) || int.class.isAssignableFrom(type)) {
|
||||||
|
try {
|
||||||
|
property.put("default", Integer.parseInt(defaultValue));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 忽略无效的默认值
|
||||||
|
}
|
||||||
|
} else if (Boolean.class.isAssignableFrom(type) || boolean.class.isAssignableFrom(type)) {
|
||||||
|
property.put("default", Boolean.parseBoolean(defaultValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为数值类型
|
||||||
|
*/
|
||||||
|
private static boolean isNumericType(Class<?> type) {
|
||||||
|
return Integer.class.isAssignableFrom(type) ||
|
||||||
|
int.class.isAssignableFrom(type) ||
|
||||||
|
Long.class.isAssignableFrom(type) ||
|
||||||
|
long.class.isAssignableFrom(type) ||
|
||||||
|
Double.class.isAssignableFrom(type) ||
|
||||||
|
double.class.isAssignableFrom(type) ||
|
||||||
|
Float.class.isAssignableFrom(type) ||
|
||||||
|
float.class.isAssignableFrom(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schema生成器的主类,用于测试和生成Schema
|
||||||
|
*/
|
||||||
|
public class SchemaGeneratorMain {
|
||||||
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
static {
|
||||||
|
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SchemaGeneratorMain() {
|
||||||
|
// 私有构造函数防止实例化
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
// 生成所有节点类型的Schema
|
||||||
|
System.out.println("=== Node Type Schemas ===");
|
||||||
|
JsonNode schemas = SchemaGenerator.generateAllNodeTypeSchemas();
|
||||||
|
System.out.println(mapper.writeValueAsString(schemas));
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("生成Schema时发生错误:");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package com.qqchen.deploy.backend.workflow.util;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class SchemaGeneratorTest {
|
||||||
|
private final SchemaGenerator generator = new SchemaGenerator();
|
||||||
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGenerateNodeTypeSchema() throws Exception {
|
||||||
|
JsonNode schema = generator.generateNodeTypeSchema();
|
||||||
|
|
||||||
|
// 将生成的schema打印出来以便查看
|
||||||
|
String prettySchema = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema);
|
||||||
|
System.out.println(prettySchema);
|
||||||
|
|
||||||
|
// 基本验证
|
||||||
|
assertTrue(schema.isArray());
|
||||||
|
assertEquals(1, schema.size());
|
||||||
|
|
||||||
|
JsonNode firstNode = schema.get(0);
|
||||||
|
assertEquals("SCRIPT", firstNode.get("code").asText());
|
||||||
|
assertEquals("脚本执行器", firstNode.get("name").asText());
|
||||||
|
|
||||||
|
JsonNode configSchema = firstNode.get("configSchema");
|
||||||
|
assertTrue(configSchema.has("properties"));
|
||||||
|
assertTrue(configSchema.has("required"));
|
||||||
|
|
||||||
|
// 验证必填字段
|
||||||
|
JsonNode required = configSchema.get("required");
|
||||||
|
assertTrue(required.isArray());
|
||||||
|
assertTrue(required.toString().contains("script"));
|
||||||
|
assertTrue(required.toString().contains("language"));
|
||||||
|
assertTrue(required.toString().contains("interpreter"));
|
||||||
|
|
||||||
|
// 验证属性
|
||||||
|
JsonNode properties = configSchema.get("properties");
|
||||||
|
assertTrue(properties.has("script"));
|
||||||
|
assertTrue(properties.has("language"));
|
||||||
|
assertTrue(properties.has("interpreter"));
|
||||||
|
assertTrue(properties.has("workingDirectory"));
|
||||||
|
assertTrue(properties.has("timeout"));
|
||||||
|
assertTrue(properties.has("retryTimes"));
|
||||||
|
assertTrue(properties.has("retryInterval"));
|
||||||
|
assertTrue(properties.has("environment"));
|
||||||
|
assertTrue(properties.has("successExitCode"));
|
||||||
|
assertTrue(properties.has("supportedLanguages"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user