|
|
@@ -360,11 +360,22 @@ public class WbsTreePrivateController extends BladeController {
|
|
|
return R.data(list);
|
|
|
}
|
|
|
|
|
|
+ // 1. 定义表单标签常量(类顶部)
|
|
|
+ private static final String[] FORM_TAG_NAMES = {
|
|
|
+ "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"
|
|
|
+ };
|
|
|
/**
|
|
|
* 批量处理HTML元素校验(解决N+1查询+串行解析)
|
|
|
*/
|
|
|
private void processHtmlElementsBatch(List<WbsNodeTableVO> data) {
|
|
|
- // 步骤1:批量收集需要查询的initTableId和pKeyId(避免循环内查询)
|
|
|
+ // 步骤1:批量收集需要查询的initTableId和pKeyId
|
|
|
Map<String, WbsNodeTableVO> initTableId2Vo = new HashMap<>();
|
|
|
Set<String> pKeyIds = new HashSet<>();
|
|
|
for (WbsNodeTableVO f : data) {
|
|
|
@@ -379,91 +390,98 @@ public class WbsTreePrivateController extends BladeController {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 步骤2:批量查询wbsFormElement(替代循环内N+1查询)
|
|
|
+ // 步骤2:批量查询wbsFormElement(统一类型为String)
|
|
|
List<WbsFormElement> wbsFormElements = wbsFormElementService.getBaseMapper().selectList(
|
|
|
Wrappers.<WbsFormElement>lambdaQuery()
|
|
|
- .in(WbsFormElement::getFId, initTableId2Vo.keySet())
|
|
|
+ .in(WbsFormElement::getFId, initTableId2Vo.keySet().stream().map(Long::valueOf).collect(Collectors.toList()))
|
|
|
.eq(WbsFormElement::getIsDeleted, 0)
|
|
|
);
|
|
|
- // 构建initTableId→eKey集合的映射
|
|
|
Map<String, Set<String>> initTableId2Keys = wbsFormElements.stream()
|
|
|
.collect(Collectors.groupingBy(
|
|
|
- WbsFormElement::getFId,
|
|
|
+ e -> String.valueOf(e.getFId()),
|
|
|
Collectors.mapping(WbsFormElement::getEKey, Collectors.toSet())
|
|
|
));
|
|
|
|
|
|
- // 步骤3:批量获取HTML内容(建议对excelHtml结果加本地缓存,比如Caffeine)
|
|
|
+
|
|
|
+ // 步骤3:批量获取HTML内容(串行+日志)
|
|
|
Map<String, String> pKeyId2Html = new HashMap<>();
|
|
|
if (CollectionUtil.isNotEmpty(pKeyIds)) {
|
|
|
- // 并行获取HTML(如果getExcelHtml是远程调用/耗时操作,并行可提升效率)
|
|
|
- pKeyIds.parallelStream().forEach(pKeyId -> {
|
|
|
+ pKeyIds.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
|
|
|
+ e.printStackTrace();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- // 步骤4:并行解析HTML(CPU密集型操作,并行提升效率)
|
|
|
- data.parallelStream().forEach(f -> {
|
|
|
+ // 步骤4:串行解析HTML(保证线程安全)
|
|
|
+ data.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());
|
|
|
+ String htmlString = pKeyId2Html.get(f.getPKeyId()+"");
|
|
|
if (StringUtil.isEmpty(htmlString)) {
|
|
|
return;
|
|
|
}
|
|
|
- // 获取预查询的eKey集合
|
|
|
Set<String> keys = initTableId2Keys.getOrDefault(initTableId, Collections.emptySet());
|
|
|
-
|
|
|
- // Jsoup解析(简化标签选择+批量判断)
|
|
|
+ // 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) {
|
|
|
+ for (String tagName : FORM_TAG_NAMES) {
|
|
|
inputs.addAll(doc.select(tagName));
|
|
|
}
|
|
|
-
|
|
|
- // 标记是否有错误(只要有一个标签不合法,就标记为1)
|
|
|
+ if (inputs.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 严格复刻原逻辑的错误判断
|
|
|
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) {
|
|
|
+ String[] idParts = id.split("__");
|
|
|
+ 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_", ""))) {
|
|
|
+ // 检查key_前缀
|
|
|
+ if (!keyPart.contains("key_")) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ // 检查key_后是否为数字
|
|
|
+ String keyNum = keyPart.replace("key_", "");
|
|
|
+ if (!StringUtils.isNumber(keyNum)) {
|
|
|
return true;
|
|
|
}
|
|
|
- // 2. 检查coordPart格式:数字_数字
|
|
|
- String[] coordParts = coordPart.split("_", 2);
|
|
|
- if (coordParts.length != 2 || !StringUtils.isNumber(coordParts[0]) || !StringUtils.isNumber(coordParts[1])) {
|
|
|
+ // 检查坐标部分
|
|
|
+ String[] coordParts = coordPart.split("_");
|
|
|
+ if (coordParts.length < 2 || !StringUtils.isNumber(coordParts[0]) || !StringUtils.isNumber(coordParts[1])) {
|
|
|
return true;
|
|
|
}
|
|
|
- // 3. 检查key是否存在
|
|
|
- return !keys.contains(keyPart);
|
|
|
+ // 检查key是否存在
|
|
|
+ if (!keys.contains(keyPart)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
});
|
|
|
|
|
|
if (hasError) {
|
|
|
f.setHtmlElementError(1);
|
|
|
+ } else {
|
|
|
+ f.setHtmlElementError(0);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 简化分组逻辑(减少重复判断)
|
|
|
*/
|