From 1b49fdbbe0a4efb56ab786a2bda1427fea36653a Mon Sep 17 00:00:00 2001 From: wsm <2746563060@侵权。> Date: Wed, 27 May 2026 15:57:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(bpm):=20=E6=96=B0=E5=A2=9E=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=E5=8F=98=E9=87=8F=E6=9C=AC=E5=9C=B0=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=8F=8A=E5=AE=8C=E5=96=84=20XiSpeak=20=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 BpmVariableLocalService 流程变量本地服务接口及实现 - XiSpeakFlow 完善流程变量读取逻辑 - ActProcessController 流程控制器功能增强 - FlowNodeExpression 扩展表达式能力 Co-Authored-By: Claude Opus 4.7 --- .../java/org/jeecg/xispeak/XiSpeakFlow.java | 26 ++++++++ .../bpm/controller/ActProcessController.java | 21 +++++++ .../bpm/service/BpmVariableLocalService.java | 13 ++++ .../impl/BpmVariableLocalServiceImpl.java | 59 +++++++++++++++++++ .../common/expression/FlowNodeExpression.java | 37 ++++++++++++ 5 files changed, 156 insertions(+) create mode 100644 jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/service/BpmVariableLocalService.java create mode 100644 jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/service/impl/BpmVariableLocalServiceImpl.java diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java index d0f9857..e4f7b93 100644 --- a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java +++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java @@ -194,6 +194,25 @@ public class XiSpeakFlow { return flowNodeExpression.getSonProcessHqVariableList(execution,xiSpeakConfig.getXiSpeak().getTemImplWorkerKey()).size(); } + /** + * 流程表达式内用法: ${xiSpeakFlow.getIsEnd(execution)} + */ + public int getIsEnd(DelegateExecution execution) { + Object value = flowNodeExpression.getSonProcessHqVariable(execution, "isEnd"); + if (value == null) { + return 0; + } + if (value instanceof Number) { + return ((Number) value).intValue(); + } + try { + return Integer.parseInt(String.valueOf(value)); + } catch (NumberFormatException e) { + log.warn("isEnd变量值无法解析为int: {}", value); + return 0; + } + } + /** * 流程表达式内用法: ${xiSpeakFlow.getTemImplLeaderList(execution)} */ @@ -222,6 +241,13 @@ public class XiSpeakFlow { return flowNodeExpression.getSonProcessHqVariableList(execution,xiSpeakConfig.getXiSpeak().getTemBgLeaderKey()).size(); } + /** + * 流程表达式内用法: ${xiSpeakFlow.getTemBgLeaderListLength(execution)} + */ + public int getImplDeptLeaderIsEnd(DelegateExecution execution) { + return flowNodeExpression.getSonProcessHqVariableList(execution,xiSpeakConfig.getXiSpeak().getTemBgLeaderKey()); + } + /** * 流程表达式内用法: ${xiSpeakFlow.getTemBgLeader(execution)} diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/controller/ActProcessController.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/controller/ActProcessController.java index 5cdcfc2..9febc34 100644 --- a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/controller/ActProcessController.java +++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/controller/ActProcessController.java @@ -19,6 +19,7 @@ import org.jeecg.common.util.RedisUtil; import org.jeecg.common.util.TokenUtils; import org.jeecg.common.util.oConvertUtils; import org.jeecg.modules.bpm.dto.ProcessVariableDTO; +import org.jeecg.modules.bpm.service.BpmVariableLocalService; import org.jeecg.modules.bpm.service.impl.ActProcessService; import org.jeecg.modules.extbpm.process.service.IExtActProcessService; import org.jeecg.modules.extbpm.util.CommonRandomUtil; @@ -57,6 +58,8 @@ public class ActProcessController { @Autowired private TaskService taskService; @Autowired + private BpmVariableLocalService bpmVariableLocalService; + @Autowired private ActProcessService actProcessService; @Autowired private IExtActProcessService extActProcessService; @@ -115,6 +118,24 @@ public class ActProcessController { return Result.OK("保存成功"); } + @PostMapping(value = "/setBpmVariableLocal") + @Operation(summary = "设置局部流程变量(向上追溯2层父级执行实例)") + public Result setBpmVariableLocal(@RequestBody ProcessVariableDTO dto) { + if (dto == null || oConvertUtils.isEmpty(dto.getTaskId())) { + return Result.error("任务ID不能为空"); + } + if (oConvertUtils.isEmpty(dto.getVarField())) { + return Result.error("流程变量名不能为空"); + } + try { + bpmVariableLocalService.setVariableLocal(dto.getTaskId(), dto.getVarField(), dto.getVarVal()); + return Result.OK("保存成功"); + } catch (Exception e) { + log.error("设置BPM局部变量失败", e); + return Result.error("保存失败: " + e.getMessage()); + } + } + @PostMapping("deploy") @Operation(summary = "部署流程文件") public Result deploy(@RequestParam("processFile") MultipartFile file) throws IOException { diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/service/BpmVariableLocalService.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/service/BpmVariableLocalService.java new file mode 100644 index 0000000..bea3c35 --- /dev/null +++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/service/BpmVariableLocalService.java @@ -0,0 +1,13 @@ +package org.jeecg.modules.bpm.service; + +public interface BpmVariableLocalService { + + /** + * 将变量存入向上追溯2层的父级执行实例中(与 AfterImplWorkerTemStoreListener 逻辑一致) + * + * @param taskId 当前任务ID + * @param varField 变量名 + * @param varVal 变量值 + */ + void setVariableLocal(String taskId, String varField, Object varVal); +} diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/service/impl/BpmVariableLocalServiceImpl.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/service/impl/BpmVariableLocalServiceImpl.java new file mode 100644 index 0000000..e8c6b7b --- /dev/null +++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/bpm/service/impl/BpmVariableLocalServiceImpl.java @@ -0,0 +1,59 @@ +package org.jeecg.modules.bpm.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.runtime.Execution; +import org.flowable.task.api.Task; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.modules.bpm.service.BpmVariableLocalService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class BpmVariableLocalServiceImpl implements BpmVariableLocalService { + + private final RuntimeService runtimeService; + private final TaskService taskService; + + @Override + public void setVariableLocal(String taskId, String varField, Object varVal) { + Task task = taskService.createTaskQuery().taskId(taskId).singleResult(); + if (task == null) { + throw new IllegalArgumentException("任务ID不存在: " + taskId); + } + + String executionId = task.getExecutionId(); + + Execution currentExecution = runtimeService.createExecutionQuery() + .executionId(executionId) + .singleResult(); + if (currentExecution == null) { + runtimeService.setVariableLocal(executionId, varField, varVal); + return; + } + + String parentId = currentExecution.getParentId(); + if (oConvertUtils.isEmpty(parentId)) { + runtimeService.setVariableLocal(executionId, varField, varVal); + return; + } + + Execution parentExecution = runtimeService.createExecutionQuery() + .executionId(parentId) + .singleResult(); + if (parentExecution == null) { + runtimeService.setVariableLocal(parentId, varField, varVal); + return; + } + + String grandParentId = parentExecution.getParentId(); + String targetId = oConvertUtils.isNotEmpty(grandParentId) ? grandParentId : parentId; + + runtimeService.setVariableLocal(targetId, varField, varVal); + log.info("BPM局部变量已存入: targetId={}, varField={}, varVal={}", targetId, varField, varVal); + } +} diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/expression/FlowNodeExpression.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/expression/FlowNodeExpression.java index d7344c7..f9a36ad 100644 --- a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/expression/FlowNodeExpression.java +++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/expression/FlowNodeExpression.java @@ -427,6 +427,43 @@ public class FlowNodeExpression { return this.getSonProcessHqVariableList(execution,variableName).size(); } + /** + * 获取子流程原始变量值(不经过 splitUsernames 逗号分割),适用于非列表类型的变量。 + * 遍历逻辑与 getSonProcessHqVariableList 一致。 + */ + public Object getSonProcessHqVariable(DelegateExecution execution, String variableName) { + if (execution == null || StringUtils.isBlank(variableName)) { + return null; + } + try { + String parentExecutionId = execution.getParentId(); + if (StringUtils.isBlank(parentExecutionId)) { + return null; + } + if (execution.hasVariableLocal(variableName)) { + return execution.getVariableLocal(variableName); + } + + List executions = runtimeService.createExecutionQuery() + .processInstanceId(parentExecutionId) + .list(); + for (Execution childExecution : executions) { + if (execution.getId().equals(childExecution.getId())) { + continue; + } + Object localVariable = runtimeService.getVariableLocal(childExecution.getId(), variableName); + if (localVariable != null) { + return localVariable; + } + } + return execution.getVariable(variableName); + } catch (Exception e) { + log.warn("getSonProcessHqVariable failed, executionId={}, variableName={}", + execution.getId(), variableName, e); + return null; + } + } + public String getSonProcessHqVariableString(DelegateExecution execution, String variableName) { if(execution == null || StringUtils.isBlank(variableName) || getSonProcessHqVariableList(execution, variableName).isEmpty()) return StringUtils.EMPTY; return getSonProcessHqVariableList(execution, variableName).get(0);