反序列化问题。
This commit is contained in:
parent
d298a8770c
commit
45e6d70ca4
@ -1052,3 +1052,43 @@ eventSource.addEventListener('STDERR', (event) => {
|
||||
eventSource.onerror = () => {
|
||||
eventSource.close();
|
||||
};
|
||||
|
||||
SELECT
|
||||
hi.PROC_INST_ID_ as process_instance_id,
|
||||
hi.PROC_DEF_ID_ as process_definition_id,
|
||||
hi.START_TIME_ as start_time,
|
||||
hi.END_TIME_ as end_time,
|
||||
hi.DELETE_REASON_ as delete_reason,
|
||||
-- 使用 GROUP_CONCAT 合并错误信息
|
||||
GROUP_CONCAT(DISTINCT
|
||||
CASE
|
||||
WHEN var.NAME_ = 'errorDetail' THEN var.TEXT_
|
||||
WHEN var.NAME_ IN ('errorMessage', 'error', 'exception') THEN var.TEXT_
|
||||
END
|
||||
SEPARATOR '; '
|
||||
) as error_message,
|
||||
-- 获取最后一个活动节点
|
||||
MAX(CASE WHEN act.END_TIME_ IS NULL THEN act.ACT_NAME_ END) as last_activity_name,
|
||||
MAX(CASE WHEN act.END_TIME_ IS NULL THEN act.ACT_ID_ END) as last_activity_id
|
||||
FROM
|
||||
ACT_HI_PROCINST hi
|
||||
LEFT JOIN
|
||||
ACT_HI_VARINST var ON hi.PROC_INST_ID_ = var.PROC_INST_ID_
|
||||
LEFT JOIN
|
||||
ACT_HI_ACTINST act ON hi.PROC_INST_ID_ = act.PROC_INST_ID_
|
||||
WHERE
|
||||
hi.PROC_INST_ID_ = '86faae44-bc47-11ef-a4cd-00155dcaffac'
|
||||
AND (
|
||||
var.NAME_ IN ('errorDetail', 'errorMessage', 'error', 'exception')
|
||||
OR var.TEXT_ LIKE '%error%'
|
||||
OR var.TEXT_ LIKE '%exception%'
|
||||
OR var.TEXT_ LIKE '%失败%'
|
||||
)
|
||||
GROUP BY
|
||||
hi.PROC_INST_ID_,
|
||||
hi.PROC_DEF_ID_,
|
||||
hi.START_TIME_,
|
||||
hi.END_TIME_,
|
||||
hi.DELETE_REASON_
|
||||
ORDER BY
|
||||
hi.START_TIME_ DESC;
|
||||
@ -26,7 +26,7 @@
|
||||
<jjwt.version>0.12.3</jjwt.version>
|
||||
<springdoc.version>2.2.0</springdoc.version>
|
||||
<hutool.version>5.8.23</hutool.version>
|
||||
<flowable.version>7.0.0</flowable.version>
|
||||
<flowable.version>7.1.0</flowable.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
@ -222,9 +222,14 @@
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.flowable</groupId>-->
|
||||
<!-- <artifactId>flowable-spring-boot-starter-rest</artifactId>-->
|
||||
<!-- <version>${flowable.version}</version>-->
|
||||
<!-- <artifactId>flowable-bpmn-model</artifactId>-->
|
||||
<!-- <version>${flowable.version}</version> <!– 使用您项目中的版本 –>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.flowable</groupId>-->
|
||||
<!-- <artifactId>flowable-spring-boot-starter-rest</artifactId>-->
|
||||
<!-- <version>${flowable.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>com.networknt</groupId>
|
||||
<artifactId>json-schema-validator</artifactId>
|
||||
|
||||
@ -7,6 +7,7 @@ import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowExecutionDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowTemplateWithInstancesDTO;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
import com.qqchen.deploy.backend.workflow.query.WorkflowDefinitionQuery;
|
||||
@ -24,6 +25,7 @@ import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.flowable.variable.api.history.HistoricVariableInstance;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
@ -56,6 +58,13 @@ public class WorkflowInstanceApiController extends BaseController<WorkflowInstan
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
@Operation(summary = "查询已发布流程模板及其最近实例")
|
||||
@GetMapping("/templates-with-instances")
|
||||
public Response<Page<WorkflowTemplateWithInstancesDTO>> getTemplatesWithInstances(WorkflowDefinitionQuery query) {
|
||||
return Response.success(workflowInstanceService.findTemplatesWithRecentInstances(query));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void exportData(HttpServletResponse response, List<WorkflowInstanceDTO> data) {
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@ public class WorkflowNodeInstanceApiController extends BaseController<WorkflowNo
|
||||
emitter.completeWithError(e);
|
||||
}
|
||||
} else {
|
||||
log.warn("No emitter found for process: {}", processInstanceId);
|
||||
// log.warn("No emitter found for process: {}", processInstanceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,4 +10,5 @@ public interface WorkFlowConstants {
|
||||
|
||||
String SEQUENCE_FLOW_ERROR_PREFIX = "SEQUENCE_FLOW_ERROR_";
|
||||
|
||||
String ASYNC_CONTINUATION = "async-continuation";
|
||||
}
|
||||
|
||||
@ -65,21 +65,21 @@ public class ShellTaskDelegate implements JavaDelegate {
|
||||
// 如果流程变量中有值,优先使用流程变量
|
||||
if (execution.hasVariable("script")) {
|
||||
scriptValue = (String) execution.getVariable("script");
|
||||
log.info("从流程变量获取到script={}", scriptValue);
|
||||
// log.info("从流程变量获取到script={}", scriptValue);
|
||||
}
|
||||
if (execution.hasVariable("workDir")) {
|
||||
workDirValue = (String) execution.getVariable("workDir");
|
||||
log.info("从流程变量获取到workDir={}", workDirValue);
|
||||
// log.info("从流程变量获取到workDir={}", workDirValue);
|
||||
}
|
||||
if (execution.hasVariable("env")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> envFromVar = (Map<String, String>) execution.getVariable("env");
|
||||
envValue = envFromVar;
|
||||
log.info("从流程变量获取到env={}", envValue);
|
||||
// log.info("从流程变量获取到env={}", envValue);
|
||||
}
|
||||
|
||||
if (scriptValue == null) {
|
||||
log.error("脚本内容为空,执行失败");
|
||||
// log.error("脚本内容为空,执行失败");
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, "Script is required but not provided");
|
||||
}
|
||||
|
||||
@ -169,16 +169,18 @@ public class ShellTaskDelegate implements JavaDelegate {
|
||||
|
||||
if (exitCode != 0) {
|
||||
log.error("Shell脚本执行失败,退出码: {}", exitCode);
|
||||
log.error("错误输出: {}", finalError);
|
||||
execution.setVariable("errorDetail", "Shell脚本执行失败,退出码: " + exitCode);
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, "Shell脚本执行失败,退出码: " + exitCode);
|
||||
// throw new RuntimeException("Shell脚本执行失败,退出码: " + exitCode);
|
||||
}
|
||||
|
||||
log.info("Shell脚本执行成功");
|
||||
log.debug("脚本输出: {}", finalOutput);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Shell脚本执行失败", e);
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, "Shell脚本执行失败");
|
||||
// runtimeService.deleteProcessInstance(execution.getProcessInstanceId(), e.getMessage());
|
||||
// throw new FlowableException("任务执行失败: " + e.getMessage(), e);
|
||||
throw new BpmnError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,13 +197,13 @@ public class ShellTaskDelegate implements JavaDelegate {
|
||||
synchronized (output) {
|
||||
output.append(line).append("\n");
|
||||
}
|
||||
log.info("Shell output: {}", line);
|
||||
// log.info("Shell output: {}", line);
|
||||
} else {
|
||||
StringBuilder error = errorMap.get(processInstanceId);
|
||||
synchronized (error) {
|
||||
error.append(line).append("\n");
|
||||
}
|
||||
log.error("Shell error: {}", line);
|
||||
// log.error("Shell error: {}", line);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
package com.qqchen.deploy.backend.workflow.dto;
|
||||
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class WorkflowTemplateWithInstancesDTO {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String businessKey;
|
||||
|
||||
private LocalDateTime lastExecutionTime;
|
||||
|
||||
private WorkflowInstanceStatusEnums lastExecutionStatus;
|
||||
|
||||
}
|
||||
@ -10,6 +10,8 @@ import lombok.Getter;
|
||||
@Getter
|
||||
public enum WorkflowInstanceStatusEnums {
|
||||
|
||||
NOT_STARTED("NOT_STARTED", "未开始"),
|
||||
|
||||
/**
|
||||
* 已创建:流程实例已创建但还未开始运行
|
||||
*/
|
||||
|
||||
@ -2,14 +2,20 @@ package com.qqchen.deploy.backend.workflow.listener;
|
||||
|
||||
import com.qqchen.deploy.backend.workflow.listener.handler.IFlowableEventHandler;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.job.service.impl.persistence.entity.JobEntityImpl;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants.ASYNC_CONTINUATION;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class FlowableEventDispatcher implements FlowableEventListener {
|
||||
@ -18,18 +24,20 @@ public class FlowableEventDispatcher implements FlowableEventListener {
|
||||
@Lazy
|
||||
private List<IFlowableEventHandler> eventHandlers;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private RuntimeService runtimeService;
|
||||
|
||||
@Override
|
||||
public void onEvent(FlowableEvent event) {
|
||||
String eventType = event.getType().name();
|
||||
// log.info("Dispatching Flowable event: {}, event class: {}", eventType, event.getClass().getName());
|
||||
|
||||
log.info("Dispatching Flowable event: {}, event class: {}", eventType, event.getClass().getName());
|
||||
for (IFlowableEventHandler handler : eventHandlers) {
|
||||
if (handler.canHandle(eventType)) {
|
||||
try {
|
||||
handler.handle(event);
|
||||
} catch (Exception e) {
|
||||
log.error("Error handling event {} by handler {}: {}",
|
||||
eventType, handler.getClass().getSimpleName(), e.getMessage(), e);
|
||||
log.error("Error handling event {} by handler {}: {}", eventType, handler.getClass().getSimpleName(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,179 +0,0 @@
|
||||
package com.qqchen.deploy.backend.workflow.listener;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
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 jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEventListener;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEventType;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.delegate.event.FlowableProcessEngineEvent;
|
||||
import org.flowable.engine.history.HistoricProcessInstance;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
||||
//@Component
|
||||
@Slf4j
|
||||
public class FlowableJobEventListener implements FlowableEventListener {
|
||||
|
||||
@Resource
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Resource
|
||||
@Lazy
|
||||
private HistoryService historyService;
|
||||
|
||||
@Override
|
||||
public void onEvent(FlowableEvent event) {
|
||||
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())) {
|
||||
FlowableProcessEngineEvent flowableEntity = (FlowableProcessEngineEvent) event;
|
||||
WorkflowInstanceStatusEnums status = convertProcessLevelEventToStatus(eventType.name());
|
||||
log.info("Process level event received: {} -> {}, processInstanceId: {}", eventType, status, null);
|
||||
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
|
||||
.processInstanceId(flowableEntity.getProcessInstanceId())
|
||||
.singleResult();
|
||||
publisher.publishEvent(WorkflowInstanceStatusChangeEvent.builder()
|
||||
.processInstanceId(flowableEntity.getProcessInstanceId())
|
||||
.status(status)
|
||||
.endTime(DateUtil.toLocalDateTime(historicProcessInstance.getEndTime()))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
if (isTaskInstanceEvent(eventType.name())) {
|
||||
// WorkflowNodeInstanceStatusEnums status = convertJobEventToStatus(eventType.name());
|
||||
// // 获取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
|
||||
public boolean isFailOnException() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFireOnTransactionLifecycleEvent() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOnTransaction() {
|
||||
return "committed";
|
||||
}
|
||||
|
||||
private boolean isProcessLevelEvent(String eventType) {
|
||||
return eventType.startsWith("PROCESS_");
|
||||
}
|
||||
|
||||
private boolean isTaskInstanceEvent(String eventType) {
|
||||
return eventType.startsWith("JOB_EXECUTION_") ||
|
||||
eventType.startsWith("ACTIVITY_"); // 添加对 ACTIVITY_ 事件的监听
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Flowable流程级别事件类型转换为工作流实例状态
|
||||
* 只处理流程实例级别的事件,不处理活动节点级别的事件
|
||||
*
|
||||
* @param eventType Flowable事件类型
|
||||
* @return 工作流实例状态枚举,如果不是流程级别事件则返回null
|
||||
*/
|
||||
private WorkflowInstanceStatusEnums convertProcessLevelEventToStatus(String eventType) {
|
||||
switch (eventType) {
|
||||
// 创建相关
|
||||
case "PROCESS_CREATED":
|
||||
return WorkflowInstanceStatusEnums.CREATED;
|
||||
|
||||
// 运行相关
|
||||
case "PROCESS_STARTED":
|
||||
return WorkflowInstanceStatusEnums.RUNNING;
|
||||
|
||||
// 暂停相关
|
||||
case "PROCESS_SUSPENDED":
|
||||
return WorkflowInstanceStatusEnums.SUSPENDED;
|
||||
|
||||
// 恢复相关
|
||||
case "PROCESS_RESUMED":
|
||||
return WorkflowInstanceStatusEnums.RUNNING;
|
||||
|
||||
// 完成相关
|
||||
case "PROCESS_COMPLETED":
|
||||
return WorkflowInstanceStatusEnums.COMPLETED;
|
||||
|
||||
// 终止相关
|
||||
case "PROCESS_CANCELLED":
|
||||
return WorkflowInstanceStatusEnums.TERMINATED;
|
||||
|
||||
// 失败相关
|
||||
case "PROCESS_COMPLETED_WITH_ERROR_END_EVENT":
|
||||
return WorkflowInstanceStatusEnums.FAILED;
|
||||
|
||||
default:
|
||||
// 不是流程级别的事件,返回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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,13 +3,21 @@ package com.qqchen.deploy.backend.workflow.listener.handler;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowNodeInstanceStatusEnums;
|
||||
import com.qqchen.deploy.backend.workflow.event.WorkflowNodeInstanceStatusChangeEvent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
|
||||
import org.flowable.engine.delegate.event.FlowableActivityEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants.BOUNDARY_EVENT_ERROR_PREFIX;
|
||||
import static com.qqchen.deploy.backend.workflow.constants.WorkFlowConstants.END_EVENT_ERROR_PREFIX;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@ -18,6 +26,7 @@ public class ActivityEventHandler implements IFlowableEventHandler {
|
||||
@Resource
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean canHandle(String eventType) {
|
||||
return eventType.startsWith("ACTIVITY_");
|
||||
@ -26,26 +35,31 @@ public class ActivityEventHandler implements IFlowableEventHandler {
|
||||
@Override
|
||||
public void handle(FlowableEvent event) {
|
||||
String eventType = event.getType().name();
|
||||
// log.info("Processing activity event: {}", eventType);
|
||||
|
||||
if (!(event instanceof FlowableActivityEvent activityEvent)) {
|
||||
return;
|
||||
}
|
||||
log.info("Processing Flowable event: {}, id: {}, name:{}", eventType, activityEvent.getActivityId(), activityEvent.getActivityName());
|
||||
|
||||
List<String> ignoredList = Arrays.asList(BOUNDARY_EVENT_ERROR_PREFIX, END_EVENT_ERROR_PREFIX);
|
||||
|
||||
boolean ignored = ignoredList.stream().anyMatch(v -> activityEvent.getActivityId().contains(v));
|
||||
if (ignored) {
|
||||
return;
|
||||
}
|
||||
WorkflowNodeInstanceStatusEnums status = convertToNodeStatus(eventType);
|
||||
|
||||
if (status != null) {
|
||||
// publisher.publishEvent(WorkflowNodeInstanceStatusChangeEvent.builder()
|
||||
// .processInstanceId(activityEvent.getProcessInstanceId())
|
||||
// .executionId(activityEvent.getExecutionId())
|
||||
// .nodeId(activityEvent.getActivityId())
|
||||
// .nodeName(activityEvent.getActivityName())
|
||||
// .nodeType(activityEvent.getActivityType())
|
||||
// .status(status)
|
||||
// .startTime(status == WorkflowNodeInstanceStatusEnums.RUNNING ? LocalDateTime.now() : null)
|
||||
// .endTime(status.isFinalState() ? LocalDateTime.now() : null)
|
||||
// .build()
|
||||
// );
|
||||
publisher.publishEvent(WorkflowNodeInstanceStatusChangeEvent.builder()
|
||||
.processInstanceId(activityEvent.getProcessInstanceId())
|
||||
.executionId(activityEvent.getExecutionId())
|
||||
.nodeId(activityEvent.getActivityId())
|
||||
.nodeName(StringUtils.isEmpty(activityEvent.getActivityName()) ? activityEvent.getActivityId() : activityEvent.getActivityName())
|
||||
.nodeType(activityEvent.getActivityType())
|
||||
.status(status)
|
||||
.startTime(status == WorkflowNodeInstanceStatusEnums.RUNNING ? LocalDateTime.now() : null)
|
||||
.endTime(status.isFinalState() ? LocalDateTime.now() : null)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,11 +2,14 @@ package com.qqchen.deploy.backend.workflow.listener.handler;
|
||||
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Flowable事件处理器接口
|
||||
*/
|
||||
public interface IFlowableEventHandler {
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否可以处理该事件
|
||||
*
|
||||
|
||||
@ -11,7 +11,10 @@ import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@ -28,17 +31,12 @@ public class JobEventHandler implements IFlowableEventHandler {
|
||||
@Override
|
||||
public void handle(FlowableEvent event) {
|
||||
String eventType = event.getType().name();
|
||||
// log.info("Processing job event: {}", eventType);
|
||||
|
||||
if (!(event instanceof FlowableEngineEntityEvent entityEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// log.info("Processing job event: {}, jobType: {}", eventType, entityEvent.getType());
|
||||
|
||||
JobEntityImpl job = (JobEntityImpl) entityEvent.getEntity();
|
||||
WorkflowNodeInstanceStatusEnums status = convertToNodeStatus(eventType);
|
||||
|
||||
if (status != null) {
|
||||
// publisher.publishEvent(WorkflowNodeInstanceStatusChangeEvent.builder()
|
||||
// .processInstanceId(job.getProcessInstanceId())
|
||||
|
||||
@ -14,6 +14,9 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class ProcessEventHandler implements IFlowableEventHandler {
|
||||
@ -33,8 +36,6 @@ public class ProcessEventHandler implements IFlowableEventHandler {
|
||||
@Override
|
||||
public void handle(FlowableEvent event) {
|
||||
String eventType = event.getType().name();
|
||||
log.info("Processing event: {}", eventType);
|
||||
|
||||
FlowableProcessEngineEvent processEvent = (FlowableProcessEngineEvent) event;
|
||||
WorkflowInstanceStatusEnums status = convertToWorkflowStatus(eventType);
|
||||
|
||||
@ -59,12 +60,12 @@ public class ProcessEventHandler implements IFlowableEventHandler {
|
||||
case "PROCESS_CREATED" -> WorkflowInstanceStatusEnums.CREATED;
|
||||
case "PROCESS_STARTED" -> WorkflowInstanceStatusEnums.RUNNING;
|
||||
case "PROCESS_COMPLETED" -> WorkflowInstanceStatusEnums.COMPLETED;
|
||||
case "PROCESS_COMPLETED_WITH_ERROR_END_EVENT" -> WorkflowInstanceStatusEnums.FAILED;
|
||||
case "PROCESS_CANCELLED" -> WorkflowInstanceStatusEnums.TERMINATED;
|
||||
case "PROCESS_CANCELLED" -> WorkflowInstanceStatusEnums.FAILED;
|
||||
case "PROCESS_SUSPENDED" -> WorkflowInstanceStatusEnums.SUSPENDED;
|
||||
case "PROCESS_RESUMED" -> WorkflowInstanceStatusEnums.RUNNING;
|
||||
// case "PROCESS_RESUMED" -> WorkflowInstanceStatusEnums.RUNNING;
|
||||
case "PROCESS_COMPLETED_WITH_TERMINATE_END_EVENT" -> WorkflowInstanceStatusEnums.TERMINATED;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,8 +2,12 @@ package com.qqchen.deploy.backend.workflow.repository;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnums;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
@ -22,4 +26,6 @@ public interface IWorkflowDefinitionRepository extends IBaseRepository<WorkflowD
|
||||
boolean existsByKeyAndDeletedFalse(String key);
|
||||
|
||||
Optional<WorkflowDefinition> findByKey(String businessKey);
|
||||
|
||||
Page<WorkflowDefinition> findByStatus(WorkflowStatusEnums workflowStatusEnums, Pageable pageable);
|
||||
}
|
||||
|
||||
@ -21,4 +21,5 @@ public interface IWorkflowInstanceRepository extends IBaseRepository<WorkflowIns
|
||||
*/
|
||||
List<WorkflowInstance> findByBusinessKey(String businessKey);
|
||||
|
||||
List<WorkflowInstance> findTop1ByWorkflowDefinitionIdOrderByCreateTimeDesc(Long workflowDefinitionId);
|
||||
}
|
||||
|
||||
@ -14,4 +14,6 @@ public interface IWorkflowNodeInstanceRepository extends IBaseRepository<Workflo
|
||||
List<WorkflowNodeInstance> findByProcessInstanceId(String processInstanceId);
|
||||
|
||||
WorkflowNodeInstance findByNodeId(String nodeId);
|
||||
|
||||
WorkflowNodeInstance findByProcessInstanceIdAndNodeId(String processInstanceId, String nodeId);
|
||||
}
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
package com.qqchen.deploy.backend.workflow.service;
|
||||
|
||||
import com.qqchen.deploy.backend.framework.service.IBaseService;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowTemplateWithInstancesDTO;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
|
||||
import com.qqchen.deploy.backend.workflow.query.WorkflowDefinitionQuery;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
@ -58,4 +59,7 @@ public interface IWorkflowInstanceService extends IBaseService<WorkflowInstance,
|
||||
void completeInstance(String processInstanceId, Map<String, Object> variables);
|
||||
|
||||
WorkflowInstanceDTO startWorkflow(WorkflowInstanceStartRequest request);
|
||||
|
||||
Page<WorkflowTemplateWithInstancesDTO> findTemplatesWithRecentInstances(WorkflowDefinitionQuery query);
|
||||
|
||||
}
|
||||
|
||||
@ -7,9 +7,12 @@ import com.qqchen.deploy.backend.workflow.converter.WorkflowInstanceConverter;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowDefinitionDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowTemplateWithInstancesDTO;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowInstance;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowInstanceStatusEnums;
|
||||
import com.qqchen.deploy.backend.workflow.enums.WorkflowStatusEnums;
|
||||
import com.qqchen.deploy.backend.workflow.query.WorkflowDefinitionQuery;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
|
||||
import com.qqchen.deploy.backend.workflow.repository.IWorkflowInstanceRepository;
|
||||
import com.qqchen.deploy.backend.workflow.service.IWorkflowDefinitionService;
|
||||
@ -20,7 +23,12 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.engine.HistoryService;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.history.HistoricActivityInstance;
|
||||
import org.flowable.engine.repository.ProcessDefinition;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -30,6 +38,8 @@ import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstance, WorkflowInstanceDTO, Long> implements IWorkflowInstanceService {
|
||||
@ -59,7 +69,7 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
||||
workflowInstance.setProcessDefinitionId(processInstance.getProcessDefinitionId());
|
||||
workflowInstance.setWorkflowDefinitionId(workflowDefinitionId);
|
||||
workflowInstance.setBusinessKey(businessKey);
|
||||
workflowInstance.setStatus(WorkflowInstanceStatusEnums.CREATED);
|
||||
workflowInstance.setStatus(WorkflowInstanceStatusEnums.NOT_STARTED);
|
||||
workflowInstance.setStartTime(LocalDateTime.now());
|
||||
workflowInstanceRepository.save(workflowInstance);
|
||||
// 3. 返回创建结果
|
||||
@ -123,7 +133,7 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
||||
public List<WorkflowInstanceDTO> findByBusinessKey(String businessKey) {
|
||||
return workflowInstanceRepository.findByBusinessKey(businessKey).stream()
|
||||
.map(instance -> getInstanceDetails(instance.getProcessInstanceId()))
|
||||
.collect(Collectors.toList());
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -164,6 +174,26 @@ public class WorkflowInstanceServiceImpl extends BaseServiceImpl<WorkflowInstanc
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<WorkflowTemplateWithInstancesDTO> findTemplatesWithRecentInstances(WorkflowDefinitionQuery query) {
|
||||
Pageable pageable = PageRequest.of(query.getPageNum() - 1, query.getPageSize());
|
||||
Page<WorkflowDefinition> definitionPage = workflowDefinitionRepository.findByStatus(WorkflowStatusEnums.PUBLISHED, pageable);
|
||||
List<WorkflowTemplateWithInstancesDTO> result = definitionPage.getContent().stream().map(definition -> {
|
||||
List<WorkflowInstance> workflowInstances = workflowInstanceRepository.findTop1ByWorkflowDefinitionIdOrderByCreateTimeDesc(definition.getId());
|
||||
WorkflowTemplateWithInstancesDTO workflowTemplateWithInstancesDTO = new WorkflowTemplateWithInstancesDTO();
|
||||
Optional<WorkflowInstance> optional = workflowInstances.stream().findFirst();
|
||||
if (optional.isPresent()) {
|
||||
workflowTemplateWithInstancesDTO.setLastExecutionStatus(optional.get().getStatus());
|
||||
workflowTemplateWithInstancesDTO.setLastExecutionTime(optional.get().getCreateTime());
|
||||
}
|
||||
workflowTemplateWithInstancesDTO.setId(definition.getId());
|
||||
workflowTemplateWithInstancesDTO.setName(definition.getName());
|
||||
workflowTemplateWithInstancesDTO.setBusinessKey(definition.getKey());
|
||||
return workflowTemplateWithInstancesDTO;
|
||||
}).collect(toList());
|
||||
return new PageImpl<>(result, definitionPage.getPageable(), definitionPage.getTotalElements());
|
||||
}
|
||||
|
||||
// private WorkflowInstanceDTO.ActivityInstance convertToActivityInstance(HistoricActivityInstance hai) {
|
||||
// WorkflowInstanceDTO.ActivityInstance ai = new WorkflowInstanceDTO.ActivityInstance();
|
||||
// ai.setId(hai.getId());
|
||||
|
||||
@ -67,7 +67,7 @@ public class WorkflowNodeInstanceServiceImpl extends BaseServiceImpl<WorkflowNod
|
||||
WorkflowNodeInstance nodeInstance = workflowNodeInstanceConverter.eventToEntity(event);
|
||||
WorkflowInstance workflowInstance = workflowInstanceRepository.findByProcessInstanceId(nodeInstance.getProcessInstanceId())
|
||||
.orElseThrow(() -> new RuntimeException("Node instance not found for processInstanceId: " + nodeInstance.getProcessInstanceId()));
|
||||
WorkflowNodeInstance workflowNodeInstance = workflowNodeInstanceRepository.findByNodeId(event.getNodeId());
|
||||
WorkflowNodeInstance workflowNodeInstance = workflowNodeInstanceRepository.findByProcessInstanceIdAndNodeId(workflowInstance.getProcessInstanceId(), event.getNodeId());
|
||||
if (workflowNodeInstance == null) {
|
||||
nodeInstance.setWorkflowInstanceId(workflowInstance.getId());
|
||||
nodeInstance.setWorkflowDefinitionId(workflowInstance.getWorkflowDefinitionId());
|
||||
|
||||
@ -18,6 +18,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* BPMN 模型转换工具
|
||||
*
|
||||
* @author cascade
|
||||
* @date 2024-12-11
|
||||
*/
|
||||
@ -28,7 +29,7 @@ public class BpmnConverter {
|
||||
/**
|
||||
* 将工作流定义图转换为Flowable XML
|
||||
*
|
||||
* @param graph 工作流定义图
|
||||
* @param graph 工作流定义图
|
||||
* @param processKey 流程定义的唯一标识
|
||||
* @return Flowable XML字符串
|
||||
* @throws RuntimeException 当转换失败时抛出
|
||||
@ -40,6 +41,7 @@ public class BpmnConverter {
|
||||
// 创建BPMN模型
|
||||
BpmnModel bpmnModel = new BpmnModel();
|
||||
bpmnModel.setTargetNamespace("http://www.flowable.org/test");
|
||||
bpmnModel.addError(WorkFlowConstants.WORKFLOW_EXEC_ERROR, WorkFlowConstants.WORKFLOW_EXEC_ERROR);
|
||||
|
||||
Process process = new Process();
|
||||
// 确保processKey符合NCName规则
|
||||
@ -59,8 +61,8 @@ public class BpmnConverter {
|
||||
// 通过NodeTypeEnums获取对应的BpmnTypeEnums中定义的实例类型
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends FlowElement> instanceClass = (Class<? extends FlowElement>) NodeTypeEnums.valueOf(node.getCode())
|
||||
.getBpmnType()
|
||||
.getInstance();
|
||||
.getBpmnType()
|
||||
.getInstance();
|
||||
|
||||
// 创建节点实例
|
||||
FlowElement element = instanceClass.getDeclaredConstructor().newInstance();
|
||||
@ -80,7 +82,7 @@ public class BpmnConverter {
|
||||
log.debug("转换连线: from {} to {}", edge.getFrom(), edge.getTo());
|
||||
|
||||
SequenceFlow flow = new SequenceFlow();
|
||||
flow.setId("flow_" + edge.getId().replaceAll("[^a-zA-Z0-9-_.]", "_"));
|
||||
flow.setId("FLOW_" + edge.getId().replaceAll("[^a-zA-Z0-9-_.]", "_"));
|
||||
flow.setName(edge.getName());
|
||||
flow.setSourceRef(idMapping.get(edge.getFrom()));
|
||||
flow.setTargetRef(idMapping.get(edge.getTo()));
|
||||
@ -105,13 +107,14 @@ public class BpmnConverter {
|
||||
}
|
||||
|
||||
private String sanitizeId(String id) {
|
||||
return "node_" + id.replaceAll("[^a-zA-Z0-9-_.]", "_");
|
||||
return "NODE_" + id.replaceAll("[^a-zA-Z0-9-_.]", "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置流程节点的特定属性
|
||||
*
|
||||
* @param element 流程节点元素
|
||||
* @param node 工作流节点定义
|
||||
* @param node 工作流节点定义
|
||||
* @param process 当前流程
|
||||
*/
|
||||
private void configureFlowElement(FlowElement element, WorkflowDefinitionNode node, Process process) {
|
||||
@ -157,7 +160,7 @@ public class BpmnConverter {
|
||||
* 为服务任务添加错误边界事件和错误结束事件
|
||||
* 当服务任务执行失败时,会触发错误边界事件,并流转到错误结束事件
|
||||
*
|
||||
* @param process BPMN流程定义
|
||||
* @param process BPMN流程定义
|
||||
* @param serviceTask 需要添加错误处理的服务任务
|
||||
*/
|
||||
private void addErrorBoundaryEventHandler(Process process, ServiceTask serviceTask) {
|
||||
@ -180,6 +183,7 @@ public class BpmnConverter {
|
||||
private BoundaryEvent createErrorBoundaryEvent(ServiceTask serviceTask) {
|
||||
BoundaryEvent boundaryEvent = new BoundaryEvent();
|
||||
boundaryEvent.setId(WorkFlowConstants.BOUNDARY_EVENT_ERROR_PREFIX + serviceTask.getId());
|
||||
boundaryEvent.setName("边界事件异常");
|
||||
boundaryEvent.setAttachedToRef(serviceTask);
|
||||
boundaryEvent.setAttachedToRefId(serviceTask.getId());
|
||||
boundaryEvent.setCancelActivity(true); // 确保取消原有活动
|
||||
@ -201,14 +205,14 @@ public class BpmnConverter {
|
||||
private EndEvent createErrorEndEvent(ServiceTask serviceTask) {
|
||||
EndEvent errorEndEvent = new EndEvent();
|
||||
errorEndEvent.setId(WorkFlowConstants.END_EVENT_ERROR_PREFIX + serviceTask.getId());
|
||||
|
||||
errorEndEvent.setName("结束事件异常");
|
||||
// 添加终止定义
|
||||
// TerminateEventDefinition terminateEventDefinition = new TerminateEventDefinition();
|
||||
// errorEndEvent.addEventDefinition(terminateEventDefinition);
|
||||
TerminateEventDefinition terminateEventDefinition = new TerminateEventDefinition();
|
||||
errorEndEvent.addEventDefinition(terminateEventDefinition);
|
||||
|
||||
ErrorEventDefinition errorEventDefinition = new ErrorEventDefinition();
|
||||
errorEventDefinition.setErrorCode(WorkFlowConstants.WORKFLOW_EXEC_ERROR);
|
||||
errorEndEvent.addEventDefinition(errorEventDefinition);
|
||||
// ErrorEventDefinition errorEventDefinition = new ErrorEventDefinition();
|
||||
// errorEventDefinition.setErrorCode(WorkFlowConstants.WORKFLOW_EXEC_ERROR);
|
||||
// errorEndEvent.addEventDefinition(errorEventDefinition);
|
||||
|
||||
return errorEndEvent;
|
||||
}
|
||||
@ -223,6 +227,7 @@ public class BpmnConverter {
|
||||
private SequenceFlow createErrorSequenceFlow(BoundaryEvent boundaryEvent, EndEvent errorEndEvent) {
|
||||
SequenceFlow errorFlow = new SequenceFlow();
|
||||
errorFlow.setId(WorkFlowConstants.SEQUENCE_FLOW_ERROR_PREFIX + boundaryEvent.getAttachedToRefId());
|
||||
errorFlow.setName("连接线异常");
|
||||
errorFlow.setSourceRef(boundaryEvent.getId());
|
||||
errorFlow.setTargetRef(errorEndEvent.getId());
|
||||
|
||||
|
||||
@ -473,7 +473,7 @@ CREATE TABLE workflow_instance
|
||||
process_definition_id VARCHAR(64) NOT NULL COMMENT '流程定义ID',
|
||||
workflow_definition_id BIGINT NOT NULL COMMENT '工作流定义ID',
|
||||
business_key VARCHAR(64) NULL COMMENT '业务标识',
|
||||
status VARCHAR(32) NOT NULL COMMENT '实例状态',
|
||||
status VARCHAR(100) NOT NULL COMMENT '实例状态',
|
||||
variables TEXT NULL COMMENT '流程变量(JSON)',
|
||||
start_time DATETIME(6) NULL COMMENT '开始时间',
|
||||
end_time DATETIME(6) NULL COMMENT '结束时间'
|
||||
|
||||
Loading…
Reference in New Issue
Block a user