|
|
@@ -317,208 +317,201 @@ 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);
|
|
|
- // 仅处理isLinkTable标记(SQL无法处理的逻辑)
|
|
|
- if (CollectionUtil.isNotEmpty(rs)) {
|
|
|
- rs.forEach(r -> {
|
|
|
+ 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) {
|
|
|
if (StringUtil.isBlank(r.getHtmlUrl())) {
|
|
|
r.setIsLinkTable(1);
|
|
|
}
|
|
|
- });
|
|
|
- return R.data(rs);
|
|
|
- }
|
|
|
- return R.fail(404, "未查询到数据"); // 200是成功码,失败用404更规范
|
|
|
- }
|
|
|
+ if (StringUtil.isBlank(r.getInitTableId())) {
|
|
|
|
|
|
- @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, "未查询到数据");
|
|
|
- }
|
|
|
+ /*提出去,不for循环查询
|
|
|
+ WbsTree wbsTree = wbsTreeService.getById(r.getId());*/
|
|
|
|
|
|
- // 3. 批量处理HTML解析(核心优化:批量查询→并行处理)
|
|
|
- processHtmlElementsBatch(data);
|
|
|
+ Long initTabId = initTableIdMaps.getOrDefault(Long.parseLong(r.getId()), null);
|
|
|
|
|
|
- // 4. 简化分组逻辑(减少重复计算)
|
|
|
- List<WbsTreePrivateTableVO> list = groupNodeTables(data);
|
|
|
+ if (initTabId != null) {
|
|
|
+ r.setInitTableId(String.valueOf(initTabId));
|
|
|
|
|
|
- return R.data(list);
|
|
|
- }
|
|
|
+ formElementFids.add(initTabId);
|
|
|
|
|
|
- /**
|
|
|
- * 批量处理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;
|
|
|
- }
|
|
|
+ /*提出去,不for循环查询
|
|
|
+ r.setElementTotal(Math.toIntExact(wbsFormElementService.count(
|
|
|
+ new LambdaQueryWrapper<WbsFormElement>().eq(WbsFormElement::getFId, r.getInitTableId()))));*/
|
|
|
|
|
|
- // 步骤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());
|
|
|
+ } else {
|
|
|
+ r.setInitTableId("");
|
|
|
}
|
|
|
- } catch (Exception e) {
|
|
|
- System.out.println("获取HTML失败,pKeyId="+pKeyId); // 增加日志,避免空catch
|
|
|
}
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 步骤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());
|
|
|
|
|
|
- // 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));
|
|
|
- }
|
|
|
+ if (formElementFids.size() > 0) {
|
|
|
+ /*查询所有相关的元素记录*/
|
|
|
+ List<WbsFormElement> wbsFormElements = wbsFormElementService.getBaseMapper().selectList(Wrappers.<WbsFormElement>lambdaQuery()
|
|
|
+ .select(WbsFormElement::getFId)
|
|
|
+ .in(WbsFormElement::getFId, formElementFids));
|
|
|
|
|
|
- // 标记是否有错误(只要有一个标签不合法,就标记为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);
|
|
|
- });
|
|
|
+ /*elementCountMap分组*/
|
|
|
+ Map<String, Integer> elementCountMap = wbsFormElements.stream()
|
|
|
+ .collect(Collectors.groupingBy(WbsFormElement::getFId, Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
|
|
|
|
|
|
- if (hasError) {
|
|
|
- f.setHtmlElementError(1);
|
|
|
+ /*元素数量赋值*/
|
|
|
+ for (WbsNodeTableVO r : rs) {
|
|
|
+ if (StringUtil.isNotBlank(r.getInitTableId())) {
|
|
|
+ r.setElementTotal(elementCountMap.getOrDefault(r.getInitTableId(), 0));
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- });
|
|
|
+
|
|
|
+ return R.data(rs);
|
|
|
+ }
|
|
|
+ return R.fail(200, "未查询到数据");
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 简化分组逻辑(减少重复判断)
|
|
|
+ * 查询当前节点下所有表单(根据节点ID查询当前表单)
|
|
|
*/
|
|
|
- private List<WbsTreePrivateTableVO> groupNodeTables(List<WbsNodeTableVO> data) {
|
|
|
+ @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();
|
|
|
List<WbsTreePrivateTableVO> list = new ArrayList<>();
|
|
|
- Integer wbsType = data.get(0).getWbsType();
|
|
|
- if (wbsType == null) {
|
|
|
- list.add(new WbsTreePrivateTableVO("", data));
|
|
|
- return list;
|
|
|
- }
|
|
|
-
|
|
|
- // 预定义分组规则(避免嵌套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;
|
|
|
+ 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");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
- 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));
|
|
|
+ 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));
|
|
|
+ }
|
|
|
}
|
|
|
+ return R.data(list);
|
|
|
}
|
|
|
- return list;
|
|
|
+ return R.fail(200, "未查询到数据");
|
|
|
}
|
|
|
|
|
|
/**
|