反序列化问题。

This commit is contained in:
dengqichen 2024-12-17 16:49:03 +08:00
parent d298a8770c
commit 45e6d70ca4
22 changed files with 266 additions and 292 deletions

View File

@ -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;

View File

@ -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,6 +222,11 @@
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.flowable</groupId>-->
<!-- <artifactId>flowable-bpmn-model</artifactId>-->
<!-- <version>${flowable.version}</version> &lt;!&ndash; 使用您项目中的版本 &ndash;&gt;-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.flowable</groupId>-->
<!-- <artifactId>flowable-spring-boot-starter-rest</artifactId>-->
<!-- <version>${flowable.version}</version>-->
<!-- </dependency>-->

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -10,4 +10,5 @@ public interface WorkFlowConstants {
String SEQUENCE_FLOW_ERROR_PREFIX = "SEQUENCE_FLOW_ERROR_";
String ASYNC_CONTINUATION = "async-continuation";
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -10,6 +10,8 @@ import lombok.Getter;
@Getter
public enum WorkflowInstanceStatusEnums {
NOT_STARTED("NOT_STARTED", "未开始"),
/**
* 已创建流程实例已创建但还未开始运行
*/

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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()
);
}
}

View File

@ -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 {
/**
* 判断是否可以处理该事件
*

View File

@ -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())

View File

@ -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;
};
}
}

View File

@ -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);
}

View File

@ -21,4 +21,5 @@ public interface IWorkflowInstanceRepository extends IBaseRepository<WorkflowIns
*/
List<WorkflowInstance> findByBusinessKey(String businessKey);
List<WorkflowInstance> findTop1ByWorkflowDefinitionIdOrderByCreateTimeDesc(Long workflowDefinitionId);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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());

View File

@ -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());

View File

@ -18,6 +18,7 @@ import java.util.Map;
/**
* BPMN 模型转换工具
*
* @author cascade
* @date 2024-12-11
*/
@ -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规则
@ -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,11 +107,12 @@ 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 process 当前流程
@ -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());

View File

@ -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 '结束时间'