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"