This commit is contained in:
asp_ly 2024-12-15 22:09:41 +08:00
parent b065e35d5f
commit 84df7e50b8
14 changed files with 404 additions and 47 deletions

View File

@ -1,25 +1,56 @@
package com.qqchen.deploy.backend.workflow.api; package com.qqchen.deploy.backend.workflow.api;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.controller.BaseController;
import com.qqchen.deploy.backend.workflow.dto.WorkflowNodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowNodeInstance;
import com.qqchen.deploy.backend.workflow.event.ShellLogEvent; import com.qqchen.deploy.backend.workflow.event.ShellLogEvent;
import com.qqchen.deploy.backend.workflow.query.WorkflowNodeInstanceQuery;
import com.qqchen.deploy.backend.workflow.service.IWorkflowNodeInstanceService;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/**
* 工作流节点实例控制器
*/
@Slf4j @Slf4j
@RestController @RestController
@RequestMapping("/api/v1/workflow/node")
@Tag(name = "工作流节点实例管理", description = "工作流节点实例管理相关接口")
@CrossOrigin(origins = "*", allowedHeaders = "*") @CrossOrigin(origins = "*", allowedHeaders = "*")
@RequestMapping("/api/v1/workflow/instance") public class WorkflowNodeInstanceApiController extends BaseController<WorkflowNodeInstance, WorkflowNodeInstanceDTO, Long, WorkflowNodeInstanceQuery> {
public class WorkflowNodeInstanceApiController {
@Resource
private IWorkflowNodeInstanceService workflowNodeInstanceService;
// 存储每个进程的SSE发射器 // 存储每个进程的SSE发射器
private static final Map<String, SseEmitter> EMITTERS = new ConcurrentHashMap<>(); private static final Map<String, SseEmitter> EMITTERS = new ConcurrentHashMap<>();
@Operation(summary = "获取流程实例的所有节点")
@GetMapping("/list/{processInstanceId}")
public Response<List<WorkflowNodeInstanceDTO>> getNodeInstances(
@Parameter(description = "流程实例ID") @PathVariable String processInstanceId
) {
return Response.success(workflowNodeInstanceService.getNodesByProcessInstanceId(processInstanceId));
}
@Operation(summary = "获取节点执行日志")
@GetMapping(value = "/log/{processInstanceId}", produces = "text/event-stream") @GetMapping(value = "/log/{processInstanceId}", produces = "text/event-stream")
public SseEmitter streamLogs(@PathVariable String processInstanceId) { public SseEmitter streamLogs(
@Parameter(description = "流程实例ID") @PathVariable String processInstanceId
) {
SseEmitter emitter = new SseEmitter(Long.MAX_VALUE); SseEmitter emitter = new SseEmitter(Long.MAX_VALUE);
// 设置超时回调 // 设置超时回调
@ -75,4 +106,9 @@ public class WorkflowNodeInstanceApiController {
log.warn("No emitter found for process: {}", processInstanceId); log.warn("No emitter found for process: {}", processInstanceId);
} }
} }
@Override
protected void exportData(HttpServletResponse response, List<WorkflowNodeInstanceDTO> data) {
}
} }

View File

@ -0,0 +1,25 @@
package com.qqchen.deploy.backend.workflow.converter;
import com.qqchen.deploy.backend.framework.converter.BaseConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowNodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowNodeInstance;
import com.qqchen.deploy.backend.workflow.event.WorkflowNodeInstanceStatusChangeEvent;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
@Mapper(config = BaseConverter.class)
public interface WorkflowNodeInstanceConverter extends BaseConverter<WorkflowNodeInstance, WorkflowNodeInstanceDTO> {
@Mappings({
@Mapping(target = "processInstanceId", source = "processInstanceId"),
@Mapping(target = "startTime", source = "startTime"),
@Mapping(target = "endTime", source = "endTime"),
@Mapping(target = "status", source = "status"),
@Mapping(target = "executionId", source = "executionId"),
@Mapping(target = "nodeId", source = "nodeId"),
@Mapping(target = "nodeName", source = "nodeName"),
@Mapping(target = "nodeType", source = "nodeType"),
})
WorkflowNodeInstance eventToEntity(WorkflowNodeInstanceStatusChangeEvent event);
}

View File

