可正常启动

This commit is contained in:
dengqichen 2024-12-03 18:16:16 +08:00
parent dbca5804ff
commit 4cab885098
44 changed files with 2222 additions and 473 deletions

View File

@ -1,6 +1,7 @@
- 作为Java编程、Spring Boot、Spring Framework、Maven、JUnit和相关Java技术专家需要实现企业应用级别的高性能管理框架
### 严格遵循要求
- 不要随意删除代码,请先详细浏览下现在所有的代码,再进行询问修改,得到确认再修改。
- 首先一步一步思考,详细描述伪代码构建计划,确认后再写代码
- 遵循正确、最佳实践、DRY原则、无错误、功能齐全的代码编写原则
- 专注于简单易读的代码实现,确保代码完整性
@ -12,7 +13,8 @@
### 包结构规范
- 框架包路径(com.qqchen.deploy.backend.framework)包含annotation、api、audit、controller等多个子包
- 业务包路径(com.qqchen.deploy.backend)包含api、controller、converter、entity等多个子包
- 系统业务路径(com.qqchen.deploy.backend.system)包含api、controller、converter、entity等多个子包
- 工作流业务路径(com.qqchen.deploy.backend.workflow)包含api、controller、converter、entity等多个子包
### 数据对象规范
- DTO设计简单CRUD使用统一DTO复杂场景使用专门Request/Response继承BaseDTO获取基础字段
@ -23,6 +25,7 @@
- 简单CRUD继承BaseServiceImpl复杂业务需定义专门接口和实现包含事务控制和异常处理
- 使用@Transactional注解控制事务合理设置事务传播机制和隔离级别
- 实现乐观锁(@Version)或悲观锁(findByIdWithLock)进行并发控制
- 使用@Resource 注入其他BEAN不要使用构造方法
- 示例
@Slf4j
@Service
@ -41,13 +44,22 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
- REST接口使用BaseController三方接口命名为模块名ApiController二方接口为模块名Controller
- 返回值com.qqchen.deploy.backend.framework.api.Response<T>
- 统一使用GlobalExceptionHandler处理异常
- 使用@Resource 注入其他BEAN不要使用构造方法
- 示例:
@Slf4j
@RestController
@RequestMapping("/api/v1/external-system")
@Tag(name = "外部系统管理", description = "外部系统管理相关接口")
public class ExternalSystemApiController extends BaseController<ExternalSystem, ExternalSystemDTO, Long, ExternalSystemQuery> {
// 特定业务方法实现
//特殊方法实现其他的均可使用父类BaseController
@Operation(summary = "测试连接")
@GetMapping("/{id}/test-connection")
public Response<Boolean> testConnection(
@Parameter(description = "系统ID", required = true) @PathVariable Long id
) {
return Response.success(externalSystemService.testConnection(id));
}
}
### Repository层规范
@ -77,6 +89,37 @@ public interface IExternalSystemRepository extends IBaseRepository<ExternalSyste
UserDTO toDto(User entity);
}
```
### 查询规范
- 简单查询使用BaseQuery
- 复杂查询需要:
继承BaseQuery,使用@QueryField注解标注查询字段指定查询类型
- 示例:
```java
@Data
@EqualsAndHashCode(callSuper = true)
public class ExternalSystemQuery extends BaseQuery {
@QueryField(field = "name", type = QueryType.LIKE)
private String name;
@QueryField(field = "type")
private SystemType type;
}
```
### Entity规范
- 示例:
```java
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "sys_external_system")
@LogicDelete
public class ExternalSystem extends Entity<Long> {
@Column(nullable = false)
private String name;
}
```
### 枚举规范
- 新增加枚举应添加到com.qqchen.deploy.backend.enums后缀以Enum结尾
- 增加枚举时,不要修改现有没有,只追加即可
@ -87,7 +130,7 @@ public interface IExternalSystemRepository extends IBaseRepository<ExternalSyste
throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED);
### 命名规范
- 类名使用PascalCaseUserController、OrderService
- 类名使用PascalCaseUserApiController、OrderApiService
- 方法和变量名使用camelCasefindUserById、isOrderValid
- 常量使用ALL_CAPSMAX_RETRY_ATTEMPTS、DEFAULT_PAGE_SIZE
- Service、Repository接口类以I开头实现类需要Impl结尾
@ -101,6 +144,7 @@ throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED);
- 系统级错误(1xxx)1000-1099通用系统错误1100-1199依赖注入错误1200-1299数据库错误
- 业务级错误(2xxx)2000-2099通用业务错误2100-2199角色相关2200-2299 JWT相关等
- 错误码命名使用大写字母和下划线,采用"模块_操作_错误"命名方式
- 示例throw new BusinessException(ResponseCode.ROLE_ADMIN_CANNOT_UPDATE);
### 缓存使用规范
- 使用@Cacheable(查询)、@CachePut(更新)、@CacheEvict(删除)注解

View File

@ -1,7 +1,8 @@
您是Java编程、Spring Boot、Spring Framework、Maven、JUnit和相关Java技术的专家你深思熟虑给出细致入微的答案并且善于推理。你细心地提供准确、真实、周到的答案是一个推理天才。
您是一名Java架构师、Spring Boot、Spring Framework、Maven、JUnit和相关Java技术的专家你深思熟虑给出细致入微的答案并且善于推理。你细心地提供准确、真实、周到的答案是一个推理天才。
需要实现的是企业应用级别的管理框架,需要具有高性能。
### 严格遵循的要求
- 不要随意删除代码,请先详细浏览下现在所有的代码,再进行询问修改,得到确认再修改。重点切记
- 首先,一步一步地思考——详细描述你在伪代码中构建什么的计划。
- 确认,然后写代码!
- 始终编写正确、最佳实践、DRY原则不要重复自己、无错误、功能齐全且可工作的代码还应与下面代码实施指南中列出的规则保持一致。
@ -36,18 +37,30 @@
com.qqchen.deploy.backend.framework.security JWT
com.qqchen.deploy.backend.framework.service IBaseService
com.qqchen.deploy.backend.framework.service.impl BaseServiceImpl
- 业务包路径
com.qqchen.deploy.backend.api 三方接口
com.qqchen.deploy.backend.controller 二方接口
com.qqchen.deploy.backend.converter 转换器
com.qqchen.deploy.backend.entity 数据库实体类
com.qqchen.deploy.backend.integration 第三方系统对接
com.qqchen.deploy.backend.enums 实体类枚举、业务枚举统一卸载这个包下
com.qqchen.deploy.backend.model
com.qqchen.deploy.backend.model.dto 存放所有DTO对象
com.qqchen.deploy.backend.model.query 配套page、list接口使用
com.qqchen.deploy.backend.model.request 接口入参(复杂业务场景使用)
com.qqchen.deploy.backend.model.response 接口出参(复杂业务场景使用)
- 系统业务路径
com.qqchen.deploy.backend.system.api 三方接口
com.qqchen.deploy.backend.system.controller 二方接口
com.qqchen.deploy.backend.system.converter 转换器
com.qqchen.deploy.backend.system.entity 数据库实体类
com.qqchen.deploy.backend.system.integration 第三方系统对接
com.qqchen.deploy.backend.system.enums 实体类枚举、业务枚举统一卸载这个包下
com.qqchen.deploy.backend.system.model
com.qqchen.deploy.backend.system.model.dto 存放所有DTO对象
com.qqchen.deploy.backend.system.model.query 配套page、list接口使用
com.qqchen.deploy.backend.system.model.request 接口入参(复杂业务场景使用)
com.qqchen.deploy.backend.system.model.response 接口出参(复杂业务场景使用)
- WorkFlow业务路径
com.qqchen.deploy.backend.workflow.api 三方接口
com.qqchen.deploy.backend.workflow.controller 二方接口
com.qqchen.deploy.backend.workflow.converter 转换器
com.qqchen.deploy.backend.workflow.entity 数据库实体类
com.qqchen.deploy.backend.workflow.integration 第三方系统对接
com.qqchen.deploy.backend.workflow.enums 实体类枚举、业务枚举统一卸载这个包下
com.qqchen.deploy.backend.workflow.model
com.qqchen.deploy.backend.workflow.model.dto 存放所有DTO对象
com.qqchen.deploy.backend.workflow.model.query 配套page、list接口使用
com.qqchen.deploy.backend.workflow.model.request 接口入参(复杂业务场景使用)
com.qqchen.deploy.backend.workflow.model.response 接口出参(复杂业务场景使用)
### DTO设计规范
- 简单CRUD场景使用统一的DTO无需额外的Request/Response对象

View File

@ -714,489 +714,174 @@ workflow:
- 接口监控
- 业务监控
# 系统优化实现
## 六、系统优化增强
## 一、工作流引擎优化
### 1.1 增强的工作流引擎接口
### 6.1 分布式工作流支持
```java
/**
* 增强的工作流引擎接口
* 分布式工作流引擎增强
*/
public interface WorkflowEngine {
// 基础功能
WorkflowInstance startWorkflow(String code, Map<String, Object> params);
void pauseWorkflow(Long instanceId);
void resumeWorkflow(Long instanceId);
void terminateWorkflow(Long instanceId, String reason);
public interface DistributedWorkflowEngine extends WorkflowEngine {
// 分布式锁支持
void acquireWorkflowLock(String lockKey, Duration timeout);
void releaseWorkflowLock(String lockKey);
// 批量操作支持
List<WorkflowInstance> batchStartWorkflow(List<WorkflowStartRequest> requests);
// 集群协调
void registerWorker(WorkerInfo workerInfo);
void updateWorkerStatus(String workerId, WorkerStatus status);
// 定时执行支持
WorkflowInstance scheduleWorkflow(String code, Map<String, Object> params, LocalDateTime executeTime);
// 任务分发
void assignTask(String workerId, WorkflowTask task);
void reassignTask(String fromWorkerId, String toWorkerId, WorkflowTask task);
// 条件触发支持
void registerTrigger(WorkflowTrigger trigger);
// 子流程支持
WorkflowInstance startSubWorkflow(Long parentInstanceId, String code, Map<String, Object> params);
}
/**
* 工作流触发器
*/
public interface WorkflowTrigger {
String getCode();
boolean shouldTrigger(TriggerContext context);
Map<String, Object> prepareParams(TriggerContext context);
// 故障转移
void handleWorkerFailure(String workerId);
void recoverTasks(String failedWorkerId);
}
```
### 1.2 工作流执行优化
### 6.2 工作流版本控制
```java
/**
* 优化的工作流执行器
* 工作流版本管理
*/
public interface WorkflowVersionManager {
// 版本控制
String createVersion(WorkflowDefinition definition);
WorkflowDefinition getVersion(String versionId);
List<WorkflowVersion> getVersionHistory(String code);
// 版本迁移
void migrateInstances(String fromVersion, String toVersion);
void rollbackVersion(String versionId);
// 版本比较
WorkflowDiff compareVersions(String version1, String version2);
}
```
### 6.3 动态节点支持
```java
/**
* 动态节点管理器
*/
public interface DynamicNodeManager {
// 动态节点注册
void registerNodeType(String type, NodeExecutor executor);
void unregisterNodeType(String type);
// 动态配置
void updateNodeConfig(String type, NodeConfig config);
NodeConfig getNodeConfig(String type);
// 节点发现
List<NodeTypeInfo> discoverNodes();
boolean isNodeTypeAvailable(String type);
}
```
### 6.4 工作流监控增强
```java
/**
* 增强的工作流监控
*/
@Component
@Slf4j
public class EnhancedWorkflowMonitor extends PerformanceMonitor {
// 业务监控指标
private final Counter businessErrorCounter;
private final Counter approvalTimeoutCounter;
private final Timer approvalDurationTimer;
// 资源监控
private final Gauge databaseConnectionGauge;
private final Gauge cacheUsageGauge;
private final Gauge messageQueueDepthGauge;
// SLA监控
private final Timer workflowSLATimer;
private final Counter slaViolationCounter;
public void recordBusinessError(String errorType, String errorCode) {
businessErrorCounter.increment();
// 记录详细错误信息
}
public void checkSLA(WorkflowInstance instance) {
Duration duration = Duration.between(instance.getStartTime(), LocalDateTime.now());
workflowSLATimer.record(duration);
if (duration.compareTo(instance.getSLADuration()) > 0) {
slaViolationCounter.increment();
// 触发SLA违规告警
}
}
}
```
### 6.5 工作流数据分析
```java
/**
* 工作流分析服务
*/
@Service
@Slf4j
public class OptimizedWorkflowExecutor {
private final ExecutorService executorService;
private final WorkflowEventBus eventBus;
private final RetryTemplate retryTemplate;
@Async
public void executeWorkflow(WorkflowInstance instance) {
MDC.put("workflowInstanceId", instance.getId().toString());
try {
// 1. 前置处理
preProcess(instance);
// 2. 并行执行支持
if (instance.isParallelExecutionEnabled()) {
executeParallel(instance);
} else {
executeSequential(instance);
}
// 3. 后置处理
postProcess(instance);
} catch (Exception e) {
handleError(instance, e);
} finally {
MDC.remove("workflowInstanceId");
}
}
private void executeParallel(WorkflowInstance instance) {
List<NodeDefinition> parallelNodes = instance.getParallelNodes();
List<Future<NodeExecuteResult>> futures = new ArrayList<>();
// 提交并行任务
for (NodeDefinition node : parallelNodes) {
futures.add(executorService.submit(() -> executeNode(instance, node)));
}
// 等待所有任务完成
for (Future<NodeExecuteResult> future : futures) {
try {
future.get(instance.getTimeout(), TimeUnit.SECONDS);
} catch (Exception e) {
handleParallelExecutionError(instance, e);
}
}
}
private NodeExecuteResult executeNode(WorkflowInstance instance, NodeDefinition node) {
return retryTemplate.execute(context -> {
NodeExecutor executor = getExecutor(node);
return executor.execute(createContext(instance, node));
});
}
}
```
## 二、节点执行优化
### 2.1 增强的节点上下文
```java
/**
* 增强的节点上下文
*/
public interface NodeContext {
// 基础功能
String getNodeId();
<T> T getNodeConfig(Class<T> clazz);
Map<String, Object> getVariables();
NodeLogger getLogger();
// 数据传递
void setVariable(String key, Object value);
void setTransientVariable(String key, Object value);
// 条件判断
boolean evaluateCondition(String expression);
// 重试策略
RetryStrategy getRetryStrategy();
// 超时控制
Duration getTimeout();
// 回滚操作
void registerRollback(Runnable rollback);
}
/**
* 重试策略
*/
@Data
@Builder
public class RetryStrategy {
private int maxAttempts;
private Duration initialDelay;
private Duration maxDelay;
private double multiplier;
private List<Class<? extends Exception>> retryableExceptions;
}
```
### 2.2 节点执行器增强
```java
/**
* 增强的节点执行器
*/
public interface NodeExecutor {
String getType();
NodeExecuteResult execute(NodeContext context);
void cancel(NodeContext context);
// 新增方法
boolean canExecute(NodeContext context);
void validate(NodeContext context);
void prepare(NodeContext context);
void cleanup(NodeContext context);
}
/**
* 节点执行结果
*/
@Data
@Builder
public class NodeExecuteResult {
private boolean success;
private Map<String, Object> output;
private String errorMessage;
private Throwable error;
private long executionTime;
private Map<String, Object> metrics;
}
```
## 三、事件系统优化
### 3.1 事件总线实现
```java
/**
* 事件总线
*/
@Component
@Slf4j
public class WorkflowEventBus {
private final Map<String, List<WorkflowEventListener>> listeners = new ConcurrentHashMap<>();
private final ExecutorService executorService;
public void publish(WorkflowEvent event) {
String eventType = event.getType();
List<WorkflowEventListener> eventListeners = listeners.get(eventType);
if (eventListeners != null) {
// 异步处理事件
for (WorkflowEventListener listener : eventListeners) {
executorService.submit(() -> {
try {
listener.onEvent(event);
} catch (Exception e) {
log.error("Handle event failed", e);
}
});
}
}
}
public void subscribe(String eventType, WorkflowEventListener listener) {
listeners.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
.add(listener);
}
public void unsubscribe(String eventType, WorkflowEventListener listener) {
List<WorkflowEventListener> eventListeners = listeners.get(eventType);
if (eventListeners != null) {
eventListeners.remove(listener);
}
}
}
```
### 3.2 事件处理器
```java
/**
* 日志事件处理器
*/
@Component
@Slf4j
public class LoggingEventHandler implements WorkflowEventListener {
@Override
public void onEvent(WorkflowEvent event) {
switch (event.getType()) {
case "WORKFLOW_START":
logWorkflowStart(event);
break;
case "WORKFLOW_END":
logWorkflowEnd(event);
break;
case "NODE_START":
logNodeStart(event);
break;
case "NODE_END":
logNodeEnd(event);
break;
default:
log.debug("Unhandled event type: {}", event.getType());
}
}
private void logWorkflowStart(WorkflowEvent event) {
WorkflowInstance instance = (WorkflowInstance) event.getPayload().get("instance");
log.info("Workflow started: {}, type: {}", instance.getId(), instance.getType());
}
}
/**
* 指标收集处理器
*/
@Component
@Slf4j
public class MetricsEventHandler implements WorkflowEventListener {
private final MeterRegistry registry;
@Override
public void onEvent(WorkflowEvent event) {
switch (event.getType()) {
case "WORKFLOW_END":
recordWorkflowMetrics(event);
break;
case "NODE_END":
recordNodeMetrics(event);
break;
}
}
private void recordWorkflowMetrics(WorkflowEvent event) {
WorkflowInstance instance = (WorkflowInstance) event.getPayload().get("instance");
Timer.builder("workflow.execution")
.tag("type", instance.getType())
.tag("status", instance.getStatus().name())
.register(registry)
.record(Duration.between(instance.getStartTime(), instance.getEndTime()));
}
}
```
## 四、插件系统优化
### 4.1 插件生命周期
```java
/**
* 插件生命周期管理
*/
public interface PluginLifecycle {
void onLoad(PluginContext context);
void onEnable();
void onDisable();
void onUnload();
}
/**
* 插件热更新支持
*/
@Component
@Slf4j
public class PluginHotLoader {
private final PluginLoader pluginLoader;
private final FileWatcher fileWatcher;
public void watchPluginDirectory(Path directory) {
fileWatcher.watch(directory, event -> {
if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
String jarPath = event.context().toString();
reloadPlugin(jarPath);
}
});
}
private void reloadPlugin(String jarPath) {
try {
// 1. 卸载旧插件
pluginLoader.unloadPlugin(jarPath);
// 2. 加载新插件
pluginLoader.loadPlugin(jarPath);
log.info("Plugin reloaded: {}", jarPath);
} catch (Exception e) {
log.error("Reload plugin failed: {}", jarPath, e);
}
}
}
```
### 4.2 插件配置
```java
/**
* 插件配置管理
*/
@Component
@Slf4j
public class PluginConfigManager {
private final Map<String, PluginConfig> configs = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
// 加载插件配置
loadConfigs();
// 监听配置变更
watchConfigChanges();
}
public void updateConfig(String pluginId, PluginConfig config) {
configs.put(pluginId, config);
// 通知插件配置更新
notifyConfigUpdate(pluginId, config);
}
private void notifyConfigUpdate(String pluginId, PluginConfig config) {
Plugin plugin = pluginLoader.getPlugin(pluginId);
if (plugin instanceof ConfigurablePlugin) {
((ConfigurablePlugin) plugin).onConfigUpdate(config);
}
}
}
```
## 五、监控增强
### 5.1 性能监控
```java
/**
* 性能监控
*/
@Component
@Slf4j
public class PerformanceMonitor {
private final MeterRegistry registry;
// 节点执行时间分布
private final Timer nodeExecutionTimer;
// 工作流执行时间分布
private final Timer workflowExecutionTimer;
// 资源使用情况
private final Gauge threadPoolActiveThreads;
private final Gauge threadPoolQueueSize;
// 业务指标
private final Counter workflowSuccessCounter;
private final Counter workflowFailureCounter;
private final Counter nodeFailureCounter;
public void recordNodeExecution(String nodeType, long duration) {
nodeExecutionTimer.record(duration, TimeUnit.MILLISECONDS,
Tags.of("nodeType", nodeType));
}
public void recordWorkflowExecution(String workflowType, long duration) {
workflowExecutionTimer.record(duration, TimeUnit.MILLISECONDS,
Tags.of("workflowType", workflowType));
}
}
```
### 5.2 健康检查
```java
/**
* 健康检查
*/
@Component
public class WorkflowHealthIndicator implements HealthIndicator {
public class WorkflowAnalyticsService {
private final WorkflowInstanceRepository instanceRepository;
private final NodeExecutorRegistry executorRegistry;
private final NodeInstanceRepository nodeRepository;
private final AnalyticsEngine analyticsEngine;
@Override
public Health health() {
try {
// 检查运行中的实例数
long runningInstances = instanceRepository.countByStatus(WorkflowStatus.RUNNING);
// 检查执行器状态
Map<String, Boolean> executorStatus = checkExecutors();
return Health.up()
.withDetail("runningInstances", runningInstances)
.withDetail("executorStatus", executorStatus)
.build();
} catch (Exception e) {
return Health.down()
.withException(e)
.build();
}
// 性能分析
public PerformanceReport analyzePerformance(String workflowType, DateRange range) {
// 分析工作流执行性能
return analyticsEngine.analyzePerformance(workflowType, range);
}
private Map<String, Boolean> checkExecutors() {
Map<String, Boolean> status = new HashMap<>();
for (NodeExecutor executor : executorRegistry.getExecutors()) {
status.put(executor.getType(), checkExecutor(executor));
}
return status;
// 瓶颈分析
public List<BottleneckNode> findBottlenecks(String workflowType) {
// 识别执行瓶颈
return analyticsEngine.findBottlenecks(workflowType);
}
// 趋势分析
public TrendReport analyzeTrend(String metric, DateRange range) {
// 分析指标趋势
return analyticsEngine.analyzeTrend(metric, range);
}
}
```
### 5.3 告警配置
### 6.6 工作流调度优化
```java
/**
* 告警配置
* 优化的工作流调度器
*/
@Configuration
public class AlertConfig {
@Component
@Slf4j
public class OptimizedWorkflowScheduler {
private final WorkloadBalancer workloadBalancer;
private final PriorityQueue<WorkflowTask> taskQueue;
@Bean
public AlertManager alertManager(MeterRegistry registry) {
return AlertManager.builder()
.addRule(AlertRule.builder()
.metric("workflow.failure.count")
.threshold(5)
.duration(Duration.ofMinutes(5))
.action(this::sendAlert)
.build())
.addRule(AlertRule.builder()
.metric("node.failure.count")
.threshold(10)
.duration(Duration.ofMinutes(5))
.action(this::sendAlert)
.build())
.build();
// 优先级调度
public void schedulePriorityTask(WorkflowTask task) {
task.setPriority(calculatePriority(task));
taskQueue.offer(task);
}
private void sendAlert(Alert alert) {
// 发送告警通知
// 负载均衡
public void balanceWorkload() {
List<WorkerNode> workers = workloadBalancer.getActiveWorkers();
for (WorkerNode worker : workers) {
if (worker.isOverloaded()) {
redistributeTasks(worker);
}
}
}
// 资源预留
public void reserveResources(WorkflowTask task) {
ResourceRequirements requirements = task.getResourceRequirements();
resourceManager.reserve(requirements);
}
}
```
[保留现有的其他内容...]

View File

@ -0,0 +1,74 @@
package com.qqchen.deploy.backend.workflow.api.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 节点实例DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NodeInstanceDTO extends BaseDTO {
/**
* 工作流实例ID
*/
@NotNull(message = "工作流实例ID不能为空")
private Long workflowInstanceId;
/**
* 节点ID
*/
@NotBlank(message = "节点ID不能为空")
@Size(max = 50, message = "节点ID长度不能超过50")
private String nodeId;
/**
* 节点类型
*/
@NotBlank(message = "节点类型不能为空")
@Size(max = 50, message = "节点类型长度不能超过50")
private String nodeType;
/**
* 节点名称
*/
@NotBlank(message = "节点名称不能为空")
@Size(max = 100, message = "节点名称长度不能超过100")
private String name;
/**
* 状态PENDING/RUNNING/COMPLETED/FAILED/CANCELED
*/
private String status;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
/**
* 输入参数(JSON)
*/
private String input;
/**
* 输出结果(JSON)
*/
private String output;
/**
* 错误信息
*/
private String error;
}

View File

@ -0,0 +1,52 @@
package com.qqchen.deploy.backend.workflow.api.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 工作流定义DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowDefinitionDTO extends BaseDTO {
/**
* 工作流编码
*/
@NotBlank(message = "工作流编码不能为空")
@Size(max = 50, message = "工作流编码长度不能超过50")
private String code;
/**
* 工作流名称
*/
@NotBlank(message = "工作流名称不能为空")
@Size(max = 100, message = "工作流名称长度不能超过100")
private String name;
/**
* 描述
*/
private String description;
/**
* 工作流定义(JSON)
*/
@NotBlank(message = "工作流定义不能为空")
private String content;
/**
* 类型DEPLOY/CONFIG_SYNC
*/
@NotBlank(message = "工作流类型不能为空")
@Size(max = 20, message = "工作流类型长度不能超过20")
private String type;
/**
* 状态DRAFT/PUBLISHED/DISABLED
*/
private String status;
}

View File

@ -0,0 +1,57 @@
package com.qqchen.deploy.backend.workflow.api.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 工作流实例DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowInstanceDTO extends BaseDTO {
/**
* 工作流定义ID
*/
@NotNull(message = "工作流定义ID不能为空")
private Long definitionId;
/**
* 项目环境ID
*/
@NotNull(message = "项目环境ID不能为空")
private Long projectEnvId;
/**
* 状态RUNNING/COMPLETED/FAILED/CANCELED
*/
private String status;
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
/**
* 工作流变量(JSON)
*/
private String variables;
/**
* 错误信息
*/
private String error;
/**
* 工作流定义名称冗余字段
*/
private String workflowName;
}

View File

@ -0,0 +1,58 @@
package com.qqchen.deploy.backend.workflow.api.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 节点实例查询对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NodeInstanceQuery extends BaseQuery {
/**
* 工作流实例ID
*/
@QueryField(field = "workflowInstance.id")
private Long workflowInstanceId;
/**
* 节点ID
*/
@QueryField(field = "nodeId")
private String nodeId;
/**
* 节点类型
*/
@QueryField(field = "nodeType")
private String nodeType;
/**
* 节点名称
*/
@QueryField(field = "name", type = QueryType.LIKE)
private String name;
/**
* 节点状态
*/
@QueryField(field = "status")
private String status;
// /**
// * 开始时间范围起始
// */
// @QueryField(field = "startTime", type = QueryType.BETWEEN)
// private LocalDateTime startTimeBegin;
//
// /**
// * 开始时间范围结束
// */
// @QueryField(field = "startTime", type = QueryType.LESS_THAN_OR_EQUAL)
// private LocalDateTime startTimeEnd;
}

View File

@ -0,0 +1,39 @@
package com.qqchen.deploy.backend.workflow.api.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 工作流定义查询对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowDefinitionQuery extends BaseQuery {
/**
* 工作流编码
*/
@QueryField(field = "code", type = QueryType.LIKE)
private String code;
/**
* 工作流名称
*/
@QueryField(field = "name", type = QueryType.LIKE)
private String name;
/**
* 工作流类型
*/
@QueryField(field = "type")
private String type;
/**
* 工作流状态
*/
@QueryField(field = "status")
private String status;
}

View File

@ -0,0 +1,52 @@
package com.qqchen.deploy.backend.workflow.api.query;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 工作流实例查询对象
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowInstanceQuery extends BaseQuery {
/**
* 工作流定义ID
*/
@QueryField(field = "definition.id")
private Long definitionId;
/**
* 项目环境ID
*/
@QueryField(field = "projectEnvId")
private Long projectEnvId;
/**
* 工作流状态
*/
@QueryField(field = "status")
private String status;
// /**
// * 开始时间范围起始
// */
// @QueryField(field = "startTime", type = QueryType.GREATER_THAN_OR_EQUAL)
// private LocalDateTime startTimeBegin;
//
// /**
// * 开始时间范围结束
// */
// @QueryField(field = "startTime", type = QueryType.LESS_THAN_OR_EQUAL)
// private LocalDateTime startTimeEnd;
/**
* 工作流定义名称
*/
@QueryField(field = "definition.name", type = QueryType.LIKE)
private String workflowName;
}

View File

@ -0,0 +1,72 @@
package com.qqchen.deploy.backend.workflow.controller;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.api.dto.NodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.api.query.NodeInstanceQuery;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.service.INodeInstanceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 节点实例控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow/node")
@Tag(name = "工作流节点管理", description = "工作流节点管理相关接口")
public class NodeInstanceApiController extends BaseController<NodeInstance, NodeInstanceDTO, Long, NodeInstanceQuery> {
@Resource
private INodeInstanceService nodeInstanceService;
@Operation(summary = "根据工作流实例ID查询节点实例列表")
@GetMapping("/workflow/{workflowInstanceId}")
public Response<List<NodeInstanceDTO>> findByWorkflowInstanceId(
@Parameter(description = "工作流实例ID", required = true)
@PathVariable Long workflowInstanceId) {
return Response.success(nodeInstanceService.findByWorkflowInstanceId(workflowInstanceId));
}
@Operation(summary = "根据工作流实例ID和状态查询节点实例列表")
@GetMapping("/workflow/{workflowInstanceId}/status/{status}")
public Response<List<NodeInstanceDTO>> findByWorkflowInstanceIdAndStatus(
@Parameter(description = "工作流实例ID", required = true)
@PathVariable Long workflowInstanceId,
@Parameter(description = "状态", required = true)
@PathVariable String status) {
return Response.success(nodeInstanceService.findByWorkflowInstanceIdAndStatus(workflowInstanceId, status));
}
@Operation(summary = "更新节点状态")
@PostMapping("/{id}/status")
public Response<Boolean> updateStatus(
@Parameter(description = "节点实例ID", required = true)
@PathVariable Long id,
@Parameter(description = "状态", required = true)
@RequestParam String status,
@Parameter(description = "输出结果")
@RequestParam(required = false) String output,
@Parameter(description = "错误信息")
@RequestParam(required = false) String error) {
return Response.success(nodeInstanceService.updateStatus(id, status, output, error));
}
@Override
protected void exportData(HttpServletResponse response, List<NodeInstanceDTO> data) {
// TODO: 实现导出功能
}
protected INodeInstanceService getService() {
return nodeInstanceService;
}
}

View File

@ -0,0 +1,60 @@
package com.qqchen.deploy.backend.workflow.controller;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.api.query.WorkflowDefinitionQuery;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.service.IWorkflowDefinitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 工作流定义控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow/definition")
@Tag(name = "工作流定义管理", description = "工作流定义管理相关接口")
public class WorkflowDefinitionApiController extends BaseController<WorkflowDefinition, WorkflowDefinitionDTO, Long, WorkflowDefinitionQuery> {
@Resource
private IWorkflowDefinitionService workflowDefinitionService;
@Operation(summary = "发布工作流")
@PostMapping("/{id}/publish")
public Response<Boolean> publish(
@Parameter(description = "工作流定义ID", required = true)
@PathVariable Long id) {
return Response.success(workflowDefinitionService.publish(id));
}
@Operation(summary = "禁用工作流")
@PostMapping("/{id}/disable")
public Response<Boolean> disable(
@Parameter(description = "工作流定义ID", required = true)
@PathVariable Long id) {
return Response.success(workflowDefinitionService.disable(id));
}
@Operation(summary = "根据编码查询工作流定义")
@GetMapping("/code/{code}")
public Response<WorkflowDefinitionDTO> findByCode(
@Parameter(description = "工作流编码", required = true)
@PathVariable String code) {
return Response.success(workflowDefinitionService.findByCode(code));
}
@Override
protected void exportData(HttpServletResponse response, List<WorkflowDefinitionDTO> data) {
// TODO: 实现导出功能
}
}

View File

@ -0,0 +1,79 @@
package com.qqchen.deploy.backend.workflow.controller;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.api.query.WorkflowInstanceQuery;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 工作流实例控制器
*/
@Slf4j
@RestController
@RequestMapping("/api/v1/workflow/instance")
@Tag(name = "工作流实例管理", description = "工作流实例管理相关接口")
public class WorkflowInstanceApiController extends BaseController<WorkflowInstance, WorkflowInstanceDTO, Long, WorkflowInstanceQuery> {
private final IWorkflowInstanceService workflowInstanceService;
public WorkflowInstanceApiController(IWorkflowInstanceService workflowInstanceService) {
this.workflowInstanceService = workflowInstanceService;
}
@Operation(summary = "启动工作流实例")
@PostMapping("/start")
public Response<WorkflowInstanceDTO> start(
@Parameter(description = "工作流定义ID", required = true)
@RequestParam Long definitionId,
@Parameter(description = "项目环境ID", required = true)
@RequestParam Long projectEnvId,
@Parameter(description = "工作流变量")
@RequestParam(required = false) String variables) {
return Response.success(workflowInstanceService.start(definitionId, projectEnvId, variables));
}
@Operation(summary = "取消工作流实例")
@PostMapping("/{id}/cancel")
public Response<Boolean> cancel(
@Parameter(description = "工作流实例ID", required = true)
@PathVariable Long id) {
return Response.success(workflowInstanceService.cancel(id));
}
@Operation(summary = "根据项目环境ID查询工作流实例列表")
@GetMapping("/project-env/{projectEnvId}")
public Response<List<WorkflowInstanceDTO>> findByProjectEnvId(
@Parameter(description = "项目环境ID", required = true)
@PathVariable Long projectEnvId) {
return Response.success(workflowInstanceService.findByProjectEnvId(projectEnvId));
}
@Operation(summary = "根据项目环境ID和状态查询工作流实例列表")
@GetMapping("/project-env/{projectEnvId}/status/{status}")
public Response<List<WorkflowInstanceDTO>> findByProjectEnvIdAndStatus(
@Parameter(description = "项目环境ID", required = true)
@PathVariable Long projectEnvId,
@Parameter(description = "状态", required = true)
@PathVariable String status) {
return Response.success(workflowInstanceService.findByProjectEnvIdAndStatus(projectEnvId, status));
}
@Override
protected void exportData(HttpServletResponse response, List<WorkflowInstanceDTO> data) {
// TODO: 实现导出功能
}
protected IWorkflowInstanceService getService() {
return workflowInstanceService;
}
}

View File

@ -0,0 +1,20 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.workflow.api.dto.NodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
/**
* 节点实例转换器
*/
@Mapper(config = BaseConverter.class)
public interface NodeInstanceConverter extends BaseConverter<NodeInstance, NodeInstanceDTO> {
@Mapping(target = "workflowInstanceId", source = "workflowInstance.id")
NodeInstanceDTO toDto(NodeInstance entity);
@Mapping(target = "workflowInstance.id", source = "workflowInstanceId")
NodeInstance toEntity(NodeInstanceDTO dto);
}

View File

@ -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.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import org.mapstruct.Mapper;
/**
* 工作流定义转换器
*/
@Mapper(config = BaseConverter.class)
public interface WorkflowDefinitionConverter extends BaseConverter<WorkflowDefinition, WorkflowDefinitionDTO> {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,64 @@
package com.qqchen.deploy.backend.workflow.engine.node;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Data;
/**
* 节点配置基类
*/
@Data
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = ApprovalNodeConfig.class, name = "APPROVAL"),
@JsonSubTypes.Type(value = ScriptNodeConfig.class, name = "SCRIPT"),
@JsonSubTypes.Type(value = JenkinsNodeConfig.class, name = "JENKINS"),
@JsonSubTypes.Type(value = GitNodeConfig.class, name = "GIT"),
@JsonSubTypes.Type(value = ConditionNodeConfig.class, name = "CONDITION"),
@JsonSubTypes.Type(value = ParallelNodeConfig.class, name = "PARALLEL"),
@JsonSubTypes.Type(value = NacosNodeConfig.class, name = "NACOS"),
@JsonSubTypes.Type(value = HttpNodeConfig.class, name = "HTTP"),
@JsonSubTypes.Type(value = NotifyNodeConfig.class, name = "NOTIFY")
})
public abstract class NodeConfig {
/**
* 节点ID
*/
private String nodeId;
/**
* 节点名称
*/
private String name;
/**
* 节点类型
*/
private NodeType type;
/**
* 超时时间分钟
*/
private Integer timeout;
/**
* 重试次数
*/
private Integer retryCount;
/**
* 重试间隔
*/
private Integer retryInterval;
/**
* 失败是否继续
*/
private Boolean continueOnFailed;
/**
* 描述
*/
private String description;
}

View File

@ -0,0 +1,27 @@
package com.qqchen.deploy.backend.workflow.engine.node;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 节点类型枚举
*/
@Getter
@AllArgsConstructor
public enum NodeType {
START("START", "开始节点"),
END("END", "结束节点"),
APPROVAL("APPROVAL", "审批节点"),
SCRIPT("SCRIPT", "脚本节点"),
JENKINS("JENKINS", "Jenkins构建节点"),
GIT("GIT", "Git操作节点"),
CONDITION("CONDITION", "条件节点"),
PARALLEL("PARALLEL", "并行节点"),
NACOS("NACOS", "Nacos配置节点"),
HTTP("HTTP", "HTTP请求节点"),
NOTIFY("NOTIFY", "通知节点");
private final String code;
private final String description;
}

View File

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

View File

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

View File

@ -0,0 +1,48 @@
package com.qqchen.deploy.backend.workflow.engine.node;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Map;
/**
* 脚本节点配置
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ScriptNodeConfig extends NodeConfig {
/**
* 脚本类型SHELL, PYTHON, GROOVY
*/
private String scriptType;
/**
* 脚本内容
*/
private String content;
/**
* 脚本参数
*/
private Map<String, String> parameters;
/**
* 执行主机ID
*/
private Long hostId;
/**
* 工作目录
*/
private String workingDirectory;
/**
* 环境变量
*/
private Map<String, String> environment;
/**
* 成功退出码
*/
private Integer successExitCode;
}

View File

@ -0,0 +1,80 @@
package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import jakarta.persistence.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 节点实例实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "sys_node_instance")
@LogicDelete
public class NodeInstance extends com.qqchen.deploy.backend.framework.domain.Entity<Long> {
/**
* 工作流实例
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "workflow_instance_id", nullable = false)
private WorkflowInstance workflowInstance;
/**
* 节点ID
*/
@Column(name = "node_id", nullable = false, length = 50)
private String nodeId;
/**
* 节点类型
*/
@Column(name = "node_type", nullable = false, length = 50)
private String nodeType;
/**
* 节点名称
*/
@Column(name = "name", nullable = false, length = 100)
private String name;
/**
* 状态PENDING/RUNNING/COMPLETED/FAILED/CANCELED
*/
@Column(name = "status", nullable = false, length = 20)
private String status;
/**
* 开始时间
*/
@Column(name = "start_time")
private LocalDateTime startTime;
/**
* 结束时间
*/
@Column(name = "end_time")
private LocalDateTime endTime;
/**
* 输入参数(JSON)
*/
@Column(name = "input", columnDefinition = "TEXT")
private String input;
/**
* 输出结果(JSON)
*/
@Column(name = "output", columnDefinition = "TEXT")
private String output;
/**
* 错误信息
*/
@Column(name = "error", columnDefinition = "TEXT")
private String error;
}

View File

@ -0,0 +1,56 @@
package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity;
import jakarta.persistence.Column;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 工作流定义实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "sys_workflow_definition")
@LogicDelete
public class WorkflowDefinition extends Entity<Long> {
/**
* 工作流编码
*/
@Column(name = "code", nullable = false, length = 50)
private String code;
/**
* 工作流名称
*/
@Column(name = "name", nullable = false, length = 100)
private String name;
/**
* 描述
*/
@Column(name = "description", columnDefinition = "TEXT")
private String description;
/**
* 工作流定义(JSON)
*/
@Column(name = "content", nullable = false, columnDefinition = "TEXT")
private String content;
/**
* 类型DEPLOY/CONFIG_SYNC
*/
@Column(name = "type", nullable = false, length = 20)
private String type;
/**
* 状态DRAFT/PUBLISHED/DISABLED
*/
@Column(name = "status", nullable = false, length = 20)
private String status = "DRAFT";
}

View File

@ -0,0 +1,67 @@
package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 工作流实例实体
*/
@Data
@EqualsAndHashCode(callSuper = true)
@jakarta.persistence.Entity
@Table(name = "sys_workflow_instance")
@LogicDelete
public class WorkflowInstance extends Entity<Long> {
/**
* 工作流定义ID
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "definition_id", nullable = false)
private WorkflowDefinition definition;
/**
* 项目环境ID
*/
@Column(name = "project_env_id", nullable = false)
private Long projectEnvId;
/**
* 状态RUNNING/COMPLETED/FAILED/CANCELED
*/
@Column(name = "status", nullable = false, length = 20)
private String status;
/**
* 开始时间
*/
@Column(name = "start_time", nullable = false)
private LocalDateTime startTime;
/**
* 结束时间
*/
@Column(name = "end_time")
private LocalDateTime endTime;
/**
* 工作流变量(JSON)
*/
@Column(name = "variables", columnDefinition = "TEXT")
private String variables;
/**
* 错误信息
*/
@Column(name = "error", columnDefinition = "TEXT")
private String error;
}

View File

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

View File

@ -0,0 +1,19 @@
package com.qqchen.deploy.backend.workflow.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 工作流状态枚举
*/
@Getter
@AllArgsConstructor
public enum WorkflowStatusEnum {
DRAFT("DRAFT", "草稿"),
PUBLISHED("PUBLISHED", "已发布"),
DISABLED("DISABLED", "已禁用");
private final String code;
private final String description;
}

View File

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

View File

@ -0,0 +1,34 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 节点实例仓库接口
*/
@Repository
public interface INodeInstanceRepository extends IBaseRepository<NodeInstance, Long> {
/**
* 根据工作流实例ID查询节点实例列表
*
* @param workflowInstanceId 工作流实例ID
* @return 节点实例列表
*/
@Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance.id = :workflowInstanceId AND n.deleted = false ORDER BY n.createTime ASC")
List<NodeInstance> findByWorkflowInstanceId(@Param("workflowInstanceId") Long workflowInstanceId);
/**
* 根据工作流实例ID和状态查询节点实例列表
*
* @param workflowInstanceId 工作流实例ID
* @param status 状态
* @return 节点实例列表
*/
@Query("SELECT n FROM NodeInstance n WHERE n.workflowInstance.id = :workflowInstanceId AND n.status = :status AND n.deleted = false ORDER BY n.createTime ASC")
List<NodeInstance> findByWorkflowInstanceIdAndStatus(@Param("workflowInstanceId") Long workflowInstanceId, @Param("status") String status);
}

View File

@ -0,0 +1,28 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import org.springframework.stereotype.Repository;
/**
* 工作流定义仓库接口
*/
@Repository
public interface IWorkflowDefinitionRepository extends IBaseRepository<WorkflowDefinition, Long> {
/**
* 根据编码查询未删除的工作流定义
*
* @param code 工作流编码
* @return 工作流定义
*/
WorkflowDefinition findByCodeAndDeletedFalse(String code);
/**
* 检查编码是否存在排除已删除的
*
* @param code 工作流编码
* @return 是否存在
*/
boolean existsByCodeAndDeletedFalse(String code);
}

View File

@ -0,0 +1,34 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
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 IWorkflowInstanceRepository extends IBaseRepository<WorkflowInstance, Long> {
/**
* 根据项目环境ID查询工作流实例列表
*
* @param projectEnvId 项目环境ID
* @return 工作流实例列表
*/
@Query("SELECT w FROM WorkflowInstance w WHERE w.projectEnvId = :projectEnvId AND w.deleted = false ORDER BY w.createTime DESC")
List<WorkflowInstance> findByProjectEnvId(@Param("projectEnvId") Long projectEnvId);
/**
* 根据项目环境ID和状态查询工作流实例列表
*
* @param projectEnvId 项目环境ID
* @param status 状态
* @return 工作流实例列表
*/
@Query("SELECT w FROM WorkflowInstance w WHERE w.projectEnvId = :projectEnvId AND w.status = :status AND w.deleted = false ORDER BY w.createTime DESC")
List<WorkflowInstance> findByProjectEnvIdAndStatus(@Param("projectEnvId") Long projectEnvId, @Param("status") String status);
}

View File

@ -0,0 +1,35 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
/**
* 工作流定义服务接口
*/
public interface IWorkflowDefinitionService extends IBaseService<WorkflowDefinition, WorkflowDefinitionDTO, Long> {
/**
* 发布工作流
*
* @param id 工作流定义ID
* @return 是否成功
*/
boolean publish(Long id);
/**
* 禁用工作流
*
* @param id 工作流定义ID
* @return 是否成功
*/
boolean disable(Long id);
/**
* 根据编码查询工作流定义
*
* @param code 工作流编码
* @return 工作流定义
*/
WorkflowDefinitionDTO findByCode(String code);
}

View File

@ -0,0 +1,47 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import java.util.List;
/**
* 工作流实例服务接口
*/
public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance, WorkflowInstanceDTO, Long> {
/**
* 启动工作流实例
*
* @param definitionId 工作流定义ID
* @param projectEnvId 项目环境ID
* @param variables 工作流变量
* @return 工作流实例
*/
WorkflowInstanceDTO start(Long definitionId, Long projectEnvId, String variables);
/**
* 取消工作流实例
*
* @param id 工作流实例ID
* @return 是否成功
*/
boolean cancel(Long id);
/**
* 根据项目环境ID查询工作流实例列表
*
* @param projectEnvId 项目环境ID
* @return 工作流实例列表
*/
List<WorkflowInstanceDTO> findByProjectEnvId(Long projectEnvId);
/**
* 根据项目环境ID和状态查询工作流实例列表
*
* @param projectEnvId 项目环境ID
* @param status 状态
* @return 工作流实例列表
*/
List<WorkflowInstanceDTO> findByProjectEnvIdAndStatus(Long projectEnvId, String status);
}

View File

@ -0,0 +1,71 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.api.dto.NodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.converter.NodeInstanceConverter;
import com.qqchen.deploy.backend.workflow.entity.NodeInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.INodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.INodeInstanceService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* 节点实例服务实现类
*/
@Slf4j
@Service
public class NodeInstanceServiceImpl extends BaseServiceImpl<NodeInstance, NodeInstanceDTO, Long>
implements INodeInstanceService {
@Resource
private INodeInstanceRepository nodeInstanceRepository;
@Resource
private NodeInstanceConverter nodeInstanceConverter;
@Override
public List<NodeInstanceDTO> findByWorkflowInstanceId(Long workflowInstanceId) {
List<NodeInstance> instances = nodeInstanceRepository.findByWorkflowInstanceId(workflowInstanceId);
return instances.stream()
.map(nodeInstanceConverter::toDto)
.collect(Collectors.toList());
}
@Override
public List<NodeInstanceDTO> findByWorkflowInstanceIdAndStatus(Long workflowInstanceId, String status) {
List<NodeInstance> instances = nodeInstanceRepository.findByWorkflowInstanceIdAndStatus(workflowInstanceId, status);
return instances.stream()
.map(nodeInstanceConverter::toDto)
.collect(Collectors.toList());
}
@Override
@Transactional
public boolean updateStatus(Long id, String status, String output, String error) {
NodeInstance instance = findEntityById(id);
instance.setStatus(status);
instance.setOutput(output);
instance.setError(error);
// 如果是开始执行设置开始时间
if (NodeStatusEnum.RUNNING.getCode().equals(status)) {
instance.setStartTime(LocalDateTime.now());
}
// 如果是结束状态设置结束时间
else if (NodeStatusEnum.COMPLETED.getCode().equals(status)
|| NodeStatusEnum.FAILED.getCode().equals(status)
|| NodeStatusEnum.CANCELED.getCode().equals(status)) {
instance.setEndTime(LocalDateTime.now());
}
nodeInstanceRepository.save(instance);
return true;
}
}

View File

@ -0,0 +1,63 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowDefinitionDTO;
import com.qqchen.deploy.backend.workflow.converter.WorkflowDefinitionConverter;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowDefinitionService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 工作流定义服务实现类
*/
@Slf4j
@Service
public class WorkflowDefinitionServiceImpl extends BaseServiceImpl<WorkflowDefinition, WorkflowDefinitionDTO, Long>
implements IWorkflowDefinitionService {
@Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Resource
private WorkflowDefinitionConverter workflowDefinitionConverter;
@Override
@Transactional
public boolean publish(Long id) {
WorkflowDefinition definition = findEntityById(id);
definition.setStatus(WorkflowStatusEnum.PUBLISHED.getCode());
workflowDefinitionRepository.save(definition);
return true;
}
@Override
@Transactional
public boolean disable(Long id) {
WorkflowDefinition definition = findEntityById(id);
definition.setStatus(WorkflowStatusEnum.DISABLED.getCode());
workflowDefinitionRepository.save(definition);
return true;
}
@Override
public WorkflowDefinitionDTO findByCode(String code) {
WorkflowDefinition definition = workflowDefinitionRepository.findByCodeAndDeletedFalse(code);
return workflowDefinitionConverter.toDto(definition);
}
@Transactional
public WorkflowDefinitionDTO save(WorkflowDefinitionDTO dto) {
// 检查编码是否已存在
if (workflowDefinitionRepository.existsByCodeAndDeletedFalse(dto.getCode())) {
throw new IllegalArgumentException("工作流编码已存在");
}
// 设置初始状态为草稿
dto.setStatus(WorkflowStatusEnum.DRAFT.getCode());
return super.create(dto);
}
}

View File

@ -0,0 +1,90 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.api.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.converter.WorkflowInstanceConverter;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.enums.NodeStatusEnum;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
/**
* 工作流实例服务实现类
*/
@Slf4j
@Service
public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstance, WorkflowInstanceDTO, Long>
implements IWorkflowInstanceService {
@Resource
private IWorkflowInstanceRepository workflowInstanceRepository;
@Resource
private IWorkflowDefinitionRepository workflowDefinitionRepository;
@Resource
private WorkflowInstanceConverter workflowInstanceConverter;
@Override
@Transactional
public WorkflowInstanceDTO start(Long definitionId, Long projectEnvId, String variables) {
// 查询工作流定义
WorkflowDefinition definition = workflowDefinitionRepository.findById(definitionId)
.orElseThrow(() -> new IllegalArgumentException("工作流定义不存在"));
// 创建工作流实例
WorkflowInstance instance = new WorkflowInstance();
instance.setDefinition(definition);
instance.setProjectEnvId(projectEnvId);
instance.setVariables(variables);
instance.setStatus(NodeStatusEnum.RUNNING.getCode());
instance.setStartTime(LocalDateTime.now());
// 保存工作流实例
WorkflowInstanceDTO dto = workflowInstanceConverter.toDto(instance);
dto = super.create(dto);
// TODO: 启动工作流执行引擎
return dto;
}
@Override
@Transactional
public boolean cancel(Long id) {
WorkflowInstance instance = findEntityById(id);
instance.setStatus(NodeStatusEnum.CANCELED.getCode());
instance.setEndTime(LocalDateTime.now());
workflowInstanceRepository.save(instance);
// TODO: 通知工作流执行引擎取消执行
return true;
}
@Override
public List<WorkflowInstanceDTO> findByProjectEnvId(Long projectEnvId) {
List<WorkflowInstance> instances = workflowInstanceRepository.findByProjectEnvId(projectEnvId);
return instances.stream()
.map(workflowInstanceConverter::toDto)
.collect(Collectors.toList());
}
@Override
public List<WorkflowInstanceDTO> findByProjectEnvIdAndStatus(Long projectEnvId, String status) {
List<WorkflowInstance> instances = workflowInstanceRepository.findByProjectEnvIdAndStatus(projectEnvId, status);
return instances.stream()
.map(workflowInstanceConverter::toDto)
.collect(Collectors.toList());
}
}

View File

@ -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 '标签<EFBFBD><EFBFBD><EFBFBD>十六进制颜色码)'
color VARCHAR(20) NULL COMMENT '标签十六进制颜色码)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色标签表';
-- 角色标签关联表
@ -377,3 +377,72 @@ CREATE TABLE deploy_repo_branch (
REFERENCES sys_external_system (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='代码仓库分支表';
-- 工作流定义表
CREATE TABLE sys_workflow_definition (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
tenant_id BIGINT NOT NULL COMMENT '租户ID',
code VARCHAR(50) NOT NULL COMMENT '工作流编码',
name VARCHAR(100) NOT NULL COMMENT '工作流名称',
description TEXT COMMENT '描述',
content TEXT NOT NULL COMMENT '工作流定义(JSON)',
type VARCHAR(20) NOT NULL COMMENT '类型DEPLOY/CONFIG_SYNC',
status VARCHAR(20) NOT NULL DEFAULT 'DRAFT' COMMENT '状态DRAFT/PUBLISHED/DISABLED',
CONSTRAINT uk_workflow_code UNIQUE (tenant_id, code),
CONSTRAINT uk_workflow_name UNIQUE (tenant_id, name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流定义表';
-- 工作流实例表
CREATE TABLE sys_workflow_instance (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
tenant_id BIGINT NOT NULL COMMENT '租户ID',
definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
project_env_id BIGINT NOT NULL COMMENT '项目环境ID',
status VARCHAR(20) NOT NULL COMMENT '状态RUNNING/COMPLETED/FAILED/CANCELED',
start_time DATETIME NOT NULL COMMENT '开始时间',
end_time DATETIME COMMENT '结束时间',
variables TEXT COMMENT '工作流变量(JSON)',
error TEXT COMMENT '错误信息',
CONSTRAINT fk_instance_definition FOREIGN KEY (definition_id) REFERENCES sys_workflow_definition(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流实例表';
-- 节点实例表
CREATE TABLE sys_node_instance (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除',
update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
tenant_id BIGINT NOT NULL COMMENT '租户ID',
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
node_id VARCHAR(50) NOT NULL COMMENT '节点ID',
node_type VARCHAR(50) NOT NULL COMMENT '节点类型',
name VARCHAR(100) NOT NULL COMMENT '节点名称',
status VARCHAR(20) NOT NULL COMMENT '状态PENDING/RUNNING/COMPLETED/FAILED/CANCELED',
start_time DATETIME COMMENT '开始时间',
end_time DATETIME COMMENT '结束时间',
input TEXT COMMENT '输入参数(JSON)',
output TEXT COMMENT '输出结果(JSON)',
error TEXT COMMENT '错误信息',
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='节点实例表';

View File

@ -56,7 +56,7 @@ INSERT INTO sys_role (id, create_time, code, name, type, description, sort)
VALUES
(1, NOW(), 'SUPER_ADMIN', '超级管理员', 1, '系统超级管理员,拥有所有权限', 1),
(2, NOW(), 'SYSTEM_ADMIN', '系统管理员', 1, '系统管理员,拥有大部分系统管理权限', 2),
(3, NOW(), 'COMMON_USER', '普通用户', 2, '普通用<EFBFBD><EFBFBD>,仅拥有基本操作权限', 3);
(3, NOW(), 'COMMON_USER', '普通用户', 2, '普通用,仅拥有基本操作权限', 3);
-- 初始化角色标签
INSERT INTO sys_role_tag (id, create_time, name, color)
@ -151,3 +151,120 @@ INSERT INTO sys_external_system (
'TOKEN', NULL, NULL, 'cNSud7D1GmYQKEMco7s5',
NULL, NULL, NULL, '{}'
);
-- 初始化工作流定义数据
INSERT INTO sys_workflow_definition (
create_by, create_time, deleted, update_by, update_time, version, tenant_id,
code, name, description, content, type, status
) VALUES (
'admin', NOW(), 0, 'admin', NOW(), 0, 1,
'DEPLOY_JAVA_APP', 'Java应用部署流程', 'Java应用标准部署流程',
'{
"nodes": [
{
"nodeId": "start",
"type": "START",
"name": "开始",
"nextNodeId": "build"
},
{
"nodeId": "build",
"type": "JENKINS",
"name": "构建应用",
"jenkinsServerId": 1,
"jobName": "$${projectName}-build",
"parameters": {
"BRANCH": "$${branch}",
"ENV": "$${env}"
},
"nextNodeId": "approval"
},
{
"nodeId": "approval",
"type": "APPROVAL",
"name": "部署审批",
"approverType": "ROLE",
"approverIds": [1],
"strategy": "ANY",
"nextNodeId": "deploy"
},
{
"nodeId": "deploy",
"type": "SCRIPT",
"name": "部署应用",
"scriptType": "SHELL",
"content": "kubectl apply -f $${deployYaml}",
"nextNodeId": "end"
},
{
"nodeId": "end",
"type": "END",
"name": "结束"
}
]
}',
'DEPLOY',
'PUBLISHED'
);
INSERT INTO sys_workflow_definition (
create_by, create_time, deleted, update_by, update_time, version, tenant_id,
code, name, description, content, type, status
) VALUES (
'admin', NOW(), 0, 'admin', NOW(), 0, 1,
'SYNC_NACOS_CONFIG', 'Nacos配置同步流程', 'Nacos配置跨环境同步流程',
'{
"nodes": [
{
"nodeId": "start",
"type": "START",
"name": "开始",
"nextNodeId": "export"
},
{
"nodeId": "export",
"type": "NACOS",
"name": "导出配置",
"nacosServerId": 1,
"operation": "EXPORT",
"namespace": "$${sourceNamespace}",
"nextNodeId": "approval"
},
{
"nodeId": "approval",
"type": "APPROVAL",
"name": "同步审批",
"approverType": "ROLE",
"approverIds": [1],
"strategy": "ANY",
"nextNodeId": "import"
},
{
"nodeId": "import",
"type": "NACOS",
"name": "导入配置",
"nacosServerId": 2,
"operation": "IMPORT",
"namespace": "$${targetNamespace}",
"nextNodeId": "notify"
},
{
"nodeId": "notify",
"type": "NOTIFY",
"name": "通知结果",
"notifyType": "DINGTALK",
"receiverType": "ROLE",
"receiverIds": [1],
"title": "配置同步完成通知",
"nextNodeId": "end"
},
{
"nodeId": "end",
"type": "END",
"name": "结束"
}
]
}',
'CONFIG_SYNC',
'PUBLISHED'
);

View File

@ -0,0 +1,20 @@
import React from 'react';
import { Menu } from 'antd';
import type { MenuProps } from 'antd';
import { DeleteOutlined, EditOutlined, CopyOutlined } from '@ant-design/icons';
export const NodeContextMenu = () => {
return (
<Menu>
<Menu.Item key="1" icon={<DeleteOutlined />}>
Delete
</Menu.Item>
<Menu.Item key="2" icon={<EditOutlined />}>
Edit
</Menu.Item>
<Menu.Item key="3" icon={<CopyOutlined />}>
Copy
</Menu.Item>
</Menu>
);
};