From 871696d50ace248887ed78a8f7134e6dee5b823c Mon Sep 17 00:00:00 2001 From: "zangch@mesnac.com" Date: Wed, 14 Jan 2026 18:06:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(workflow):=20=E4=BB=8Ehwbm=E7=9A=84?= =?UTF-8?q?=E6=96=B0=E7=89=88=E6=9C=ACplus=E6=A1=86=E6=9E=B6=E4=B8=AD?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E5=A4=8D=E5=88=B6=EF=BC=8C=E6=9C=AA=E7=BB=8F?= =?UTF-8?q?=E8=BF=87=E6=B5=8B=E8=AF=95=EF=BC=81=E6=96=B0=E5=A2=9E=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=AE=9E=E4=BE=8B=E4=B8=9A=E5=8A=A1=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=92=8C=E5=90=AF=E5=8A=A8=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E5=B9=B6=E6=8E=A8=E8=BF=9B=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 FlowInstanceBizExt 类作为流程实例业务扩展对象 - 在 RemoteStartProcess 中添加 bizExt 字段用于业务扩展信息 - 在 StartProcessBo 中添加 bizExt 字段用于业务扩展信息 - 新增 startCompleteTask 方法实现启动流程并自动推进到下一节点 - 实现 getCurrentTaskIdByInstanceId 方法根据流程实例ID获取当前活跃任务 - 通过独立事务机制避免数据库死锁问题 - 添加完整的注释文档说明新功能的使用场景和核心机制 --- .../workflow/api/RemoteWorkflowService.java | 26 +++- .../api/domain/RemoteStartProcess.java | 17 +++ .../workflow/domain/FlowInstanceBizExt.java | 60 ++++++++ .../workflow/domain/bo/StartProcessBo.java | 17 +++ .../dubbo/RemoteWorkflowServiceImpl.java | 6 + .../workflow/service/WorkflowService.java | 17 ++- .../service/impl/WorkflowServiceImpl.java | 143 +++++++++++++++++- 7 files changed, 275 insertions(+), 11 deletions(-) create mode 100644 ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java diff --git a/ruoyi-api/hwmom-api-workflow/src/main/java/org/dromara/workflow/api/RemoteWorkflowService.java b/ruoyi-api/hwmom-api-workflow/src/main/java/org/dromara/workflow/api/RemoteWorkflowService.java index 31a2b162..9995f186 100644 --- a/ruoyi-api/hwmom-api-workflow/src/main/java/org/dromara/workflow/api/RemoteWorkflowService.java +++ b/ruoyi-api/hwmom-api-workflow/src/main/java/org/dromara/workflow/api/RemoteWorkflowService.java @@ -89,7 +89,7 @@ public interface RemoteWorkflowService { * 办理任务 * 系统后台发起审批 无用户信息 需要忽略权限 * completeTask.getVariables().put("ignore", true); - * + * * @param taskId 任务ID * @param message 办理意见 */ @@ -98,7 +98,7 @@ public interface RemoteWorkflowService { /** * 【新增方法】根据业务ID获取当前活跃任务ID * 用于润滑工单完成时获取正确的任务ID - * + * * @param businessId 业务ID * @return 当前活跃任务ID,如果没有找到返回null */ @@ -106,10 +106,30 @@ public interface RemoteWorkflowService { /** * 【新增方法】根据流程实例ID获取当前活跃任务ID - * + * * @param instanceId 流程实例ID * @return 当前活跃任务ID,如果没有找到返回null */ Long getCurrentTaskIdByInstanceId(Long instanceId); + /** + * 从hwbm的新版本plus框架中直接复制,未经过测试 + * 【新增方法】启动流程并自动推进到下一节点 + *

+ * 此方法将 startWorkFlow() 和 completeTask() 合并为一次调用, + * 但在内部使用两个独立的事务执行,避免数据库死锁。 + *

+ *

+ * 适用场景: + *

+ *

+ * + * @param startProcess 启动流程参数(包含 flowCode, businessId, variables 等) + * @return 结果 true=成功, false=失败 + */ + boolean startCompleteTask(RemoteStartProcess startProcess); + } diff --git a/ruoyi-api/hwmom-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteStartProcess.java b/ruoyi-api/hwmom-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteStartProcess.java index f7f12a53..3c53ec9b 100644 --- a/ruoyi-api/hwmom-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteStartProcess.java +++ b/ruoyi-api/hwmom-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteStartProcess.java @@ -1,6 +1,7 @@ package org.dromara.workflow.api.domain; +import cn.hutool.core.util.ObjectUtil; import lombok.Data; import java.io.Serial; @@ -35,6 +36,13 @@ public class RemoteStartProcess implements Serializable { */ private Map variables; + /** + * 从hwbm的新版本plus框架中直接复制,未经过测试 + * 业务扩展对象 + * 包含业务相关的扩展信息,如业务编号、办理人等 + */ + private Object bizExt; + public Map getVariables() { if (variables == null) { return new HashMap<>(16); @@ -42,4 +50,13 @@ public class RemoteStartProcess implements Serializable { variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); return variables; } + + //从hwbm的新版本plus框架中直接复制,未经过测试 + public Object getBizExt() { + if (ObjectUtil.isNull(bizExt)) { + bizExt = new Object(); + } + return bizExt; + } + } diff --git a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java new file mode 100644 index 00000000..283a483f --- /dev/null +++ b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/domain/FlowInstanceBizExt.java @@ -0,0 +1,60 @@ +package org.dromara.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; + +/** + * 从hwbm的新版本plus框架中直接复制,未经过测试! + * 流程实例业务扩展对象 flow_instance_biz_ext + * + * @author may + * @date 2025-08-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("flow_instance_biz_ext") +public class FlowInstanceBizExt extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 流程实例ID + */ + private Long instanceId; + + /** + * 业务ID + */ + private String businessId; + + /** + * 业务编码 + */ + private String businessCode; + + /** + * 业务标题 + */ + private String businessTitle; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java index ea21a81e..eb8b64d4 100644 --- a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java +++ b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java @@ -1,6 +1,7 @@ package org.dromara.workflow.domain.bo; +import cn.hutool.core.util.ObjectUtil; import jakarta.validation.constraints.NotBlank; import lombok.Data; import org.dromara.common.core.validate.AddGroup; @@ -39,6 +40,13 @@ public class StartProcessBo implements Serializable { */ private Map variables; + /** + * 从hwbm的新版本plus框架中直接复制,未经过测试 + * 业务扩展对象 + * 包含业务相关的扩展信息,如业务编号、办理人等 + */ + private Object bizExt; + public Map getVariables() { if (variables == null) { return new HashMap<>(16); @@ -46,4 +54,13 @@ public class StartProcessBo implements Serializable { variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); return variables; } + + + //从hwbm的新版本plus框架中直接复制,未经过测试 + public Object getBizExt() { + if (ObjectUtil.isNull(bizExt)) { + bizExt = new Object(); + } + return bizExt; + } } diff --git a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/dubbo/RemoteWorkflowServiceImpl.java b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/dubbo/RemoteWorkflowServiceImpl.java index bf4a3b37..a8a5391e 100644 --- a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/dubbo/RemoteWorkflowServiceImpl.java +++ b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/dubbo/RemoteWorkflowServiceImpl.java @@ -83,4 +83,10 @@ public class RemoteWorkflowServiceImpl implements RemoteWorkflowService { return workflowService.getCurrentTaskIdByInstanceId(instanceId); } + //从hwbm的新版本plus框架中直接复制,未经过测试 + @Override + public boolean startCompleteTask(RemoteStartProcess startProcess) { + return workflowService.startCompleteTask(startProcess); + } + } diff --git a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/service/WorkflowService.java b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/service/WorkflowService.java index 746046d3..e54341bc 100644 --- a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/service/WorkflowService.java +++ b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/service/WorkflowService.java @@ -95,7 +95,7 @@ public interface WorkflowService { /** * 【新增方法】根据业务ID获取当前活跃任务ID * 用于润滑工单完成时获取正确的任务ID - * + * * @param businessId 业务ID * @return 当前活跃任务ID,如果没有找到返回null */ @@ -103,10 +103,23 @@ public interface WorkflowService { /** * 【新增方法】根据流程实例ID获取当前活跃任务ID - * + * * @param instanceId 流程实例ID * @return 当前活跃任务ID,如果没有找到返回null */ Long getCurrentTaskIdByInstanceId(Long instanceId); + /** + * 从hwbm的新版本plus框架中直接复制,未经过测试! + * 【新增方法】启动流程并自动推进到下一节点 + *

+ * 此方法将 startWorkFlow() 和 completeTask() 合并为一次调用, + * 但在内部使用两个独立的事务执行,避免数据库死锁。 + *

+ * + * @param startProcess 启动流程参数(包含 flowCode, businessId, variables 等) + * @return 结果 true=成功, false=失败 + */ + boolean startCompleteTask(RemoteStartProcess startProcess); + } diff --git a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java index f9a04515..252aef2c 100644 --- a/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java +++ b/ruoyi-modules/hwmom-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java @@ -4,12 +4,15 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StringUtils; import org.dromara.warm.flow.orm.entity.FlowInstance; import org.dromara.workflow.api.domain.RemoteCompleteTask; import org.dromara.workflow.api.domain.RemoteStartProcess; import org.dromara.workflow.api.domain.RemoteStartProcessReturn; import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.common.enums.MessageTypeEnum; +import org.dromara.workflow.domain.FlowInstanceBizExt; import org.dromara.workflow.domain.bo.CompleteTaskBo; import org.dromara.workflow.domain.bo.StartProcessBo; import org.dromara.workflow.service.IFlwDefinitionService; @@ -17,7 +20,10 @@ import org.dromara.workflow.service.IFlwInstanceService; import org.dromara.workflow.service.IFlwTaskService; import org.dromara.workflow.service.WorkflowService; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; @@ -154,7 +160,7 @@ public class WorkflowServiceImpl implements WorkflowService { /** * 【新增方法】根据业务ID获取当前活跃任务ID * 用于润滑工单完成时获取正确的任务ID - * + * * @param businessId 业务ID * @return 当前活跃任务ID,如果没有找到返回null */ @@ -165,14 +171,14 @@ public class WorkflowServiceImpl implements WorkflowService { if (instanceId == null) { return null; } - + // 通过流程实例ID获取当前活跃任务ID return getCurrentTaskIdByInstanceId(instanceId); } /** * 【新增方法】根据流程实例ID获取当前活跃任务ID - * + * * @param instanceId 流程实例ID * @return 当前活跃任务ID,如果没有找到返回null */ @@ -181,16 +187,16 @@ public class WorkflowServiceImpl implements WorkflowService { if (instanceId == null) { return null; } - + try { // 获取该流程实例下的所有活跃任务 List activeTasks = flwTaskService.selectByInstId(instanceId); - + // 过滤出当前活跃的任务(del_flag=0) Optional activeTask = activeTasks.stream() .filter(task -> "0".equals(task.getDelFlag())) .findFirst(); - + return activeTask.map(org.dromara.warm.flow.orm.entity.FlowTask::getId).orElse(null); } catch (Exception e) { log.error("获取当前活跃任务失败,instanceId: {}", instanceId, e); @@ -198,4 +204,129 @@ public class WorkflowServiceImpl implements WorkflowService { } } + /** + * 【新增方法】启动流程并自动推进到下一节点 + *

+ * 此方法将 startWorkFlow() 和 completeTask() 合并为一次调用, + * 但在内部使用两个独立的事务执行,避免数据库死锁。 + *

+ *

+ * 核心机制: + *

    + *
  • 1. 启动流程(事务1):创建 flow_instance 和 flow_task
  • + *
  • 2. 提交事务1,释放数据库锁
  • + *
  • 3. 推进流程(事务2):完成当前任务,创建下一节点任务
  • + *
+ *

+ * + * @param startProcess 启动流程参数 + * @return 结果 true=成功, false=失败 + */ +// @Override +// public boolean startCompleteTask(RemoteStartProcess startProcess) { +// try { +// // 阶段1:启动流程(独立事务) +// // startWorkFlow() 方法已有 @Transactional 注解,会在独立事务中执行 +// RemoteStartProcessReturn startResult = flwTaskService.startWorkFlow( +// BeanUtil.toBean(startProcess, StartProcessBo.class) +// ); +// +// if (startResult == null || startResult.getTaskId() == null) { +// log.error("启动工作流失败,flowCode: {}, businessId: {}", +// startProcess.getFlowCode(), startProcess.getBusinessId()); +// return false; +// } +// +// Long taskId = startResult.getTaskId(); +// +// // 阶段1事务已提交,数据库锁已释放 +// +// // 阶段2:推进流程(新独立事务) +// // 使用 REQUIRES_NEW 确保在新事务中执行,避免与阶段1的事务冲突 +// return completeTaskInNewTransaction(taskId, startProcess); +// +// } catch (Exception e) { +// log.error("startCompleteTask 执行异常,flowCode: {}, businessId: {}, error: {}", +// startProcess.getFlowCode(), startProcess.getBusinessId(), e.getMessage(), e); +// return false; +// } +// } + + /** + * 在新事务中推进流程 + *

+ * 使用 REQUIRES_NEW 传播级别,确保在新事务中执行, + * 避免与 startWorkFlow() 的事务产生死锁。 + *

+ * + * @param taskId 任务ID + * @param startProcess 启动参数(包含 variables) + * @return 结果 true=成功, false=失败 + */ + @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) + protected boolean completeTaskInNewTransaction(Long taskId, RemoteStartProcess startProcess) { + try { + RemoteCompleteTask completeTask = new RemoteCompleteTask(); + completeTask.setTaskId(taskId); + + // 传递 variables(如果有) + // variables 可以包含流程分支条件、message、messageType、notice 等参数 + if (startProcess.getVariables() != null && !startProcess.getVariables().isEmpty()) { + completeTask.setVariables(startProcess.getVariables()); + } + + // 调用 completeTask(在新事务中) + boolean result = flwTaskService.completeTask(BeanUtil.toBean(completeTask, CompleteTaskBo.class)); + + if (result) { + log.info("工作流启动并推进成功,flowCode: {}, businessId: {}, taskId: {}", + startProcess.getFlowCode(), startProcess.getBusinessId(), taskId); + } else { + log.warn("工作流推进失败,flowCode: {}, businessId: {}, taskId: {}", + startProcess.getFlowCode(), startProcess.getBusinessId(), taskId); + } + + return result; + + } catch (Exception e) { + log.error("completeTaskInNewTransaction 执行异常,taskId: {}, businessId: {}, error: {}", + taskId, startProcess.getBusinessId(), e.getMessage(), e); + throw e; // 重新抛出异常,触发事务回滚 + } + } + + /** + * 启动流程并办理第一个任务 + * 从hwbm的新版本plus框架中直接复制,未经过测试 + * + * @param startProcess 参数 + */ + @Override + public boolean startCompleteTask(RemoteStartProcess startProcess) { + try { + StartProcessBo processBo = new StartProcessBo(); + processBo.setBusinessId(startProcess.getBusinessId()); + processBo.setFlowCode(startProcess.getFlowCode()); + processBo.setVariables(startProcess.getVariables()); +// processBo.setHandler(startProcess.getHandler()); + processBo.setBizExt(BeanUtil.toBean(startProcess.getBizExt(), FlowInstanceBizExt.class)); + + RemoteStartProcessReturn result = flwTaskService.startWorkFlow(processBo); + CompleteTaskBo taskBo = new CompleteTaskBo(); + taskBo.setTaskId(result.getTaskId()); + taskBo.setMessageType(Collections.singletonList(MessageTypeEnum.SYSTEM_MESSAGE.getCode())); + taskBo.setVariables(startProcess.getVariables()); +// taskBo.setHandler(startProcess.getHandler()); + + boolean flag = flwTaskService.completeTask(taskBo); + if (!flag) { + throw new ServiceException("流程发起异常"); + } + return true; + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + + }