Kaynağa Gözat

后管优化查询优化 最终版

cr 1 hafta önce
ebeveyn
işleme
7bd34a0224

+ 304 - 74
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreePrivateController.java

@@ -10,9 +10,12 @@ import io.swagger.annotations.*;
 import lombok.AllArgsConstructor;
 import org.jsoup.Jsoup;
 import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
 import org.jsoup.select.Elements;
 import org.springblade.business.entity.TrialSelfInspectionRecord;
 import org.springblade.business.vo.SaveLogContractVO;
+import org.springblade.common.constant.CommonConstant;
+import org.springblade.common.utils.CommonUtil;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.boot.ctrl.BladeController;
 import org.springblade.core.cache.utils.CacheUtil;
@@ -28,15 +31,18 @@ import org.springblade.manager.dto.NameRuleDto;
 import org.springblade.manager.dto.WbsTreePrivateDTO2;
 import org.springblade.manager.dto.WbsTreePrivateDTO3;
 import org.springblade.manager.entity.*;
+import org.springblade.manager.mapper.ExcelTabMapper;
 import org.springblade.manager.mapper.WbsTreeContractMapper;
 import org.springblade.manager.mapper.WbsTreePrivateMapper;
 import org.springblade.manager.service.*;
 import org.springblade.manager.utils.FileUtils;
 import org.springblade.manager.vo.*;
 import org.springblade.manager.wrapper.WbsTreePrivateWrapper;
+import org.springblade.system.cache.ParamCache;
 import org.springblade.system.cache.DictCache;
 import org.springblade.system.user.entity.User;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.web.bind.annotation.*;