@ -0,0 +1,33 @@
package com.qqchen.deploy.backend.workflow.dto;
import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class WorkflowNodeInstanceDTO extends BaseDTO {
private Long id;
private String processInstanceId;
private String executionId;
private String nodeId;
private String nodeName;
private String nodeType;
private WorkflowNodeInstanceStatusEnums status;
private LocalDateTime startTime;
private LocalDateTime endTime;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}

View File

@ -1,7 +1,11 @@
package com.qqchen.deploy.backend.workflow.entity; package com.qqchen.deploy.backend.workflow.entity;
import com.qqchen.deploy.backend.framework.domain.Entity; import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -17,12 +21,21 @@ import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class WorkflowNodeInstance extends Entity<Long> { public class WorkflowNodeInstance extends Entity<Long> {
@Column(name = "workflow_definition_id", nullable = false)
private Long workflowDefinitionId;
/** /**
* 工作流实例ID * 工作流实例ID
*/ */
@Column(name = "workflow_instance_id", nullable = false) @Column(name = "workflow_instance_id", nullable = false)
private Long workflowInstanceId; private Long workflowInstanceId;
@Column(name = "process_instance_id", nullable = false)
private String processInstanceId;
@Column(name = "execution_id", nullable = false)
private String executionId;
/** /**
* 节点ID * 节点ID
*/ */
@ -45,7 +58,8 @@ public class WorkflowNodeInstance extends Entity<Long> {
* 节点状态 * 节点状态
*/ */
@Column(nullable = false) @Column(nullable = false)
private String status; @Enumerated(EnumType.STRING)
private WorkflowNodeInstanceStatusEnums status;
/** /**
* 开始时间 * 开始时间

View File

@ -9,10 +9,6 @@ public enum WorkflowNodeInstanceStatusEnums {
* 未开始节点尚未开始执行 * 未开始节点尚未开始执行
*/ */
NOT_STARTED("NOT_STARTED", "未开始"), NOT_STARTED("NOT_STARTED", "未开始"),
/**
* 已创建流程实例已创建但还未开始运行
*/
CREATED("CREATED", "已创建"),
/** /**
* 运行中流程实例正在执行 * 运行中流程实例正在执行

View File

@ -0,0 +1,30 @@
package com.qqchen.deploy.backend.workflow.event;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Builder
public class WorkflowNodeInstanceStatusChangeEvent {
private String processInstanceId;
private String executionId;
private String nodeId;
private String nodeName;
private String nodeType;
private WorkflowNodeInstanceStatusEnums status;
private LocalDateTime startTime;
private LocalDateTime endTime;
}

View File

@ -2,7 +2,9 @@ package com.qqchen.deploy.backend.workflow.listener;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums; import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.event.WorkflowInstanceStatusChangeEvent; import com.qqchen.deploy.backend.workflow.event.WorkflowInstanceStatusChangeEvent;
import com.qqchen.deploy.backend.workflow.event.WorkflowNodeInstanceStatusChangeEvent;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
@ -15,11 +17,12 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component @Component
@Slf4j @Slf4j
public class FlowableJobEventListener implements FlowableEventListener { public class FlowableJobEventListener implements FlowableEventListener {
@Resource @Resource
private ApplicationEventPublisher publisher; private ApplicationEventPublisher publisher;
@ -27,17 +30,23 @@ public class FlowableJobEventListener implements FlowableEventListener {
@Lazy @Lazy
private HistoryService historyService; private HistoryService historyService;
@Override @Override
public void onEvent(FlowableEvent event) { public void onEvent(FlowableEvent event) {
FlowableEventType eventType = event.getType(); FlowableEventType eventType = event.getType();
log.info("Received Flowable event: {}, event class: {}", eventType, event.getClass().getName());
// 只处理实体事件
if (!(event instanceof FlowableEngineEntityEvent entity)) {
return;
}
String processInstanceId = entity.getProcessInstanceId();
if (isProcessLevelEvent(eventType.name())) { if (isProcessLevelEvent(eventType.name())) {
FlowableEngineEntityEvent entity = (FlowableEngineEntityEvent) event;
String processInstanceId = entity.getProcessInstanceId();
WorkflowInstanceStatusEnums status = convertProcessLevelEventToStatus(eventType.name()); WorkflowInstanceStatusEnums status = convertProcessLevelEventToStatus(eventType.name());
log.info("Process level event received: {} -> {}, processInstanceId: {}", eventType, status, processInstanceId); log.info("Process level event received: {} -> {}, processInstanceId: {}", eventType, status, processInstanceId);
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
log.info("historicProcessInstance: {}", historicProcessInstance.getEndTime()); .processInstanceId(processInstanceId)
.singleResult();
publisher.publishEvent(WorkflowInstanceStatusChangeEvent.builder() publisher.publishEvent(WorkflowInstanceStatusChangeEvent.builder()
.processInstanceId(processInstanceId) .processInstanceId(processInstanceId)
.status(status) .status(status)
@ -45,11 +54,38 @@ public class FlowableJobEventListener implements FlowableEventListener {
.build() .build()
); );
} }
//
// if (isActivityInstanceEvent(flowableEvent.name())) { if (isTaskInstanceEvent(eventType.name())) {
// System.out.println("节点:" + flowableEvent.name()); WorkflowNodeInstanceStatusEnums status = convertJobEventToStatus(eventType.name());
// return; // 获取Job信息
// } String executionId = entity.getExecutionId();
org.flowable.job.api.Job job = (org.flowable.job.service.impl.persistence.entity.JobEntityImpl) entity.getEntity();
String activityId = job.getElementId();
String activityName = job.getElementName();
String activityType = job.getJobHandlerType();
LocalDateTime startTime = job.getCreateTime() != null ? DateUtil.toLocalDateTime(job.getCreateTime()) : null;
LocalDateTime endTime = null;
if (status == WorkflowNodeInstanceStatusEnums.COMPLETED ||
status == WorkflowNodeInstanceStatusEnums.FAILED) {
endTime = LocalDateTime.now(); // 对于完成或失败状态使用当前时间作为结束时间
}
publisher.publishEvent(WorkflowNodeInstanceStatusChangeEvent.builder()
.processInstanceId(processInstanceId)
.executionId(executionId)
.nodeId(activityId)
.nodeName(activityName)
.nodeType(activityType)
.status(status)
.startTime(startTime)
.endTime(endTime)
.build()
);
log.info("Job event received: {} -> {}, processInstanceId: {}, nodeId: {}, nodeName: {}",
eventType, status, processInstanceId, activityId, activityName);
}
} }
@Override @Override
@ -71,6 +107,11 @@ public class FlowableJobEventListener implements FlowableEventListener {
return eventType.startsWith("PROCESS_"); return eventType.startsWith("PROCESS_");
} }
private boolean isTaskInstanceEvent(String eventType) {
return eventType.startsWith("JOB_EXECUTION_") ||
eventType.startsWith("ACTIVITY_"); // 添加对 ACTIVITY_ 事件的监听
}
/** /**
* 将Flowable流程级别事件类型转换为工作流实例状态 * 将Flowable流程级别事件类型转换为工作流实例状态
* 只处理流程实例级别的事件不处理活动节点级别的事件 * 只处理流程实例级别的事件不处理活动节点级别的事件
@ -113,4 +154,27 @@ public class FlowableJobEventListener implements FlowableEventListener {
return null; return null;
} }
} }
/**
* 将Flowable活动节点事件类型转换为工作流节点实例状态
*
* @param eventType Flowable事件类型
* @return 工作流节点实例状态枚举如果不是节点级别事件则返回null
*/
private WorkflowNodeInstanceStatusEnums convertJobEventToStatus(String eventType) {
switch (eventType) {
case "JOB_EXECUTION_SUCCESS":
return WorkflowNodeInstanceStatusEnums.COMPLETED;
case "JOB_EXECUTION_START":
return WorkflowNodeInstanceStatusEnums.RUNNING;
case "JOB_EXECUTION_FAILURE":
return WorkflowNodeInstanceStatusEnums.FAILED;
case "JOB_EXECUTION_REJECTED": // 当作业被拒绝执行时
return WorkflowNodeInstanceStatusEnums.FAILED;
case "JOB_EXECUTION_NOJOB_FOUND": // 当找不到要执行的作业时
return WorkflowNodeInstanceStatusEnums.FAILED;
default:
return null;
}
}
} }

View File

@ -1,2 +0,0 @@
package com.qqchen.deploy.backend.workflow.listener;

View File

@ -0,0 +1,25 @@
package com.qqchen.deploy.backend.workflow.listener;
import com.qqchen.deploy.backend.workflow.event.WorkflowNodeInstanceStatusChangeEvent;
import com.qqchen.deploy.backend.workflow.service.IWorkflowNodeInstanceService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Slf4j
@Component
public class WorkflowNodeInstanceStatusChangeListener {
@Resource
private IWorkflowNodeInstanceService workflowNodeInstanceService;
@EventListener
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void handleWorkflowStatusChange(WorkflowNodeInstanceStatusChangeEvent event) {
log.info("Handling workflow node instance status change event: {}", event);
workflowNodeInstanceService.saveWorkflowNodeInstance(event);
}
}

View File

@ -0,0 +1,11 @@
package com.qqchen.deploy.backend.workflow.query;
import com.qqchen.deploy.backend.framework.query.BaseQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class WorkflowNodeInstanceQuery extends BaseQuery {
}

View File

@ -0,0 +1,15 @@
package com.qqchen.deploy.backend.workflow.repository;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.workflow.entity.WorkflowNodeInstance;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface IWorkflowNodeInstanceRepository extends IBaseRepository<WorkflowNodeInstance, Long> {
Optional<WorkflowNodeInstance> findByProcessInstanceIdAndExecutionId(String processInstanceId, String executionId);
List<WorkflowNodeInstance> findByProcessInstanceId(String processInstanceId);
}

View File

@ -0,0 +1,33 @@
package com.qqchen.deploy.backend.workflow.service;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.workflow.dto.WorkflowNodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowNodeInstance;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.event.WorkflowInstanceStatusChangeEvent;
import com.qqchen.deploy.backend.workflow.event.WorkflowNodeInstanceStatusChangeEvent;
import com.qqchen.deploy.backend.workflow.query.WorkflowNodeInstanceQuery;
import java.util.List;
public interface IWorkflowNodeInstanceService extends IBaseService<WorkflowNodeInstance, WorkflowNodeInstanceDTO, Long> {
/**
* 更新节点实例状态
*
* @param processInstanceId 流程实例ID
* @param executionId 执行ID
* @param status 状态
* @return 更新后的节点实例
*/
WorkflowNodeInstance updateNodeStatus(String processInstanceId, String executionId, WorkflowNodeInstanceStatusEnums status);
/**
* 获取流程实例的所有节点
*
* @param processInstanceId 流程实例ID
* @return 节点列表
*/
List<WorkflowNodeInstanceDTO> getNodesByProcessInstanceId(String processInstanceId);
void saveWorkflowNodeInstance(WorkflowNodeInstanceStatusChangeEvent event);
}

View File

@ -0,0 +1,74 @@
package com.qqchen.deploy.backend.workflow.service.impl;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import com.qqchen.deploy.backend.workflow.converter.WorkflowNodeInstanceConverter;
import com.qqchen.deploy.backend.workflow.dto.WorkflowNodeInstanceDTO;
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
import com.qqchen.deploy.backend.workflow.entity.WorkflowNodeInstance;
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
import com.qqchen.deploy.backend.workflow.event.WorkflowInstanceStatusChangeEvent;
import com.qqchen.deploy.backend.workflow.event.WorkflowNodeInstanceStatusChangeEvent;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowNodeInstanceRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import com.qqchen.deploy.backend.workflow.service.IWorkflowNodeInstanceService;
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.Optional;
import java.util.stream.Collectors;
@Slf4j
@Service
public class WorkflowNodeInstanceServiceImpl extends BaseServiceImpl<WorkflowNodeInstance, WorkflowNodeInstanceDTO, Long> implements IWorkflowNodeInstanceService {
@Resource
private IWorkflowNodeInstanceRepository workflowNodeInstanceRepository;
@Resource
private WorkflowNodeInstanceConverter workflowNodeInstanceConverter;
@Resource
private IWorkflowInstanceRepository workflowInstanceRepository;
@Override
@Transactional(rollbackFor = Exception.class)
public WorkflowNodeInstance updateNodeStatus(String processInstanceId, String executionId, WorkflowNodeInstanceStatusEnums status) {
WorkflowNodeInstance nodeInstance = workflowNodeInstanceRepository.findByProcessInstanceIdAndExecutionId(processInstanceId, executionId)
.orElseThrow(() -> new RuntimeException("Node instance not found for processInstanceId: " + processInstanceId + ", executionId: " + executionId));
nodeInstance.setStatus(status);
nodeInstance.setUpdateTime(LocalDateTime.now());
if (status == WorkflowNodeInstanceStatusEnums.COMPLETED ||
status == WorkflowNodeInstanceStatusEnums.TERMINATED ||
status == WorkflowNodeInstanceStatusEnums.FAILED) {
nodeInstance.setEndTime(LocalDateTime.now());
}
return workflowNodeInstanceRepository.save(nodeInstance);
}
@Override
public List<WorkflowNodeInstanceDTO> getNodesByProcessInstanceId(String processInstanceId) {
List<WorkflowNodeInstance> nodes = workflowNodeInstanceRepository.findByProcessInstanceId(processInstanceId);
return nodes.stream()
.map(workflowNodeInstanceConverter::toDto)
.collect(Collectors.toList());
}
@Override
@Transactional
public void saveWorkflowNodeInstance(WorkflowNodeInstanceStatusChangeEvent event) {
WorkflowNodeInstance nodeInstance = workflowNodeInstanceConverter.eventToEntity(event);
WorkflowInstance workflowInstance = workflowInstanceRepository.findByProcessInstanceId(nodeInstance.getProcessInstanceId())
.orElseThrow(() -> new RuntimeException("Node instance not found for processInstanceId: " + nodeInstance.getProcessInstanceId()));
nodeInstance.setWorkflowInstanceId(workflowInstance.getId());
nodeInstance.setWorkflowDefinitionId(workflowInstance.getWorkflowDefinitionId());
super.repository.save(nodeInstance);
}
}

View File

@ -483,23 +483,26 @@ CREATE TABLE workflow_instance
-- 工作流节点实例表 -- 工作流节点实例表
CREATE TABLE workflow_node_instance CREATE TABLE workflow_node_instance
( (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
create_by VARCHAR(255) NULL COMMENT '创建人', create_by VARCHAR(255) NULL COMMENT '创建人',
create_time DATETIME(6) NULL COMMENT '创建时间', create_time DATETIME(6) NULL COMMENT '创建时间',
deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除', deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除0未删除1已删除',
update_by VARCHAR(255) NULL COMMENT '更新人', update_by VARCHAR(255) NULL COMMENT '更新人',
update_time DATETIME(6) NULL COMMENT '更新时间', update_time DATETIME(6) NULL COMMENT '更新时间',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号', version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID', workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
node_id VARCHAR(64) NOT NULL COMMENT '节点ID', workflow_instance_id BIGINT NOT NULL COMMENT '工作流实例ID',
node_name VARCHAR(100) NOT NULL COMMENT '节点名称', process_instance_id VARCHAR(64) NOT NULL COMMENT '流程实例ID',
node_type VARCHAR(32) NOT NULL COMMENT '节点类型', execution_id VARCHAR(100) NOT NULL COMMENT '执行实例ID',
status VARCHAR(32) NOT NULL COMMENT '节点状态', node_id VARCHAR(64) NOT NULL COMMENT '节点ID',
start_time DATETIME(6) NOT NULL COMMENT '开始时间', node_name VARCHAR(100) NOT NULL COMMENT '节点名称',
end_time DATETIME(6) NULL COMMENT '结束时间', node_type VARCHAR(32) NOT NULL COMMENT '节点类型',
variables TEXT NULL COMMENT '节点变量(JSON)', status VARCHAR(32) NOT NULL COMMENT '节点状态',
error_message TEXT NULL COMMENT '错误信息', start_time DATETIME(6) NOT NULL COMMENT '开始时间',
end_time DATETIME(6) NULL COMMENT '结束时间',
variables TEXT NULL COMMENT '节点变量(JSON)',
error_message TEXT NULL COMMENT '错误信息',
CONSTRAINT FK_workflow_node_instance_instance FOREIGN KEY (workflow_instance_id) REFERENCES workflow_instance (id) CONSTRAINT FK_workflow_node_instance_instance FOREIGN KEY (workflow_instance_id) REFERENCES workflow_instance (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流节点实例表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='工作流节点实例表';