From 97d61af6c24e80962bf9e57eeec6f19f553dae37 Mon Sep 17 00:00:00 2001
From: wsm <2746563060@侵权。>
Date: Wed, 6 May 2026 08:32:25 +0800
Subject: [PATCH] =?UTF-8?q?xiSpeak-flow=E6=A8=A1=E5=9D=97?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
jeecg-boot-module/jeecg-module-flow/pom.xml | 41 ++++++
.../src/main/java/org/jeecg/Main.java | 17 +++
.../java/org/jeecg/xispeak/XiSpeakConfig.java | 33 +++++
.../java/org/jeecg/xispeak/XiSpeakFlow.java | 129 ++++++++++++++++++
.../listener/AfterSDWApproveHqListener.java | 118 ++++++++++++++++
.../src/main/resources/application-flow.yml | 8 ++
6 files changed, 346 insertions(+)
create mode 100644 jeecg-boot-module/jeecg-module-flow/pom.xml
create mode 100644 jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/Main.java
create mode 100644 jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakConfig.java
create mode 100644 jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java
create mode 100644 jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/listener/AfterSDWApproveHqListener.java
create mode 100644 jeecg-boot-module/jeecg-module-flow/src/main/resources/application-flow.yml
diff --git a/jeecg-boot-module/jeecg-module-flow/pom.xml b/jeecg-boot-module/jeecg-module-flow/pom.xml
new file mode 100644
index 0000000..608b87b
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ jeecg-boot-module
+ org.jeecgframework.boot
+ ${jeecgProjectVersion}
+
+
+ jeecg-module-flow
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+ org.jeecgframework.boot
+ jeecg-boot-base-core
+
+
+ org.jeecgframework.boot
+ jeecg-system-local-api
+
+
+ org.jeecgframework.boot
+ jeecg-boot-module-bpm-flowable
+ 3.8.0
+ compile
+
+
+ org.jeecgframework.boot
+ jeecg-system-biz
+
+
+
+
\ No newline at end of file
diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/Main.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/Main.java
new file mode 100644
index 0000000..28f7a57
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/Main.java
@@ -0,0 +1,17 @@
+package org.jeecg;
+
+//TIP 要运行代码,请按 或
+// 点击装订区域中的 图标。
+public class Main {
+ public static void main(String[] args) {
+ //TIP 当文本光标位于高亮显示的文本处时按
+ // 查看 IntelliJ IDEA 建议如何修正。
+ System.out.printf("Hello and welcome!");
+
+ for (int i = 1; i <= 5; i++) {
+ //TIP 按 开始调试代码。我们已经设置了一个 断点
+ // 但您始终可以通过按 添加更多断点。
+ System.out.println("i = " + i);
+ }
+ }
+}
\ No newline at end of file
diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakConfig.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakConfig.java
new file mode 100644
index 0000000..d9369ef
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakConfig.java
@@ -0,0 +1,33 @@
+package org.jeecg.xispeak;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "flow-biz")
+public class XiSpeakConfig {
+ /**
+ * 对应 yml 中的 xi-speak 节点
+ * Spring 会自动将中划线命名映射为驼峰命名
+ */
+ private XiSpeakProperties xiSpeak;
+
+
+ @Data
+ public static class XiSpeakProperties {
+ /** 对应 bg-dept-id */
+ private String bgDeptId;
+ /** 对应 ld-role-id */
+ private String ldRoleId;
+ /** 对应 need-sdw-approve-code */
+ private String needSdwApproveCode;
+ /** 对应 json-data-key */
+ private String jsonDataKey;
+ /** 对应 impl-dept-key */
+ private String implDeptKey;
+ /** 对应impl-dept-collection-used*/
+ private String implDeptCollectionUsedKey;
+ }
+}
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
new file mode 100644
index 0000000..dd147da
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/XiSpeakFlow.java
@@ -0,0 +1,129 @@
+package org.jeecg.xispeak;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.util.StringUtils;
+import org.flowable.engine.RuntimeService;
+import org.jeecg.common.system.api.ISysBaseAPI;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Component("xiSpeakFlow")
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class XiSpeakFlow {
+
+ private final XiSpeakConfig xiSpeakConfig;
+ private final ISysBaseAPI iSysBaseAPI;
+ private final RuntimeService runtimeService;
+
+ public List getBgDeptLdUserIdList() {
+ // 1. 安全获取配置,防止 NPE
+ XiSpeakConfig.XiSpeakProperties props = xiSpeakConfig.getXiSpeak();
+ if (props == null || !StringUtils.hasText(props.getBgDeptId())) {
+ log.error("流程配置缺失: [flow-biz.xi-speak.bg-dept-id] 未在 YAML 中定义");
+ return Collections.emptyList();
+ }
+
+ // 2. 调用接口获取数据
+ log.info("正在查询部门: {} 下的角色: {} 的用户列表", props.getBgDeptId(), props.getLdRoleId());
+ List userList = iSysBaseAPI.getUsersListByDeptIdAndRoleIdLocalApi(
+ props.getBgDeptId(),
+ props.getLdRoleId()
+ );
+ log.info("查询到的用户为: {}", userList);
+ return userList;
+ }
+
+ public Integer getNeedSdwApproval(Object jsonData) {
+ XiSpeakConfig.XiSpeakProperties props = xiSpeakConfig.getXiSpeak();
+ if (jsonData == null || org.apache.commons.lang.StringUtils.isBlank(props.getNeedSdwApproveCode())) {
+ return 0;
+ }
+ String codeName = props.getNeedSdwApproveCode();
+ if (org.apache.commons.lang.StringUtils.isBlank(codeName)) return 0;
+ try {
+ JSONObject data;
+ if (jsonData instanceof JSONObject) {
+ data = (JSONObject) jsonData;
+ } else if (jsonData instanceof String) {
+ if (org.apache.commons.lang.StringUtils.isBlank((String) jsonData)) {
+ return 0;
+ }
+ data = JSONObject.parseObject((String) jsonData);
+ } else {
+ data = JSONObject.parseObject(JSONObject.toJSONString(jsonData));
+ }
+ log.info("needSdwApproval结果为{}", data.getInteger(codeName));
+ return data == null ? 0 : data.getInteger(codeName);
+ } catch (Exception e) {
+ log.warn("getJsondata parse failed, codeName={}, jsonData={}", codeName, jsonData, e);
+ return 0;
+ }
+ }
+
+ public List getImplDeptLdsList(String deptId) {
+ return iSysBaseAPI.getUsersListByDeptIdAndRoleIdLocalApi(deptId, xiSpeakConfig.getXiSpeak().getLdRoleId());
+ }
+
+ public int getListSize(String jsonData){
+ if (org.apache.commons.lang3.StringUtils.isEmpty(jsonData)) {
+ return 0;
+ }
+ return Math.toIntExact(Arrays.stream(jsonData.split(","))
+ .map(String::trim)
+ .filter(org.apache.commons.lang3.StringUtils::isNotEmpty)
+ .count());
+ }
+
+// List getImplDeptWorker(String JG_LOCAL_PROCESS_ID,String deptId){
+// runtimeService.get
+// }
+
+ public Integer getImplDeptNums(String jsonData) {
+ String codeName = xiSpeakConfig.getXiSpeak().getImplDeptKey();
+ if (jsonData == null || org.apache.commons.lang.StringUtils.isEmpty(codeName)) {
+ return 0;
+ }
+
+ // 1. 统一转成 JSONObject
+ JSONObject json;
+ json = JSON.parseObject((String) jsonData);
+
+ 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 (org.apache.commons.lang.StringUtils.isNotEmpty(str)) {
+ // 使用 split 拆分,并过滤掉空格和空字符串
+ String[] split = str.split(",");
+ for (String s : split) {
+ if (org.apache.commons.lang.StringUtils.isNotEmpty(s.trim())) {
+ resultList.add(s.trim());
+ }
+ }
+ }
+ }
+
+ return resultList.size();
+ }
+}
diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/listener/AfterSDWApproveHqListener.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/listener/AfterSDWApproveHqListener.java
new file mode 100644
index 0000000..9d13a44
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeak/listener/AfterSDWApproveHqListener.java
@@ -0,0 +1,118 @@
+package org.jeecg.modules.extbpm.listener.execution;
+
+import lombok.extern.slf4j.Slf4j;
+import org.jeecg.modules.extbpm.process.common.expression.FlowNodeExpression;
+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.oConvertUtils;
+import org.jeecg.modules.extbpm.process.common.WorkFlowGlobals;
+import org.jeecg.xispeak.XiSpeakConfig;
+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("AfterSDWApproveHqListener")
+@RequiredArgsConstructor(onConstructor_ = @Autowired)
+public class AfterSDWApproveHqListener implements ExecutionListener {
+
+ private static final long serialVersionUID = 1L;
+
+ private final FlowNodeExpression flowNodeExpression;
+ @Autowired
+ private RuntimeService runtimeService;
+
+ private final XiSpeakConfig xiSpeakConfig;
+
+ @Override
+ public void notify(DelegateExecution execution) {
+
+ XiSpeakConfig.XiSpeakProperties props = xiSpeakConfig.getXiSpeak();
+ //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,props.getJsonDataKey());
+
+ if (oConvertUtils.isNotEmpty(jsonDataObj)) {
+ String json_data = jsonDataObj.toString();
+ try {
+ // 2. 调用表达式解析工具获取特定字段的字符串内容
+ String jsonStr = flowNodeExpression.getJsondatastring(json_data, props.getImplDeptKey());
+
+ // 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( props.getImplDeptKey(), 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-module/jeecg-module-flow/src/main/resources/application-flow.yml b/jeecg-boot-module/jeecg-module-flow/src/main/resources/application-flow.yml
new file mode 100644
index 0000000..0feb5e8
--- /dev/null
+++ b/jeecg-boot-module/jeecg-module-flow/src/main/resources/application-flow.yml
@@ -0,0 +1,8 @@
+flow-biz:
+ xi-speak:
+ bg-dept-id: "2044677604785229826"
+ ld-role-id: "2044680793306591234"
+ need-sdw-approve-code: "needSdwApproval"
+ json-data-key: "json_data"
+ impl-dept-key: "implDept"
+ impl-dept-collection-used-key: "impl_dept_id"