@@ -45,6 +51,7 @@ import javax.validation.Valid;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.*;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
@@ -68,6 +75,7 @@ public class WbsTreePrivateController extends BladeController {
     private final JdbcTemplate jdbcTemplate;
     private final IWbsParamService iWbsParamService;
     private final ExcelTabController excelTabController;
+    private final ExcelTabMapper excelTabMapper;
 
     /**
      * 保存项目日志划分
@@ -335,8 +343,8 @@ public class WbsTreePrivateController extends BladeController {
             /*表单对应的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))
+                            .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));
@@ -394,85 +402,156 @@ public class WbsTreePrivateController extends BladeController {
         return R.fail(200, "未查询到数据");
     }
 
+    // ========== 1. 自定义线程池(核心:可控并发,避免资源耗尽) ==========
+    private final ExecutorService parseExecutor = new ThreadPoolExecutor(
+            8,          // 核心线程数(根据CPU/接口性能调整)
+            16,         // 最大线程数
+            30,         // 空闲线程30秒回收
+            TimeUnit.SECONDS,
+            new LinkedBlockingQueue<>(100), // 任务队列
+            new ThreadFactory() {           // 命名线程,便于排查
+                private final AtomicInteger count = new AtomicInteger(1);
+                @Override
+                public Thread newThread(Runnable r) {
+                    Thread t = new Thread(r, "html-parse-thread-" + count.getAndIncrement());
+                    t.setDaemon(true); // 守护线程,不阻塞应用关闭
+                    return t;
+                }
+            },
+            new ThreadPoolExecutor.CallerRunsPolicy() // 队列满时,调用线程兜底执行
+    );
     /**
      * 查询当前节点下所有表单(根据节点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) {
+    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();
-        //解析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<WbsTreePrivateTableVO> list = new ArrayList<>();
 
-                List<String> keys = wbsFormElements.stream().map(WbsFormElement::getEKey).collect(Collectors.toList());
+        if (data != null && !data.isEmpty()) {
+            // ========== 2. 批量预处理:收集参数+批量查询,避免N+1 ==========
+            // 2.1 收集需要处理的VO、initTableId、pKeyId
+            Map<String, WbsNodeTableVO> initTableId2Vo = new HashMap<>();
+            Set<String> needProcessInitTableIds = new HashSet<>();
+            Set<Long> needProcessPKeyIds = 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);
+                    needProcessInitTableIds.add(initTableId);
+                    if (f.getPKeyId() != null) {
+                        needProcessPKeyIds.add(f.getPKeyId());
+                    }
+                }
+            }
 
+            // 2.2 批量查询所有WbsFormElement,替代循环内查询
+            Map<String, List<String>> initTableId2Keys;
+            if (!needProcessInitTableIds.isEmpty()) {
+                List<WbsFormElement> allFormElements = wbsFormElementService.getBaseMapper().selectList(
+                        Wrappers.<WbsFormElement>lambdaQuery()
+                                .in(WbsFormElement::getFId, needProcessInitTableIds)
+                                .eq(WbsFormElement::getIsDeleted, 0)
+                );
+                // 构建initTableId -> 对应的eKey列表
+                initTableId2Keys = allFormElements.stream()
+                        .collect(Collectors.groupingBy(
+                                e -> String.valueOf(e.getFId()),
+                                Collectors.mapping(WbsFormElement::getEKey, Collectors.toList())
+                        ));
+            } else {
+                initTableId2Keys = new HashMap<>();
+            }
 
-                // 读取html页面信息
-                String htmlString = "";
-                //解析html
-                try {
-                    R excelHtml = excelTabController.getExcelHtml(f.getPKeyId());
-                    htmlString = excelHtml.getData().toString();
-                } catch (Exception e) {
-                }
-                if(StringUtil.isEmpty(htmlString)){
-                    return;
+            // ========== 3. 多线程异步解析HTML ==========
+            List<CompletableFuture<Void>> futures = new ArrayList<>();
+            for (WbsNodeTableVO f : data) {
+                String htmlUrl = f.getHtmlUrl();
+                String initTableId = f.getInitTableId();
+                if (StringUtil.isBlank(htmlUrl) || StringUtils.isEmpty(initTableId)) {
+                    continue; // 无需处理的VO直接跳过
                 }
-                // 样式集合
-                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");
+
+                // 捕获循环变量,避免lambda引用异常
+                final WbsNodeTableVO finalF = f;
+                final String finalInitTableId = initTableId;
+
+                // 提交异步解析任务
+                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
+                    try {
+                        // 3.1 获取当前initTableId对应的eKey列表(批量查询的结果)
+                        List<String> keys = initTableId2Keys.getOrDefault(finalInitTableId, new ArrayList<>());
+
+                        // 3.2 异步获取HTML内容
+                        String htmlString = "";
+                        R excelHtml = this.getExcelHtml(finalF.getPKeyId());
+                        if (excelHtml != null && excelHtml.isSuccess() && excelHtml.getData() != null) {
+                            htmlString = excelHtml.getData().toString();
                         }
+                        if (StringUtil.isEmpty(htmlString)) {
+                            return;
+                        }
+
+                        // 3.3 Jsoup解析HTML标签
+                        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));
+                        }
+
+                        // 3.4 校验标签ID,设置错误标记(线程安全:仅修改当前VO的字段)
+                        inputs.forEach(input -> {
+                            String id = input.attr("id");
+                            if (StringUtils.isEmpty(id)) {
+                                finalF.setHtmlElementError(1);
+                                input.attr("htmlElementError", "1");
+                            } else {
+                                // 格式校验(和你原有逻辑一致)
+                                boolean isError = !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]);
+                                if (isError) {
+                                    finalF.setHtmlElementError(1);
+                                    input.attr("htmlElementError", "1");
+                                }
+                            }
+                        });
+                    } catch (Exception e) {
+                        // 捕获所有异常,避免单个任务失败影响整体
+                        System.err.println("解析HTML异常:VO.id=" + finalF.getId() + ",错误:" + e.getMessage());
+                        e.printStackTrace();
                     }
-                });
+                }, parseExecutor); // 提交到自定义线程池
+                futures.add(future);
             }
-        });
 
-        List<WbsTreePrivateTableVO> list = new ArrayList<>();
-        if (data != null && !data.isEmpty()) {
+            // ========== 4. 等待所有异步解析完成(核心:确保数据完整) ==========
+            try {
+                // 总超时控制(根据实际情况调整,如30秒)
+                CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
+                        .get(30, TimeUnit.SECONDS);
+            } catch (TimeoutException e) {
+                System.err.println("部分HTML解析任务超时,强制终止");
+                futures.forEach(f -> f.cancel(true)); // 取消未完成任务
+            } catch (Exception e) {
+                System.err.println("等待解析任务异常:" + e.getMessage());
+            }
+
+            // ========== 5. 原有分组逻辑(无需修改) ==========
             Integer wbsType = data.get(0).getWbsType();
             Map<Integer, List<WbsNodeTableVO>> groupMap;
             if (wbsType != null && wbsType == 1) {
@@ -493,9 +572,9 @@ public class WbsTreePrivateController extends BladeController {
                     String tableType = vo.getTableType();
                     if (StringUtil.isNumeric(tableType)) {
                         int i = Integer.parseInt(tableType);
-                        if (i == 1 ) {
+                        if (i == 1) {
                             return 2;
-                        } else if (i == 2 ) {
+                        } else if (i == 2) {
                             return 1;
                         }
                     }
@@ -517,6 +596,147 @@ public class WbsTreePrivateController extends BladeController {
         return R.fail(200, "未查询到数据");
     }
 
+
+    // ========== 5. 销毁线程池(避免内存泄漏) ==========
+    @PreDestroy
+    public void destroyExecutor() {
+        parseExecutor.shutdown();
+        try {
+            if (!parseExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
+                parseExecutor.shutdownNow();
+            }
+        } catch (InterruptedException e) {
+            parseExecutor.shutdownNow();
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    public R getExcelHtml(Long pkeyId) throws Exception {
+        String file_path = ParamCache.getValue(CommonConstant.SYS_LOCAL_URL);
+        String sys_file_net_url = ParamCache.getValue(CommonConstant.SYS_FILE_NET_URL);
+        Thread.sleep(200);
+        WbsTreePrivate wbsTreePrivate = wbsTreePrivateMapper.getByPKeyId(pkeyId);
+        if (wbsTreePrivate == null) {
+            return R.fail("该数据下无此节点!");
+        }
+        if (wbsTreePrivate.getHtmlUrl() == null) {
+            return R.fail("请上传清表!");
+        }
+
+        String fileUrl = wbsTreePrivate.getHtmlUrl();
+        File file1 = ResourceUtil.getFile(fileUrl);
+        InputStream fileInputStream;
+        if (file1.exists()) {
+            fileInputStream = new FileInputStream(file1);
+        } else {
+            String path = sys_file_net_url + fileUrl.replaceAll("//", "/").replaceAll(file_path, "");
+            fileInputStream = CommonUtil.getOSSInputStream(path);
+        }
+
+        String htmlString = IoUtil.readToString(fileInputStream);
+
+        // 远程搜索配置
+        Document doc = Jsoup.parse(htmlString);
+
+        //获取所有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));
+        }
+
+        //获取元素表数据
+        List<WbsFormElement> wbsFormElements = wbsFormElementService.getBaseMapper().selectList(Wrappers.<WbsFormElement>lambdaQuery()
+                .eq(WbsFormElement::getFId, wbsTreePrivate.getInitTableId())
+                .eq(WbsFormElement::getIsDeleted, 0)
+        );
+        List<String> keys = wbsFormElements.stream().map(WbsFormElement::getEKey).collect(Collectors.toList());
+
+        //判断标签是否存在id属性
+        inputs.forEach(input -> {
+            String id = input.attr("id");
+            if(com.mixsmart.utils.StringUtils.isEmpty(id)){
+                input.attr("htmlElementError", "1");
+            } else {
+                /**
+                 * 判断当前元素是否符合格式
+                 * 1、是否存在key_
+                 * 2、是否存在__
+                 * 3、key_后面是否为数字
+                 * 4、__后面是否为坐标
+                 * 5、key是否在元素库中存在
+                 */
+                if(!id.contains("key_")
+                        || !id.contains("__")
+                        || !com.mixsmart.utils.StringUtils.isNumber(id.split("__")[0].replace("key_",""))
+                        || !id.split("__")[1].contains("_")
+                        || !com.mixsmart.utils.StringUtils.isNumber(id.split("__")[1].split("_")[0])
+                        || !com.mixsmart.utils.StringUtils.isNumber(id.split("__")[1].split("_")[1])
+                        || !keys.contains(id.split("__")[0])
+                ){
+                    input.attr("htmlElementError", "1");
+                }
+            }
+        });
+
+
+        Element table = doc.select("table").first();
+        Elements col = doc.select("Col");
+        doc.select("Col").remove();
+        ProjectInfo projectInfo = projectInfoService.getById(wbsTreePrivate.getProjectId());
+        Boolean isWater = false;
+        ExcelTab tab = excelTabMapper.getWaterByTableId(wbsTreePrivate.getExcelId());
+        if (tab != null) {
+            isWater = true;
+        }
+        ExcelTab dataInfo = excelTabMapper.selectById(wbsTreePrivate.getExcelId());
+        // 添加标题显示
+        Elements trs = table.select("tr");
+        //这句代码在正式环境要加上
+
+        if (Func.isNotEmpty(dataInfo) && dataInfo.getTabType() != null && dataInfo.getTabType() != 100L) {
+            int x=0;
+            for (int i = 1; i < 6; i++) {
+                Element tr = trs.get(i);
+                Elements tds = tr.select("td");
+                for (int j = 0; j < tds.size(); j++) {
+                    Element data = tds.get(j);
+                    String style = data.attr("style");
+                    if (style.indexOf("font-size") >= 0) {
+                        int fontsize = Integer.parseInt(style.substring(style.indexOf("font-size:") + 10, style.indexOf(".0pt")));
+                        if (isWater) {
+                            if (org.apache.commons.lang.StringUtils.isNotEmpty(data.text()) && fontsize >= 12&&x<=0) {
+                                trs.get(i - 1).select("td").get(0).text(projectInfo.getProjectName());
+                                x=x+1;
+                            }
+                        } else {
+                            if (org.apache.commons.lang.StringUtils.isNotEmpty(data.text()) && fontsize >= 14) {
+                                Elements td = trs.get(i - 1).select("td");
+                                if (td != null && td.size() >= 1) {
+                                    for (Element element : td) {
+                                        String style2 = element.attr("style");
+                                        String sizeinfo = style2.substring(style.indexOf("font-size:") + 10, style.indexOf(".0pt"));
+                                        if (sizeinfo.length() >= 2 && sizeinfo.indexOf(".") < 0) {
+                                            int fontsize2 = Func.toInt(sizeinfo);
+                                            if (org.apache.commons.lang.StringUtils.isEmpty(element.text()) && fontsize2 >= 14) {
+                                                element.text(projectInfo.getProjectName());
+                                                break;
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        fileInputStream.close();
+        return R.data(table + "");
+    }
+
     /**
      * 根据pkeyid检验html是否存在错误细腻些
      * @param pKeyId
@@ -596,13 +816,23 @@ public class WbsTreePrivateController extends BladeController {
     @ApiOperation(value = "删除节点下的元素表", notes = "传入表单id、wbsId、projectId")
     public R removeTableByCondition(@RequestParam("id") String id,
                                     @RequestParam("wbsId") String wbsId,
-                                    @RequestParam("projectId") String projectId) {
-        Long row = wbsTreeContractMapper.selectCount(Wrappers.<WbsTreeContract>query().lambda().eq(WbsTreeContract::getProjectId, projectId).eq(WbsTreeContract::getWbsId, wbsId).eq(WbsTreeContract::getId, id));
-        if (row > 0L) {
-            throw new ServiceException("当前表单被合同段引用中,删除失败");
-        }
-        if (wbsTreePrivateService.removeTableByCondition(id, wbsId, projectId)) {
-            return R.success("删除成功");
+                                    @RequestParam("projectId") String projectId, @RequestParam("pKeyId") String pKeyId) {
+        if (StringUtil.isNumeric(pKeyId)) {
+            Long row = wbsTreeContractMapper.selectCount(Wrappers.<WbsTreeContract>query().lambda().eq(WbsTreeContract::getProjectId, projectId).eq(WbsTreeContract::getWbsId, wbsId).eq(WbsTreeContract::getIsTypePrivatePid, pKeyId));
+            if (row > 0L) {
+                throw new ServiceException("当前表单被合同段引用中,删除失败");
+            }
+            if (wbsTreePrivateService.removeTableByPKeyIdCondition(pKeyId, wbsId, projectId)) {
+                return R.success("删除成功");
+            }
+        } else if (StringUtil.isNumeric(id)) {
+            Long row = wbsTreeContractMapper.selectCount(Wrappers.<WbsTreeContract>query().lambda().eq(WbsTreeContract::getProjectId, projectId).eq(WbsTreeContract::getWbsId, wbsId).eq(WbsTreeContract::getId, id));
+            if (row > 0L) {
+                throw new ServiceException("当前表单被合同段引用中,删除失败");
+            }
+            if (wbsTreePrivateService.removeTableByCondition(id, wbsId, projectId)) {
+                return R.success("删除成功");
+            }
         }
         return R.fail("删除失败");
     }

+ 27 - 25
blade-service/blade-manager/src/main/java/org/springblade/manager/mapper/WbsTreePrivateMapper.xml

@@ -540,6 +540,7 @@
                 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
         FROM m_wbs_tree_private AS wt
         WHERE wt.type = 2
           AND wt.is_deleted = 0
@@ -547,7 +548,7 @@
           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.wbs_type != 2 OR wt.table_type IN (1,2,9,10))
         ORDER BY wt.sort, fullName
     </select>
     <select id="getByCondition" resultType="org.springblade.manager.entity.WbsTreePrivate">
@@ -1011,6 +1012,7 @@
         (SELECT dict_value from blade_dict where code='table_type' and dict_key not in(-1,0) and dict_key=table_type )
         as tabType,
         (SELECT count(1) FROM m_wbs_form_element WHERE f_id = initTableId and is_deleted=0) AS "elementTotal",
+        (SELECT tab_ch_name from m_table_info where tab_en_name = initTableName)     As elementTableName,
         (SELECT dict_value from blade_dict where code='owner_type' and dict_key not in(-1,0) and dict_key=table_owner )
         as tabOwner,
         /*排序*/
@@ -1078,30 +1080,6 @@
             contract_id = #{contractId}
     </select>
     <select id="selectAllChildNode" resultType="org.springblade.manager.entity.WbsTreePrivate"></select>
-    <select id="testFormInit" resultType="java.lang.Long">
-        SELECT
-            p_key_id
-        FROM
-            m_wbs_tree_private
-        WHERE
-            project_id = #{projectId}
-            AND wbs_id = #{wbsId}
-            AND parent_id = #{id}
-            AND type = 2
-            AND STATUS = 1
-            AND is_deleted = 0
-            AND table_owner = 7
-            AND locate( '__', node_name ) = 0
-            /*解决不同合同段中复制表问题*/
-            <if test="contractId == null">
-                -- 后管加载原始表
-                AND trial_tab_contract_id is null
-            </if>
-            <if test="contractId != null and contractId != ''">
-                -- 客户端加载当前合同段表+原始表
-                AND (trial_tab_contract_id is null OR (trial_tab_contract_id = #{contractId}))
-            </if>
-    </select>
     <select id="getRecommendTable" resultType="org.springblade.manager.vo.WbsNodeTableVO">
         SELECT
             a.p_key_id AS "pKeyId",
@@ -1145,4 +1123,28 @@
                 a.excel_id,
                 a.init_table_name
     </select>
+    <select id="testFormInit" resultType="java.lang.Long">
+        SELECT
+            p_key_id
+        FROM
+            m_wbs_tree_private
+        WHERE
+            project_id = #{projectId}
+            AND wbs_id = #{wbsId}
+            AND parent_id = #{id}
+            AND type = 2
+            AND STATUS = 1
+            AND is_deleted = 0
+            AND table_owner = 7
+            AND locate( '__', node_name ) = 0
+            /*解决不同合同段中复制表问题*/
+            <if test="contractId == null">
+                -- 后管加载原始表
+                AND trial_tab_contract_id is null
+            </if>
+            <if test="contractId != null and contractId != ''">
+                -- 客户端加载当前合同段表+原始表
+                AND (trial_tab_contract_id is null OR (trial_tab_contract_id = #{contractId}))
+            </if>
+    </select>
 </mapper>