可正常启动
This commit is contained in:
parent
dbca5804ff
commit
4cab885098
@ -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);
|
||||
|
||||
### 命名规范
|
||||
- 类名使用PascalCase(UserController、OrderService)
|
||||
- 类名使用PascalCase(UserApiController、OrderApiService)
|
||||
- 方法和变量名使用camelCase(findUserById、isOrderValid)
|
||||
- 常量使用ALL_CAPS(MAX_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(删除)注解
|
||||
|
||||
@ -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对象
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
[保留现有的其他内容...]
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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: 实现导出功能
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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> {
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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";
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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='节点实例表';
|
||||
|
||||
@ -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'
|
||||
);
|
||||
@ -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>
|
||||
);
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user