From 2deaf52ddf73d3d3f9818574d9e49e40899edd8e Mon Sep 17 00:00:00 2001 From: wsm <2746563060@侵权。> Date: Wed, 6 May 2026 08:33:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=8A=A0=E7=9B=91=E5=90=AC=E5=99=A8?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E6=B5=81=E7=A8=8B=E8=A1=A8=E8=BE=BE=E5=BC=8F?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../EmbeddedSubProcessHqStartListener.java | 117 ++++++++++++++ .../SubProcessSelectUserListener.java | 61 +++++++ .../process/common/WorkFlowGlobals.java | 6 + .../common/expression/FlowNodeExpression.java | 153 +++++++++++++++++- 4 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/EmbeddedSubProcessHqStartListener.java create mode 100644 jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/SubProcessSelectUserListener.java diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/EmbeddedSubProcessHqStartListener.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/EmbeddedSubProcessHqStartListener.java new file mode 100644 index 0000000..8027061 --- /dev/null +++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/EmbeddedSubProcessHqStartListener.java @@ -0,0 +1,117 @@ +package org.jeecg.modules.extbpm.listener.execution; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtil; +import org.jeecg.modules.extbpm.process.common.expression.FlowNodeExpression; +import com.alibaba.fastjson.JSON; +import lombok.RequiredArgsConstructor; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ExecutionListener; +import org.jeecg.common.util.SpringContextUtils; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Component("embeddedSubProcessHqStartListener") +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class EmbeddedSubProcessHqStartListener implements ExecutionListener { + + private static final long serialVersionUID = 1L; + + private final FlowNodeExpression flowNodeExpression; + @Autowired + private RuntimeService runtimeService; + + @Override + public void notify(DelegateExecution execution) { + + //RuntimeService runtimeService = SpringContextUtils.getBean(RuntimeService.class); + String mainProcessId = (String)execution.getProcessInstanceId(); + String url = (String)execution.getVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL); + + // 打印主流程里所有的变量名,看看有没有 json_data + Map variables = runtimeService.getVariables(execution.getRootProcessInstanceId()); + log.info("主流程中的所有变量名: " + variables.keySet()); + +// 打印当前执行流的所有变量名 + log.info("当前执行流的所有变量名: " + execution.getVariables().keySet()); + + String bizTitle = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_BIZ_TITLE); + + if(org.apache.commons.lang.StringUtil.isEmpty(url)) { + url = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_CONTENT_URL); + String mobileUrl = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE); + execution.setVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL, url); + execution.setVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL_MOBILE, mobileUrl); + } + + // 1. 获取主流程中的 json_data 对象(可能是 String 或 JSONObject) + Object jsonDataObj = (String)runtimeService.getVariable(mainProcessId,"json_data"); + + if (oConvertUtils.isNotEmpty(jsonDataObj)) { + String json_data = jsonDataObj.toString(); + try { + // 2. 调用表达式解析工具获取特定字段的字符串内容 + String jsonStr = flowNodeExpression.getJsondatastring(json_data, "implDept"); + + // 3. 只有当字段存在且内容不为空时,才进行解析 + if (oConvertUtils.isNotEmpty(jsonStr)) { + List deptIds = Arrays.stream(jsonStr.split(",")) + .map(String::trim) // 去掉可能存在的空格 + .filter(s -> !s.isEmpty()) // 过滤掉空字符串 + .collect(Collectors.toList()); + + if (deptIds != null && !deptIds.isEmpty()) { + // 4. 将提取出的 List 存入子流程变量(供多实例 Collection 使用) + execution.setVariable("pending_depts_id", deptIds); + log.info("子流程变量 deptIdList 注入成功: " + deptIds); + } + } + } catch (Exception e) { + log.error("解析 JSON 字段 pending_depts_id 失败: ", e); + } + } + + //获取主表数据id + String businessKey = (String)runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DATA_ID); + //获取主表的设计表单数据ID + String BPM_DES_DATA_ID = oConvertUtils.getString(runtimeService.getVariable(mainProcessId, WorkFlowGlobals.BPM_DES_DATA_ID)); + + //获取主表表名 + String tableName = (String)runtimeService.getVariable(mainProcessId,WorkFlowGlobals.BPM_FORM_KEY); + execution.setVariable(WorkFlowGlobals.BPM_FORM_KEY, tableName); + + //--update--begin------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题--------- + //log.info("----------传入子流程的数据ID--------------: "+execution.getVariable(WorkFlowGlobals.DATA_ID)); + if(oConvertUtils.isNotEmpty(execution.getVariable(WorkFlowGlobals.DATA_ID))){ + execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID)); + //如果是设计器表单,则还需要设置设计表单数据ID + if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){ + execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, execution.getVariable(WorkFlowGlobals.DATA_ID)); + } + }else{ + //未传入id值,则获取主表数据id给子流程【online表单需要】 + execution.setVariable(WorkFlowGlobals.BPM_DATA_ID, businessKey); + //如果是设计器表单,则还需要设置设计表单数据ID + if(oConvertUtils.isNotEmpty(BPM_DES_DATA_ID)){ + execution.setVariable(WorkFlowGlobals.BPM_DES_DATA_ID, BPM_DES_DATA_ID); + } + } + //--update--end------author:scott-----date:20210512-----for:出差借款子流程,加载表单数据为空问题--------- + + //子流程实例set业务号和主流程保持一致 + runtimeService.updateBusinessKey(execution.getProcessInstanceId(), businessKey); + } + + + + +} diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/SubProcessSelectUserListener.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/SubProcessSelectUserListener.java new file mode 100644 index 0000000..7a57642 --- /dev/null +++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/listener/execution/SubProcessSelectUserListener.java @@ -0,0 +1,61 @@ +package org.jeecg.modules.extbpm.listener.execution; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ExecutionListener; +import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals; +import org.jeecg.modules.extbpm.process.common.expression.FlowNodeExpression; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@Slf4j +@Component("subProcessSelectUserListener") +@RequiredArgsConstructor(onConstructor_ = @Autowired) +public class SubProcessSelectUserListener implements ExecutionListener { + + private final FlowNodeExpression flowNodeExpression; + + @Autowired + private RuntimeService runtimeService; + + @Override + public void notify(DelegateExecution execution) { + + String mainProcessId = (String) execution.getProcessInstanceId(); + String url = (String)execution.getVariable(WorkFlowGlobals.BPM_FORM_CONTENT_URL); + + // 打印主流程里所有的变量名,看看有没有 json_data + Map variables = runtimeService.getVariables(execution.getRootProcessInstanceId()); + log.info("主流程中的所有变量名: " + variables.keySet()); + + // 打印当前执行流的所有变量名 + log.info("当前执行流的所有变量名: " + execution.getVariables().keySet()); + + // 1. 获取原生组件选中的处理人 (假设 Key 为 assigneeUserIdList) + Object selectedValue = execution.getVariable("assigneeUserIdList"); + + List list = new ArrayList<>(); + if (selectedValue instanceof String) { + // 如果是逗号分隔的字符串,拆分 + list = Arrays.asList(((String) selectedValue).split(",")); + } else if (selectedValue instanceof List) { + list = (List) selectedValue; + } + + // 2. 如果没选人,给个默认值防止报错,或者抛出业务异常 + if (list.isEmpty()) { + throw new RuntimeException("请选择下一步处理人!"); + } + + // 3. 关键:将这个 List 存入局部变量,供下一节点的 Multi-instance 读取 + // 使用 setVariableLocal 确保并行子流程互不干扰 + execution.setVariableLocal("internal_collection", list); + } +} diff --git a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/WorkFlowGlobals.java b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/WorkFlowGlobals.java index 5c01be9..ca0ec91 100644 --- a/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/WorkFlowGlobals.java +++ b/jeecg-boot-platform/jeecg-boot-module-bpm-flowable/src/main/java/org/jeecg/modules/extbpm/process/common/WorkFlowGlobals.java @@ -201,4 +201,10 @@ public final class WorkFlowGlobals { * 流程信号启动类型:按钮触发 */ public static String START_BUTTON_EVENT = "buttonEvent"; + + /** + * 用于加载json_data字段的键名 + */ + public static String JSON_DATA = "json_data"; + public static String PENDING_DEPTS_ID = "pending_depts_id"; } 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 7eeb5ee..39dcde2 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 @@ -1,6 +1,7 @@ package org.jeecg.modules.extbpm.process.common.expression; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -75,6 +76,144 @@ public class FlowNodeExpression { } } + /** + * 根据字段名称在json_data内查询对应list或set的长度 + * ${flowNodeExpression.getJsonDataSizeByCodeName(json_data,"pending_depts_id")} + * + * @param jsonData + * @param codeName + * @return + */ + public Integer getJsonDataSizeByCodeNameStr(Object jsonData, String codeName){ + if (jsonData == null || StringUtils.isEmpty(codeName)) { + return 0; + } + + // 1. 统一转成 JSONObject + JSONObject json; + if (jsonData instanceof String) { + json = JSON.parseObject((String) jsonData); + } else if (jsonData instanceof JSONObject) { + json = (JSONObject) jsonData; + } else { + return 0; + } + + if (json == null || !json.containsKey(codeName)) { + return 0; + } + + // 2. 获取原始对象进行兼容性处理 + Object value = json.get(codeName); + if (value == null) { + return 0; + } + + List resultList = new ArrayList<>(); + + if (value instanceof Collection) { + // 情况 A: 本身就是集合/JSON数组 + resultList.addAll(json.getJSONArray(codeName).toJavaList(String.class)); + } else if (value instanceof String) { + // 情况 B: 是逗号分隔的字符串 "ID1,ID2,ID3" + String str = (String) value; + if (StringUtils.isNotEmpty(str)) { + // 使用 split 拆分,并过滤掉空格和空字符串 + String[] split = str.split(","); + for (String s : split) { + if (StringUtils.isNotEmpty(s.trim())) { + resultList.add(s.trim()); + } + } + } + } + + return resultList.size(); + } + + /** + * 根据字段名称在json_data内查询对应list或set的长度 + * ${flowNodeExpression.getJsonDataSizeByCodeName(json_data,"pending_depts_id")} + * + * @param jsonData + * @param codeName + * @return + */ + public Integer getJsonDataSizeByCodeNameObj(Object jsonData, String codeName){ + if (jsonData == null || StringUtils.isEmpty(codeName)) { + return 0; + } + + // 1. 统一转成 JSONObject + JSONObject json; + if (jsonData instanceof String) { + json = JSON.parseObject((String) jsonData); + } else if (jsonData instanceof JSONObject) { + json = (JSONObject) jsonData; + } else { + return 0; + } + + if (json == null || !json.containsKey(codeName)) { + return 0; + } + + // 2. 获取原始对象进行兼容性处理 + Object value = json.get(codeName); + if (value == null) { + return 0; + } + + List resultList = new ArrayList<>(); + + if (value instanceof Collection) { + // 情况 A: 本身就是集合/JSON数组 + resultList.addAll(json.getJSONArray(codeName).toJavaList(String.class)); + } else if (value instanceof String) { + // 情况 B: 是逗号分隔的字符串 "ID1,ID2,ID3" + String str = (String) value; + if (StringUtils.isNotEmpty(str)) { + // 使用 split 拆分,并过滤掉空格和空字符串 + String[] split = str.split(","); + for (String s : split) { + if (StringUtils.isNotEmpty(s.trim())) { + resultList.add(s.trim()); + } + } + } + } + + return resultList.size(); + } + + /** + * 优雅地从 JSON 对象中提取 List + */ + private List getListFromJsonByCodeName(JSONObject json, String key) { + if (json == null || !json.containsKey(key)) { + return Collections.emptyList(); + } + Object value = json.get(key); + if (value == null) { + return Collections.emptyList(); + } + + // 如果本身就是 JSONArray(Collection 子类) + if (value instanceof Collection) { + return ((JSONArray) value).toJavaList(String.class); + } + + // 如果是逗号分隔的字符串 + if (value instanceof String && StringUtils.isNotBlank((String) value)) { + return Arrays.stream(((String) value).split(",")) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()); + } + + return Collections.emptyList(); + } + /** * 根据字段名称取json_data查询对应业务表单的数据 * ${flowNodeExpression.getJsondatastring(jsonData,'supDeptleaderid')} @@ -84,7 +223,19 @@ public class FlowNodeExpression { * @return roleId */ public List getUsersListByJson(Object jsonData, String deptKeyName, String roleId) { - return ISysBaseAPI.getUsersListByJsonLocalAPI(jsonData,deptKeyName,roleId); + return ISysBaseAPI.getUsersListByJsonLocalAPI(jsonData, deptKeyName, roleId); + } + + /** + * 根据部门id角色id获取用户列表 + * ${flowNodeExpression.getUsersListByDeptIdAndRoleId(deptId,roleId)} + * + * @param deptId + * @param roleId + * @return roleId + */ + public List getUsersListByDeptIdAndRoleId(String deptId, String roleId) { + return sysbase.getUsersListByDeptIdAndRoleIdLocalApi(deptId, roleId); } /**