Browse Source

Revert "回退后管优化查询速度"

This reverts commit c4e7981ce7ccc09d93e16ebabc504e73ef734b94.
cr 15 hours ago
parent
commit
e497d7680b

+ 173 - 166
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreePrivateController.java

@@ -317,201 +317,208 @@ public class WbsTreePrivateController extends BladeController {
         return R.fail("操作失败");
     }
 
-//
-    /**
-     * 查询当前节点下所有表单(根据节点ID查询当前表单)
-     */
     @GetMapping("/get-node-tables")
     @ApiOperationSupport(order = 3)
     @ApiOperation(value = "查询当前节点下所有元素表", notes = "传入父节点id、wbsId、projectId")
-    public R<List<WbsNodeTableVO>> findNodeTableByCondition(@RequestParam("parentId") String parentId,
-                                                            @RequestParam("wbsId") String wbsId,
-                                                            @RequestParam("projectId") String projectId) {
+    public R<List<WbsNodeTableVO>> findNodeTableByCondition(
+            @RequestParam("parentId") String parentId,
+            @RequestParam("wbsId") String wbsId,
+            @RequestParam("projectId") String projectId) {
         List<WbsNodeTableVO> rs = wbsTreePrivateService.selectByNodeTable(parentId, wbsId, projectId);
-        if (rs.size() > 0) {
-            /*表单对应的initTableIdMaps*/
-            Set<String> ids = rs.stream().map(WbsNodeTableVO::getId).collect(Collectors.toSet());
-            Map<Long, Long> initTableIdMaps = wbsTreeService.getBaseMapper().selectList(Wrappers.<WbsTree>lambdaQuery()
-                            .select(WbsTree::getInitTableId, WbsTree::getId)
-                            .in(WbsTree::getId, ids))
-                    .stream()
-                    .filter(wbsTree -> wbsTree.getId() != null && wbsTree.getInitTableId() != null)
-                    .collect(Collectors.toMap(WbsTree::getId, WbsTree::getInitTableId));
-
-            /*元素FidSet,统计数量*/
-            Set<Long> formElementFids = new HashSet<>();
-
-            /*initTabId赋值*/
-            for (WbsNodeTableVO r : rs) {
+        // 仅处理isLinkTable标记(SQL无法处理的逻辑)
+        if (CollectionUtil.isNotEmpty(rs)) {
+            rs.forEach(r -> {
                 if (StringUtil.isBlank(r.getHtmlUrl())) {
                     r.setIsLinkTable(1);
                 }
-                if (StringUtil.isBlank(r.getInitTableId())) {
+            });
+            return R.data(rs);
+        }
+        return R.fail(404, "未查询到数据"); // 200是成功码,失败用404更规范
+    }
 
-                    /*提出去,不for循环查询
-                    WbsTree wbsTree = wbsTreeService.getById(r.getId());*/
+    @GetMapping("/get-group-node-tables")
+    @ApiOperationSupport(order = 3)
+    @ApiOperation(value = "查询当前节点下所有元素表并分类", notes = "传入父节点id、wbsId、projectId")
+    public R<List<WbsTreePrivateTableVO>> findAndGroupNodeTableByCondition(
+            @RequestParam("parentId") String parentId,
+            @RequestParam("wbsId") String wbsId,
+            @RequestParam("projectId") String projectId) {
+        // 2. 获取基础数据(已优化SQL,无冗余)
+        R<List<WbsNodeTableVO>> r = findNodeTableByCondition(parentId, wbsId, projectId);
+        List<WbsNodeTableVO> data = r.getData();
+        if (CollectionUtil.isEmpty(data)) {
+            return R.fail(404, "未查询到数据");
+        }
 
-                    Long initTabId = initTableIdMaps.getOrDefault(Long.parseLong(r.getId()), null);
+        // 3. 批量处理HTML解析(核心优化:批量查询→并行处理)
+        processHtmlElementsBatch(data);
 
-                    if (initTabId != null) {
-                        r.setInitTableId(String.valueOf(initTabId));
+        // 4. 简化分组逻辑(减少重复计算)
+        List<WbsTreePrivateTableVO> list = groupNodeTables(data);
 
-                        formElementFids.add(initTabId);
+        return R.data(list);
+    }
 
-                        /*提出去,不for循环查询
-                        r.setElementTotal(Math.toIntExact(wbsFormElementService.count(
-                                new LambdaQueryWrapper<WbsFormElement>().eq(WbsFormElement::getFId, r.getInitTableId()))));*/
+    /**
+     * 批量处理HTML元素校验(解决N+1查询+串行解析)
+     */
+    private void processHtmlElementsBatch(List<WbsNodeTableVO> data) {
+        // 步骤1:批量收集需要查询的initTableId和pKeyId(避免循环内查询)
+        Map<String, WbsNodeTableVO> initTableId2Vo = new HashMap<>();
+        Set<String> pKeyIds = new HashSet<>();
+        for (WbsNodeTableVO f : data) {
+            String htmlUrl = f.getHtmlUrl();
+            String initTableId = f.getInitTableId();
+            if (StringUtil.isNotBlank(htmlUrl) && StringUtils.isNotEmpty(initTableId)) {
+                initTableId2Vo.put(initTableId, f);
+                pKeyIds.add(f.getPKeyId()+"");
+            }
+        }
+        if (initTableId2Vo.isEmpty()) {
+            return;
+        }
 
-                    } else {
-                        r.setInitTableId("");
+        // 步骤2:批量查询wbsFormElement(替代循环内N+1查询)
+        List<WbsFormElement> wbsFormElements = wbsFormElementService.getBaseMapper().selectList(
+                Wrappers.<WbsFormElement>lambdaQuery()
+                        .in(WbsFormElement::getFId, initTableId2Vo.keySet())
+                        .eq(WbsFormElement::getIsDeleted, 0)
+        );
+        // 构建initTableId→eKey集合的映射
+        Map<String, Set<String>> initTableId2Keys = wbsFormElements.stream()
+                .collect(Collectors.groupingBy(
+                        WbsFormElement::getFId,
+                        Collectors.mapping(WbsFormElement::getEKey, Collectors.toSet())
+                ));
+
+        // 步骤3:批量获取HTML内容(建议对excelHtml结果加本地缓存,比如Caffeine)
+        Map<String, String> pKeyId2Html = new HashMap<>();
+        if (CollectionUtil.isNotEmpty(pKeyIds)) {
+            // 并行获取HTML(如果getExcelHtml是远程调用/耗时操作,并行可提升效率)
+            pKeyIds.parallelStream().forEach(pKeyId -> {
+                try {
+                    R excelHtml = excelTabController.getExcelHtml(Long.parseLong(pKeyId));
+                    if (excelHtml.isSuccess() && excelHtml.getData() != null) {
+                        pKeyId2Html.put(pKeyId, excelHtml.getData().toString());
                     }
+                } catch (Exception e) {
+                    System.out.println("获取HTML失败,pKeyId="+pKeyId); // 增加日志,避免空catch
                 }
-            }
+            });
+        }
 
-            if (formElementFids.size() > 0) {
-                /*查询所有相关的元素记录*/
-                List<WbsFormElement> wbsFormElements = wbsFormElementService.getBaseMapper().selectList(Wrappers.<WbsFormElement>lambdaQuery()
-                        .select(WbsFormElement::getFId)
-                        .in(WbsFormElement::getFId, formElementFids));
+        // 步骤4:并行解析HTML(CPU密集型操作,并行提升效率)
+        data.parallelStream().forEach(f -> {
+            String htmlUrl = f.getHtmlUrl();
+            String initTableId = f.getInitTableId();
+            if (StringUtil.isBlank(htmlUrl) || StringUtils.isEmpty(initTableId)) {
+                return;
+            }
+            // 获取缓存的HTML内容
+            String htmlString = pKeyId2Html.get(f.getPKeyId());
+            if (StringUtil.isEmpty(htmlString)) {
+                return;
+            }
+            // 获取预查询的eKey集合
+            Set<String> keys = initTableId2Keys.getOrDefault(initTableId, Collections.emptySet());
 
-                /*elementCountMap分组*/
-                Map<String, Integer> elementCountMap = wbsFormElements.stream()
-                        .collect(Collectors.groupingBy(WbsFormElement::getFId, Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
+            // Jsoup解析(简化标签选择+批量判断)
+            Document doc = Jsoup.parse(htmlString);
+            String[] tagNames = {"el-input", "el-date-picker", "el-time-picker", "hc-form-select-search",
+                    "hc-table-form-upload", "hc-form-checkbox-group", "el-radio-group", "el-select"};
+            Elements inputs = new Elements();
+            for (String tagName : tagNames) {
+                inputs.addAll(doc.select(tagName));
+            }
 
-                /*元素数量赋值*/
-                for (WbsNodeTableVO r : rs) {
-                    if (StringUtil.isNotBlank(r.getInitTableId())) {
-                        r.setElementTotal(elementCountMap.getOrDefault(r.getInitTableId(), 0));
-                    }
+            // 标记是否有错误(只要有一个标签不合法,就标记为1)
+            boolean hasError = inputs.stream().anyMatch(input -> {
+                String id = input.attr("id");
+                if (StringUtils.isEmpty(id)) {
+                    return true;
                 }
-            }
+                // 预编译分割逻辑,减少重复split(核心优化)
+                String[] idParts = id.split("__", 2); // 只分割一次
+                if (idParts.length != 2) {
+                    return true;
+                }
+                String keyPart = idParts[0];
+                String coordPart = idParts[1];
+                // 1. 检查keyPart格式:key_+数字
+                if (!keyPart.startsWith("key_") || !StringUtils.isNumber(keyPart.replace("key_", ""))) {
+                    return true;
+                }
+                // 2. 检查coordPart格式:数字_数字
+                String[] coordParts = coordPart.split("_", 2);
+                if (coordParts.length != 2 || !StringUtils.isNumber(coordParts[0]) || !StringUtils.isNumber(coordParts[1])) {
+                    return true;
+                }
+                // 3. 检查key是否存在
+                return !keys.contains(keyPart);
+            });
 
-            return R.data(rs);
-        }
-        return R.fail(200, "未查询到数据");
+            if (hasError) {
+                f.setHtmlElementError(1);
+            }
+        });
     }
 
     /**
-     * 查询当前节点下所有表单(根据节点ID查询当前表单)
+     * 简化分组逻辑(减少重复判断
      */
-    @GetMapping("/get-group-node-tables")
-    @ApiOperationSupport(order = 3)
-    @ApiOperation(value = "查询当前节点下所有元素表并分类", notes = "传入父节点id、wbsId、projectId")
-    public R<List<WbsTreePrivateTableVO>> findAndGroupNodeTableByCondition(@RequestParam("parentId") String parentId,
-                                                                           @RequestParam("wbsId") String wbsId,
-                                                                           @RequestParam("projectId") String projectId) {
-        R<List<WbsNodeTableVO>> r = findNodeTableByCondition(parentId, wbsId, projectId);
-        List<WbsNodeTableVO> data = r.getData();
+    private List<WbsTreePrivateTableVO> groupNodeTables(List<WbsNodeTableVO> data) {
         List<WbsTreePrivateTableVO> list = new ArrayList<>();
-        if (data != null && !data.isEmpty()) {
-            //解析html
-            data.forEach(f -> {
-                String htmlUrl = f.getHtmlUrl();
-                String initTableId = f.getInitTableId();
-                if(StringUtil.isNotBlank(htmlUrl) && StringUtils.isNotEmpty(initTableId)){
-                    //获取元素表数据
-                    List<WbsFormElement> wbsFormElements = wbsFormElementService.getBaseMapper().selectList(Wrappers.<WbsFormElement>lambdaQuery()
-                            .eq(WbsFormElement::getFId, initTableId)
-                            .eq(WbsFormElement::getIsDeleted, 0)
-                    );
-
-                    List<String> keys = wbsFormElements.stream().map(WbsFormElement::getEKey).collect(Collectors.toList());
-
-
-                    // 读取html页面信息
-                    String htmlString = "";
-                    //解析html
-                    try {
-                        R excelHtml = excelTabController.getExcelHtml(f.getPKeyId());
-                        htmlString = excelHtml.getData().toString();
-                    } catch (Exception e) {
-                    }
-                    if(StringUtil.isEmpty(htmlString)){
-                        return;
-                    }
-                    // 样式集合
-                    Document doc = Jsoup.parse(htmlString);
-                    //获取所有input标签
-                    //获取所有input标签
-                    String[] tagNames = {"el-input", "el-date-picker", "el-time-picker", "hc-form-select-search",
-                            "hc-table-form-upload", "hc-form-checkbox-group", "el-radio-group", "el-select"};
-                    Elements inputs = new Elements();
-                    for (String tagName : tagNames) {
-                        inputs.addAll(doc.select(tagName));
-                    }
-                    //判断标签是否存在id属性
-                    inputs.forEach(input -> {
-                        String id = input.attr("id");
-                        if(StringUtils.isEmpty(id)){
-                            f.setHtmlElementError(1);
-                            input.attr("htmlElementError", "1");
-                        } else {
-                            /**
-                             * 判断当前元素是否符合格式
-                             * 1、是否存在key_
-                             * 2、是否存在__
-                             * 3、key_后面是否为数字
-                             * 4、__后面是否为坐标
-                             * 5、key是否在元素库中存在
-                             */
-                            if(!id.contains("key_")
-                                    || !id.contains("__")
-                                    || !StringUtils.isNumber(id.split("__")[0].replace("key_",""))
-                                    || !id.split("__")[1].contains("_")
-                                    || !StringUtils.isNumber(id.split("__")[1].split("_")[0])
-                                    || !StringUtils.isNumber(id.split("__")[1].split("_")[1])
-                                    || !keys.contains(id.split("__")[0])
-                            ){
-                                f.setHtmlElementError(1);
-                                input.attr("htmlElementError", "1");
-                            }
-                        }
-                    });
-                }
-            });
+        Integer wbsType = data.get(0).getWbsType();
+        if (wbsType == null) {
+            list.add(new WbsTreePrivateTableVO("", data));
+            return list;
+        }
 
-            Integer wbsType = data.get(0).getWbsType();
-            Map<Integer, List<WbsNodeTableVO>> groupMap;
-            if (wbsType != null && wbsType == 1) {
-                groupMap = data.stream().collect(Collectors.groupingBy(vo -> {
-                    String tableOwner = vo.getTableOwner();
-                    if (StringUtil.isNumeric(tableOwner)) {
-                        int i = Integer.parseInt(tableOwner);
-                        if (i > 0 && i <= 3) {
-                            return 1;
-                        } else if (i > 3 && i <= 6) {
-                            return 2;
-                        }
-                    }
-                    return 3;
-                }));
-            } else if (wbsType != null && wbsType == 2) {
-                groupMap = data.stream().collect(Collectors.groupingBy(vo -> {
-                    String tableType = vo.getTableType();
-                    if (StringUtil.isNumeric(tableType)) {
-                        int i = Integer.parseInt(tableType);
-                        if (i == 1 ) {
-                            return 2;
-                        } else if (i == 2 ) {
-                            return 1;
-                        }
-                    }
-                    return 3;
-                }));
-            } else {
-                list.add(new WbsTreePrivateTableVO("", data));
-                return R.data(list);
-            }
-            String[][] titles = {{"施工方", "监理方", "其他"}, {"报告表", "记录表", "其他"}};
-            for (int i = 1; i <= 3; i++) {
-                List<WbsNodeTableVO> wbsNodeTableVOS = groupMap.get(i);
-                if (wbsNodeTableVOS != null && !wbsNodeTableVOS.isEmpty()) {
-                    list.add(new WbsTreePrivateTableVO(titles[wbsType - 1][i - 1], wbsNodeTableVOS));
+        // 预定义分组规则(避免嵌套if)
+        Function<WbsNodeTableVO, Integer> groupFunction;
+        String[] titles;
+        if (wbsType == 1) {
+            // 施工方/监理方/其他分组
+            groupFunction = vo -> {
+                String tableOwner = vo.getTableOwner();
+                if (StringUtil.isNumeric(tableOwner)) {
+                    int i = Integer.parseInt(tableOwner);
+                    if (i >= 1 && i <= 3) return 1;
+                    if (i >= 4 && i <= 6) return 2;
+                }
+                return 3;
+            };
+            titles = new String[]{"施工方", "监理方", "其他"};
+        } else if (wbsType == 2) {
+            // 报告表/记录表/其他分组
+            groupFunction = vo -> {
+                String tableType = vo.getTableType();
+                if (StringUtil.isNumeric(tableType)) {
+                    int i = Integer.parseInt(tableType);
+                    if (i == 1) return 2;
+                    if (i == 2) return 1;
                 }
+                return 3;
+            };
+            titles = new String[]{"报告表", "记录表", "其他"};
+        } else {
+            list.add(new WbsTreePrivateTableVO("", data));
+            return list;
+        }
+
+        // 分组(流式API简化)
+        Map<Integer, List<WbsNodeTableVO>> groupMap = data.stream()
+                .collect(Collectors.groupingBy(groupFunction));
+
+        // 构建返回列表
+        for (int i = 1; i <= 3; i++) {
+            List<WbsNodeTableVO> voList = groupMap.get(i);
+            if (CollectionUtil.isNotEmpty(voList)) {
+                list.add(new WbsTreePrivateTableVO(titles[i - 1], voList));
             }
-            return R.data(list);
         }
-        return R.fail(200, "未查询到数据");
+        return list;
     }
 
     /**

+ 1 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreePrivateMapper.java

@@ -35,7 +35,7 @@ public interface WbsTreePrivateMapper extends EasyBaseMapper<WbsTreePrivate> {
 
     List<WbsTree> selectIsTable(Long tableId, String projectId);
 
-    List<WbsNodeTableVO> selectByNodeTable(String id, String wbsId, String projectId);
+    List<WbsNodeTableVO> selectByNodeTable(@Param("parentId")String id, @Param("wbsId")String wbsId, @Param("projectId")String projectId);
 
     void updateDeletedByCondition(String id, String wbsId, String projectId);
     void removeTableByPKeyIdCondition(String pKeyId, String wbsId, String projectId);

+ 39 - 32
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreePrivateMapper.xml

@@ -512,43 +512,50 @@
           AND project_id = #{projectId}
     </select>
     <select id="selectByNodeTable" resultType="org.springblade.manager.vo.WbsNodeTableVO">
-        SELECT wt.p_key_id                                                                          AS "pKeyId",
-               wt.id                                                                                AS id,
-               wt.wbs_type                                                                          AS wbsType,
-               wt.node_name                                                                         AS tableName,
-               wt.is_buss_show                                                                      AS isBussShow,
-               IFNULL(if(length(trim(wt.full_name)) > 0, wt.full_name, wt.node_name), wt.node_name) AS fullName,
-               case
-                   when wt.table_type in (1, 9) then 1
-                   when wt.table_type in (2, 10) then 2
-                   else wt.table_type
-                   end                                                                              as tableType,
-               wt.`status`                                                                          AS isCreateTable,
-               wt.table_owner                                                                       AS tableOwner,
-               wt.is_link_table,
-               wt.init_table_name,
-               wt.init_table_id                                                                     as initTableId,
-               wt.excel_id                                                                          AS excelId,
-               wt.sort,
-               wt.status,
-               wt.node_type                                                                         AS nodeType,
-               wt.wbs_type                                                                          AS wbsType,
-               wt.default_conceal                                                                   AS defaultConceal,
-               wt.fill_rate                                                                         AS "fillRate",
-               wt.html_url                                                                          AS htmlUrl,
-               (SELECT count(1)
-                FROM m_wbs_form_element
-                WHERE f_id = wt.init_table_id
-                  and is_deleted = 0)                                                               AS "elementTotal"
-                , (SELECT tab_ch_name from m_table_info where tab_en_name = wt.init_table_name)     As elementTableName
+        SELECT
+            wt.p_key_id AS "pKeyId",
+            wt.id AS id,
+            wt.wbs_type AS wbsType,
+            wt.node_name AS tableName,
+            wt.is_buss_show AS isBussShow,
+            IFNULL(IF(LENGTH(TRIM(wt.full_name)) > 0, wt.full_name, wt.node_name), wt.node_name) AS fullName,
+            CASE
+                WHEN wt.table_type IN (1, 9) THEN 1
+                WHEN wt.table_type IN (2, 10) THEN 2
+                ELSE wt.table_type
+                END AS tableType,
+            wt.`status` AS isCreateTable,
+            wt.table_owner AS tableOwner,
+            wt.is_link_table,
+            wt.init_table_name,
+            -- 关联m_wbs_tree获取init_table_id,避免Java层二次查询
+            COALESCE(wt.init_table_id, wt2.init_table_id) AS initTableId,
+            wt.excel_id AS excelId,
+            wt.sort,
+            wt.status,
+            wt.node_type AS nodeType,
+            wt.wbs_type AS wbsType,
+            wt.default_conceal AS defaultConceal,
+            wt.fill_rate AS "fillRate",
+            wt.html_url AS htmlUrl,
+            -- 预统计元素数量,避免Java层重复查询
+            COALESCE(we.element_count, 0) AS elementTotal
         FROM m_wbs_tree_private AS wt
+                 -- 左关联m_wbs_tree获取init_table_id(原Java层initTableIdMaps逻辑)
+                 LEFT JOIN m_wbs_tree AS wt2 ON wt.id = wt2.id AND wt2.is_deleted = 0
+            -- 预统计每个init_table_id的元素数量,消除行级子查询
+                 LEFT JOIN (
+            SELECT f_id, COUNT(1) AS element_count
+            FROM m_wbs_form_element
+            WHERE is_deleted = 0
+            GROUP BY f_id
+        ) AS we ON wt.init_table_id = we.f_id
         WHERE wt.type = 2
           AND wt.is_deleted = 0
-          AND wt.parent_id = #{id}
+          AND wt.parent_id = #{parentId}  -- 原参数写错了!是parentId不是id
           AND wt.wbs_id = #{wbsId}
           AND wt.project_id = #{projectId}
-          AND wt.trial_tab_contract_id is null
-        -- AND (wt.wbs_type != 2 OR wt.table_type IN (1,2,9,10))
+          AND wt.trial_tab_contract_id IS NULL
         ORDER BY wt.sort, fullName
     </select>
     <select id="getByCondition" resultType="org.springblade.manager.entity.WbsTreePrivate">