Browse Source

refactor(online): 将WidgetJson解析逻辑提取为独立工具类

- 提取字典ID解析逻辑到WidgetJsonUtil.extractDictIdSet方法
- 移除OnlineFormController中的extractDictIdSetFromWidgetJson私有方法
- 提取字段名收集逻辑到WidgetJsonUtil.collectBindColumnNames方法
- 移除OnlineFormController中的collectAllBindColumnNames私有方法
- 提取移动端字段名收集逻辑到WidgetJsonUtil.buildMobileColumnNameWidgetMap方法
- 移除OnlineFormController中的collectMobileBindColumnNames私有方法
- 提取子表属性收集逻辑到WidgetJsonUtil.collectSubTableProps方法
- 移除OnlineFormController中的collectSubTableAndPropMap私有方法
- 提取字段名到控件映射构建逻辑到多个WidgetJsonUtil静态方法
- 移除OnlineFormController中相关的私有方法实现
- 添加字段路径步骤解析功能支持PC和移动端不同结构
- 实现统一的控件树遍历和递归处理机制
feature/2026-07-ccc-dev
chenchuchuan 2 months ago
parent
commit
618f14aced
  1. 350
      common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java
  2. 457
      common/common-online/src/main/java/apelet/common/online/util/WidgetJsonUtil.java

350
common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java

@ -29,6 +29,7 @@ import apelet.common.online.model.constant.FormType;
import apelet.common.online.service.*; import apelet.common.online.service.*;
import apelet.common.online.util.OnlineRedisKeyUtil; import apelet.common.online.util.OnlineRedisKeyUtil;
import apelet.common.online.util.WidgetFieldTypeMapping; import apelet.common.online.util.WidgetFieldTypeMapping;
import apelet.common.online.util.WidgetJsonUtil;
import apelet.common.online.vo.OnlineFormVo; import apelet.common.online.vo.OnlineFormVo;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
@ -56,7 +57,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.groups.Default; import javax.validation.groups.Default;
import java.util.*; import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -519,7 +519,7 @@ public class OnlineFormController {
jsonObject.put("onlineVirtualColumnList", virtualColumnList); jsonObject.put("onlineVirtualColumnList", virtualColumnList);
Set<Long> dictIdSet = onlineColumnList.stream() Set<Long> dictIdSet = onlineColumnList.stream()
.filter(c -> c.getDictId() != null).map(OnlineColumn::getDictId).collect(Collectors.toSet()); .filter(c -> c.getDictId() != null).map(OnlineColumn::getDictId).collect(Collectors.toSet());
Set<Long> widgetDictIdSet = this.extractDictIdSetFromWidgetJson(onlineForm.getWidgetJson()); Set<Long> widgetDictIdSet = WidgetJsonUtil.extractDictIdSet(onlineForm.getWidgetJson());
CollUtil.addAll(dictIdSet, widgetDictIdSet); CollUtil.addAll(dictIdSet, widgetDictIdSet);
if (CollUtil.isNotEmpty(dictIdSet)) { if (CollUtil.isNotEmpty(dictIdSet)) {
List<OnlineDict> onlineDictList = onlineDictService.getOnlineDictListFromCache(dictIdSet); List<OnlineDict> onlineDictList = onlineDictService.getOnlineDictListFromCache(dictIdSet);
@ -660,46 +660,6 @@ public class OnlineFormController {
return ResponseResult.success(datasourceIdSet); return ResponseResult.success(datasourceIdSet);
} }
private Set<Long> extractDictIdSetFromWidgetJson(String widgetJson) {
Set<Long> dictIdSet = new HashSet<>();
if (StrUtil.isBlank(widgetJson)) {
return dictIdSet;
}
JSONObject allData = JSON.parseObject(widgetJson);
JSONObject pcData = allData.getJSONObject(AppDeviceType.getDeviceType());
if (MapUtil.isEmpty(pcData)) {
return dictIdSet;
}
JSONArray widgetListArray = pcData.getJSONArray("widgetList");
if (CollUtil.isEmpty(widgetListArray)) {
return dictIdSet;
}
for (int i = 0; i < widgetListArray.size(); i++) {
this.recursiveExtractDictId(widgetListArray.getJSONObject(i), dictIdSet);
}
return dictIdSet;
}
private void recursiveExtractDictId(JSONObject widgetData, Set<Long> dictIdSet) {
JSONObject propsData = widgetData.getJSONObject("props");
if (MapUtil.isNotEmpty(propsData)) {
JSONObject dictInfoData = propsData.getJSONObject("dictInfo");
if (MapUtil.isNotEmpty(dictInfoData)) {
Long dictId = dictInfoData.getLong("dictId");
if (dictId != null) {
dictIdSet.add(dictId);
}
}
}
JSONArray childWidgetArray = widgetData.getJSONArray("childWidgetList");
if (CollUtil.isNotEmpty(childWidgetArray)) {
for (int i = 0; i < childWidgetArray.size(); i++) {
this.recursiveExtractDictId(childWidgetArray.getJSONObject(i), dictIdSet);
}
}
}
/** /**
* 获取流程对应的在线表单 * 获取流程对应的在线表单
* @param bosType * @param bosType
@ -761,19 +721,16 @@ public class OnlineFormController {
boolean changed = false; boolean changed = false;
// === Diff 新增字段 === // === Diff 新增字段 ===
// 这里是取pc 的 还要取一下 moblie的 // 收集 pc/mobile 端所有绑定的字段列名
Set<String> widgetColumnNames = collectAllBindColumnNames(widgetJson); Set<String> widgetColumnNames = WidgetJsonUtil.collectBindColumnNames(widgetJson);
// 收集 mobile 端的列名(遍历 childWidgetList 路径)
widgetColumnNames.addAll(collectMobileBindColumnNames(widgetJson));
List<OnlFormField> existingFields = onlFormFieldService.list( List<OnlFormField> existingFields = onlFormFieldService.list(new LambdaQueryWrapper<OnlFormField>().eq(OnlFormField::getHeadId, onlFormHead.getId()));
new LambdaQueryWrapper<OnlFormField>().eq(OnlFormField::getHeadId, onlFormHead.getId()));
Set<String> existingFieldNames = existingFields.stream() Set<String> existingFieldNames = existingFields.stream()
.map(OnlFormField::getFieldName).collect(Collectors.toSet()); .map(OnlFormField::getFieldName).collect(Collectors.toSet());
Set<String> newColumnNames = new HashSet<>(widgetColumnNames); Set<String> newColumnNames = new HashSet<>(widgetColumnNames);
newColumnNames.removeAll(existingFieldNames); newColumnNames.removeAll(existingFieldNames);
// 主表/单表已有字段:如果绑定了从表(slaveTableId),同步更新 refPropert // 主表/单表已有字段:如果绑定了从表(slaveTableId),同步更新 refPropert
Map<String, JSONObject> fieldNameToWidget = mapFieldNameToWidget(widgetJson); Map<String, JSONObject> fieldNameToWidget = WidgetJsonUtil.buildFieldNameWidgetMap(widgetJson);
for (OnlFormField existingField : existingFields) { for (OnlFormField existingField : existingFields) {
JSONObject widget = fieldNameToWidget.get(existingField.getFieldName()); JSONObject widget = fieldNameToWidget.get(existingField.getFieldName());
if (widget == null) { if (widget == null) {
@ -790,9 +747,9 @@ public class OnlineFormController {
} }
} }
if (!newColumnNames.isEmpty()) { if (!newColumnNames.isEmpty()) {
Map<String, JSONObject> nameToWidget = mapColumnNameToWidget(widgetJson); Map<String, JSONObject> nameToWidget = WidgetJsonUtil.buildColumnNameWidgetMap(widgetJson);
// 收集 mobile 端的 widget 映射(遍历 childWidgetList 路径) // 收集 mobile 端的 widget 映射(遍历 childWidgetList 路径)
Map<String, JSONObject> mobileNameToWidget = mapMobileColumnNameToWidget(widgetJson); Map<String, JSONObject> mobileNameToWidget = WidgetJsonUtil.buildMobileColumnNameWidgetMap(widgetJson);
OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId());
// 预校验:过滤掉 widgetType 不在映射中的控件,避免 getFieldTypeId 抛异常导致部分数据已入库 // 预校验:过滤掉 widgetType 不在映射中的控件,避免 getFieldTypeId 抛异常导致部分数据已入库
@ -876,7 +833,7 @@ public class OnlineFormController {
// === Diff 新增子表 === // === Diff 新增子表 ===
// 取 widgetList->bindData -> tableId 在去 zz_online_table 查询,这样就可以判断哪些是新增,哪些是 已有的表 // 取 widgetList->bindData -> tableId 在去 zz_online_table 查询,这样就可以判断哪些是新增,哪些是 已有的表
// 如果存在附表 // 如果存在附表
Map<String, JSONObject> tableIdAndPropMap = collectSubTableAndPropMap(widgetJson); Map<String, JSONObject> tableIdAndPropMap = WidgetJsonUtil.collectSubTableProps(widgetJson);
if (MapUtil.isNotEmpty(tableIdAndPropMap)) { if (MapUtil.isNotEmpty(tableIdAndPropMap)) {
//搜集的所有tableId, 要么是 tableId(已存在),要么是 tableName(不存在) //搜集的所有tableId, 要么是 tableId(已存在),要么是 tableName(不存在)
@ -900,7 +857,7 @@ public class OnlineFormController {
List<OnlFormHead> onlFormHeadList = onlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().in(OnlFormHead::getTableName, tableNames)); List<OnlFormHead> onlFormHeadList = onlFormHeadService.list(new LambdaQueryWrapper<OnlFormHead>().in(OnlFormHead::getTableName, tableNames));
Map<String, OnlFormHead> tableNameAndHeadIdMap = onlFormHeadList.stream().collect(Collectors.toMap(OnlFormHead::getTableName, Function.identity())); Map<String, OnlFormHead> tableNameAndHeadIdMap = onlFormHeadList.stream().collect(Collectors.toMap(OnlFormHead::getTableName, Function.identity()));
//因为这里是zz_online_column.column_id 的id和 列名的混合,我只要列名 //因为这里是zz_online_column.column_id 的id和 列名的混合,我只要列名
Map<String, JSONObject> nameToWidget = mapColumnIdAndWidget(widgetJson); Map<String, JSONObject> nameToWidget = WidgetJsonUtil.buildSubTableColumnWidgetMap(widgetJson);
Set<String> newSubColumnNameSet = nameToWidget.keySet().stream().filter(s -> !s.matches("\\d+")).collect(Collectors.toSet()); Set<String> newSubColumnNameSet = nameToWidget.keySet().stream().filter(s -> !s.matches("\\d+")).collect(Collectors.toSet());
//这里要走 新增逻辑 //这里要走 新增逻辑
if (!newSubColumnNameSet.isEmpty()) { if (!newSubColumnNameSet.isEmpty()) {
@ -1016,7 +973,7 @@ public class OnlineFormController {
private boolean createNewSubTables(JSONObject widgetJson, OnlFormHead onlFormHead, OnlineDatasource datasource, Set<String> newSubTableNames) { private boolean createNewSubTables(JSONObject widgetJson, OnlFormHead onlFormHead, OnlineDatasource datasource, Set<String> newSubTableNames) {
if (newSubTableNames.isEmpty()) { if (newSubTableNames.isEmpty()) {
// 如果他为空,说明我们走tableId取值没取到,我们就去一下 tableName; // 如果他为空,说明我们走tableId取值没取到,我们就去一下 tableName;
newSubTableNames = this.collectSubTableName(widgetJson); newSubTableNames = WidgetJsonUtil.collectSubTableNames(widgetJson);
} }
if (newSubTableNames.isEmpty()) { if (newSubTableNames.isEmpty()) {
return false; return false;
@ -1036,7 +993,7 @@ public class OnlineFormController {
onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, datasource, null, dblink); onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, datasource, null, dblink);
if (subOnlineTable != null) { if (subOnlineTable != null) {
backfillRelationId(widgetJson, subTableName, subOnlineTable.getTableId()); WidgetJsonUtil.backfillTableId(widgetJson, subTableName, subOnlineTable.getTableId());
} }
} }
@ -1077,210 +1034,6 @@ public class OnlineFormController {
onlFormFieldService.save(field); onlFormFieldService.save(field);
} }
/**
* 遍历 pc/mobile 的 widget 树,收集所有 bindData.columnName 值。
*/
private Set<String> collectAllBindColumnNames(JSONObject widgetJson) {
Set<String> names = new HashSet<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
names.add(name);
}
}
});
return names;
}
/**
* 遍历 pc/mobile 的 widget 树,收集 widgetType=100 (TableContainer) 的 bindData 下的 tableId
*/
private Map<String, JSONObject> collectSubTableAndPropMap(JSONObject widgetJson) {
Map<String, JSONObject> resultMap = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String tableId = bindData.getString("tableId");
if (StrUtil.isBlank(tableId)) {
continue;
}
JSONObject props = childWidget.getJSONObject("props");
if (props != null) {
resultMap.put(tableId, props);
}
}
}
});
return resultMap;
}
private Set<String> collectSubTableName(JSONObject widgetJson) {
Set<String> tableNameSet = new HashSet<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
if (bindData.containsKey("tableName")) {
tableNameSet.add(bindData.getString("tableName"));
}
}
}
});
return tableNameSet;
}
/**
* 构建 columnName → widget 的映射。
*/
private Map<String, JSONObject> mapColumnNameToWidget(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
map.put(name, widget);
}
}
});
return map;
}
/**
* 构建 fieldName → widget 的映射(用于主表已有字段匹配)。
* key 优先取 bindData.columnFieldName,兼容 bindData.columnName。
*/
private Map<String, JSONObject> mapFieldNameToWidget(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData == null) {
return;
}
String fieldName = bindData.getString("columnFieldName");
if (StrUtil.isEmpty(fieldName)) {
fieldName = bindData.getString("columnName");
}
if (StrUtil.isNotEmpty(fieldName)) {
map.put(fieldName, widget);
}
});
return map;
}
private Map<String, JSONObject> mapColumnIdAndWidget(JSONObject widgetJson) {
Map<String, JSONObject> resultMap = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理widgetType为100的子表
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String tableId = bindData.getString("tableId");
if (StrUtil.isBlank(tableId)) {
continue;
}
JSONObject props = childWidget.getJSONObject("props");
if (props == null) {
continue;
}
JSONArray tableColumnList = props.getJSONArray("tableColumnList");
for (int j = 0; j < tableColumnList.size(); j++) {
JSONObject tableColumn = tableColumnList.getJSONObject(j);
// 过滤掉 fieldType=1 的列(如序号、合计等非真实字段),避免被识别为需要新增的字段
Integer fieldType = tableColumn.getInteger("fieldType");
if (fieldType != null && fieldType == 1) {
continue;
}
resultMap.put(tableColumn.getString("columnId"), tableColumn);
}
}
}
});
return resultMap;
}
/**
* 在 widgetJson 中查找匹配 tableName 的 TableContainer 控件,回填 tableId。
*/
private void backfillRelationId(JSONObject widgetJson, String tableName, Long tableId) {
walkAllModes(widgetJson, widget -> {
if (widget.getInteger("widgetType") != null && 100 == widget.getInteger("widgetType")) {
JSONObject props = widget.getJSONObject("bindData");
if (props != null && tableName.equals(props.getString("tableName"))) {
props.put("tableId", tableId);
}
}
});
}
/**
* 遍历 "pc" 和 "mobile" 两个模式的控件树。
*/
private void walkAllModes(JSONObject widgetJson, Consumer<JSONObject> visitor) {
for (String mode : new String[]{"pc", "mobile"}) {
JSONObject modeObj = widgetJson.getJSONObject(mode);
if (modeObj != null) {
walkWidgets(modeObj, visitor);
}
}
}
/**
* 递归遍历控件树,访问每个控件及其子控件(widgetList)。
*/
private void walkWidgets(JSONObject node, Consumer<JSONObject> visitor) {
if (node == null) {
return;
}
visitor.accept(node);
JSONArray children = node.getJSONArray("widgetList");
if (children != null) {
for (int i = 0; i < children.size(); i++) {
walkWidgets(children.getJSONObject(i), visitor);
}
}
}
// ============ mobile 端子表同步相关方法 ============ // ============ mobile 端子表同步相关方法 ============
/** /**
@ -1355,7 +1108,7 @@ public class OnlineFormController {
if (StrUtil.isEmpty(tableName) || !newSubTableNames.contains(tableName)) { if (StrUtil.isEmpty(tableName) || !newSubTableNames.contains(tableName)) {
continue; continue;
} }
// createNewSubTables 已通过 backfillRelationId 回写了 tableId,这里只需补 masterColumnName // createNewSubTables 已通过 WidgetJsonUtil.backfillTableId 回写了 tableId,这里只需补 masterColumnName
if (!bindData.containsKey("tableId")) { if (!bindData.containsKey("tableId")) {
OnlineTable subTable = onlineTableService.getOne(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, tableName.toLowerCase())); OnlineTable subTable = onlineTableService.getOne(new LambdaQueryWrapper<OnlineTable>().eq(OnlineTable::getTableName, tableName.toLowerCase()));
if (subTable != null) { if (subTable != null) {
@ -1473,83 +1226,6 @@ public class OnlineFormController {
} }
/** /**
* 遍历 mobile 端 widget 树,收集所有 bindData.columnName 值。
* 路径: mobile -> tableWidget -> childWidgetList -> childWidgetList -> bindData -> columnName
*/
private Set<String> collectMobileBindColumnNames(JSONObject widgetJson) {
Set<String> names = new HashSet<>();
JSONObject mobile = widgetJson.getJSONObject("mobile");
if (mobile == null) {
return names;
}
JSONObject tableWidget = mobile.getJSONObject("tableWidget");
if (tableWidget == null) {
return names;
}
JSONArray cardList = tableWidget.getJSONArray("childWidgetList");
if (cardList == null) {
return names;
}
for (int i = 0; i < cardList.size(); i++) {
JSONObject cardWidget = cardList.getJSONObject(i);
JSONArray fieldList = cardWidget.getJSONArray("childWidgetList");
if (fieldList == null) {
continue;
}
for (int j = 0; j < fieldList.size(); j++) {
JSONObject fieldWidget = fieldList.getJSONObject(j);
JSONObject bindData = fieldWidget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
names.add(name);
}
}
}
}
return names;
}
/**
* 构建 mobile 端 columnName → widget 的映射。
* 路径: mobile -> tableWidget -> childWidgetList -> childWidgetList -> bindData -> columnName
*/
private Map<String, JSONObject> mapMobileColumnNameToWidget(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
JSONObject mobile = widgetJson.getJSONObject("mobile");
if (mobile == null) {
return map;
}
JSONObject tableWidget = mobile.getJSONObject("tableWidget");
if (tableWidget == null) {
return map;
}
JSONArray cardList = tableWidget.getJSONArray("childWidgetList");
if (cardList == null) {
return map;
}
for (int i = 0; i < cardList.size(); i++) {
JSONObject cardWidget = cardList.getJSONObject(i);
JSONArray fieldList = cardWidget.getJSONArray("childWidgetList");
if (fieldList == null) {
continue;
}
for (int j = 0; j < fieldList.size(); j++) {
JSONObject fieldWidget = fieldList.getJSONObject(j);
JSONObject bindData = fieldWidget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
map.put(name, fieldWidget);
}
}
}
}
return map;
}
/**
* 查询指定表名的列表表单 * 查询指定表名的列表表单
* *
* @param tableName 表名 * @param tableName 表名

457
common/common-online/src/main/java/apelet/common/online/util/WidgetJsonUtil.java

@ -0,0 +1,457 @@
package apelet.common.online.util;
import apelet.common.core.constant.AppDeviceType;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
/**
* widgetJson 数据操作工具类。
* 提供从在线表单 widgetJson 中提取字典、绑定字段、子表控件等信息的纯数据操作方法。
*
* @author chenchuchuan
* @date 2026-08-06
*/
public class WidgetJsonUtil {
private WidgetJsonUtil() {
}
/**
* 从 widgetJson 中提取所有控件绑定的字典Id。
* 仅遍历当前设备端(widgetList 及 childWidgetList)控件树中 props.dictInfo.dictId。
*
* @param widgetJson 在线表单的 widgetJson。
* @return 字典Id集合。
*/
public static Set<Long> extractDictIdSet(String widgetJson) {
Set<Long> dictIdSet = new HashSet<>();
if (StrUtil.isBlank(widgetJson)) {
return dictIdSet;
}
JSONObject deviceData = JSON.parseObject(widgetJson).getJSONObject(AppDeviceType.getDeviceType());
if (MapUtil.isEmpty(deviceData)) {
return dictIdSet;
}
JSONArray widgetListArray = deviceData.getJSONArray("widgetList");
if (CollUtil.isEmpty(widgetListArray)) {
return dictIdSet;
}
for (int i = 0; i < widgetListArray.size(); i++) {
extractDictIdRecursively(widgetListArray.getJSONObject(i), dictIdSet);
}
return dictIdSet;
}
/**
* 递归提取单个控件及其子控件中的字典Id。
*/
private static void extractDictIdRecursively(JSONObject widgetData, Set<Long> dictIdSet) {
JSONObject propsData = widgetData.getJSONObject("props");
if (MapUtil.isNotEmpty(propsData)) {
JSONObject dictInfoData = propsData.getJSONObject("dictInfo");
if (MapUtil.isNotEmpty(dictInfoData)) {
Long dictId = dictInfoData.getLong("dictId");
if (dictId != null) {
dictIdSet.add(dictId);
}
}
}
JSONArray childWidgetArray = widgetData.getJSONArray("childWidgetList");
if (CollUtil.isNotEmpty(childWidgetArray)) {
for (int i = 0; i < childWidgetArray.size(); i++) {
extractDictIdRecursively(childWidgetArray.getJSONObject(i), dictIdSet);
}
}
}
/**
* 遍历 pc/mobile 控件树,收集所有控件 bindData.columnName 值。
* <p>
* 每个读取源用"设备端 + 字段步骤链"描述,步骤支持对象字段 obj() 和数组字段 arr():
* <pre>
* pc: widgetList -> bindData -> columnName
* mobile: tableWidget -> childWidgetList -> childWidgetList -> bindData -> columnName
* mobile: widgetList -> childWidgetList -> bindData -> columnName
* </pre>
* 后续新增读取源(如 otherWidgetList)时,只需在下方追加一行
* {@code collectColumnNamesByPath(widgetJson, names, "other", arr("otherWidgetList"))}。
*
* @param widgetJson 在线表单的 widgetJson。
* @return 绑定的字段名列集合。
*/
public static Set<String> collectBindColumnNames(JSONObject widgetJson) {
Set<String> names = new HashSet<>();
if (widgetJson == null) {
return names;
}
// pc 端: widgetList -> bindData -> columnName
collectColumnNamesByPath(widgetJson, names, "pc", arr("widgetList"));
// mobile 端(原 tableWidget 结构): tableWidget -> childWidgetList -> childWidgetList -> bindData -> columnName
collectColumnNamesByPath(widgetJson, names, "mobile", obj("tableWidget"), arr("childWidgetList"), arr("childWidgetList"));
// mobile 端(兜底 widgetList 结构): widgetList -> childWidgetList -> bindData -> columnName
collectColumnNamesByPath(widgetJson, names, "mobile", arr("widgetList"), arr("childWidgetList"));
return names;
}
/**
* 按"设备端 + 字段步骤链"读取 columnName。
* 从 widgetJson 的 mode 节点开始,依次下钻 steps 中的每个字段步骤,
* 最后一个步骤的节点即为控件,取其 bindData.columnName。
*
* @param widgetJson widgetJson 根对象。
* @param names 收集结果。
* @param mode 设备端 key,如 pc/mobile。
* @param steps 字段步骤链,支持对象字段 obj() 与数组字段 arr()。
*/
private static void collectColumnNamesByPath(JSONObject widgetJson, Set<String> names, String mode, PathStep... steps) {
JSONObject modeObj = widgetJson.getJSONObject(mode);
if (modeObj == null) {
return;
}
collectColumnNamesByStep(modeObj, 0, steps, names);
}
/**
* 逐层下钻字段步骤链,到达末尾时读取控件的 bindData.columnName。
*/
private static void collectColumnNamesByStep(JSONObject node, int index, PathStep[] steps, Set<String> names) {
if (index >= steps.length) {
addColumnName(node, names);
return;
}
PathStep step = steps[index];
if (step.isArray) {
JSONArray array = node.getJSONArray(step.field);
if (CollUtil.isEmpty(array)) {
return;
}
for (int i = 0; i < array.size(); i++) {
collectColumnNamesByStep(array.getJSONObject(i), index + 1, steps, names);
}
} else {
JSONObject child = node.getJSONObject(step.field);
if (child == null) {
return;
}
collectColumnNamesByStep(child, index + 1, steps, names);
}
}
/**
* 读取路径步骤:对象字段(obj) 或 数组字段(arr)。
*/
private static class PathStep {
final boolean isArray;
final String field;
private PathStep(boolean isArray, String field) {
this.isArray = isArray;
this.field = field;
}
}
/** 对象字段步骤,如 tableWidget。 */
private static PathStep obj(String field) {
return new PathStep(false, field);
}
/** 数组字段步骤,如 widgetList、childWidgetList。 */
private static PathStep arr(String field) {
return new PathStep(true, field);
}
/**
* 从控件对象中提取 bindData.columnName。
*/
private static void addColumnName(JSONObject widget, Set<String> names) {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null) {
String columnName = bindData.getString("columnName");
if (StrUtil.isNotEmpty(columnName)) {
names.add(columnName);
}
}
}
/**
* 收集子表控件(widgetType=100)的 bindData.tableId → props 映射。
*
* @param widgetJson 在线表单的 widgetJson。
* @return tableId → 子表控件 props 的映射。
*/
public static Map<String, JSONObject> collectSubTableProps(JSONObject widgetJson) {
Map<String, JSONObject> resultMap = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList == null) {
return;
}
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理 widgetType=100 的子表控件
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String tableId = bindData.getString("tableId");
if (StrUtil.isBlank(tableId)) {
continue;
}
JSONObject props = childWidget.getJSONObject("props");
if (props != null) {
resultMap.put(tableId, props);
}
}
});
return resultMap;
}
/**
* 收集子表控件(widgetType=100) bindData.tableName 的名称集合。
*
* @param widgetJson 在线表单的 widgetJson。
* @return 子表名称集合。
*/
public static Set<String> collectSubTableNames(JSONObject widgetJson) {
Set<String> tableNameSet = new HashSet<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList == null) {
return;
}
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理 widgetType=100 的子表控件
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
if (bindData.containsKey("tableName")) {
tableNameSet.add(bindData.getString("tableName"));
}
}
});
return tableNameSet;
}
/**
* 构建 columnName → 控件 widget 的映射。
*
* @param widgetJson 在线表单的 widgetJson。
* @return 字段名 → widget 的映射。
*/
public static Map<String, JSONObject> buildColumnNameWidgetMap(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
map.put(name, widget);
}
}
});
return map;
}
/**
* 构建 fieldName → widget 的映射(用于主表已有字段匹配)。
* key 优先取 bindData.columnFieldName,兼容 bindData.columnName。
*
* @param widgetJson 在线表单的 widgetJson。
* @return 字段名 → widget 的映射。
*/
public static Map<String, JSONObject> buildFieldNameWidgetMap(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData == null) {
return;
}
String fieldName = bindData.getString("columnFieldName");
if (StrUtil.isEmpty(fieldName)) {
fieldName = bindData.getString("columnName");
}
if (StrUtil.isNotEmpty(fieldName)) {
map.put(fieldName, widget);
}
});
return map;
}
/**
* 构建子表控件 tableColumnList 的 columnId → 列对象 映射。
* 过滤掉 fieldType=1 的列(如序号、合计等非真实字段)。
*
* @param widgetJson 在线表单的 widgetJson。
* @return columnId → 子表列对象 的映射。
*/
public static Map<String, JSONObject> buildSubTableColumnWidgetMap(JSONObject widgetJson) {
Map<String, JSONObject> resultMap = new HashMap<>();
walkAllModes(widgetJson, widget -> {
JSONArray widgetList = widget.getJSONArray("widgetList");
if (widgetList == null) {
return;
}
for (int i = 0; i < widgetList.size(); i++) {
JSONObject childWidget = widgetList.getJSONObject(i);
// 只处理 widgetType=100 的子表控件
Integer widgetType = childWidget.getInteger("widgetType");
if (widgetType == null || widgetType != 100) {
continue;
}
JSONObject bindData = childWidget.getJSONObject("bindData");
if (bindData == null) {
continue;
}
String tableId = bindData.getString("tableId");
if (StrUtil.isBlank(tableId)) {
continue;
}
JSONObject props = childWidget.getJSONObject("props");
if (props == null) {
continue;
}
JSONArray tableColumnList = props.getJSONArray("tableColumnList");
for (int j = 0; j < tableColumnList.size(); j++) {
JSONObject tableColumn = tableColumnList.getJSONObject(j);
// 过滤掉 fieldType=1 的列(如序号、合计等非真实字段),避免被识别为需要新增的字段
Integer fieldType = tableColumn.getInteger("fieldType");
if (fieldType != null && fieldType == 1) {
continue;
}
resultMap.put(tableColumn.getString("columnId"), tableColumn);
}
}
});
return resultMap;
}
/**
* 构建 mobile 端 columnName → widget 的映射。
* 兼容两种结构:
* 1. mobile -> tableWidget -> childWidgetList -> childWidgetList -> bindData -> columnName
* 2. mobile -> widgetList -> childWidgetList -> bindData -> columnName (缺少 tableWidget 时)
*
* @param widgetJson 在线表单的 widgetJson。
* @return mobile 端字段名 → widget 的映射。
*/
public static Map<String, JSONObject> buildMobileColumnNameWidgetMap(JSONObject widgetJson) {
Map<String, JSONObject> map = new HashMap<>();
JSONObject mobile = widgetJson.getJSONObject("mobile");
if (mobile == null) {
return map;
}
// 优先走原有结构: mobile.tableWidget
JSONObject tableWidget = mobile.getJSONObject("tableWidget");
if (tableWidget != null) {
JSONArray cardList = tableWidget.getJSONArray("childWidgetList");
if (cardList != null) {
for (int i = 0; i < cardList.size(); i++) {
JSONObject cardWidget = cardList.getJSONObject(i);
JSONArray fieldList = cardWidget.getJSONArray("childWidgetList");
if (fieldList == null) {
continue;
}
for (int j = 0; j < fieldList.size(); j++) {
JSONObject fieldWidget = fieldList.getJSONObject(j);
JSONObject bindData = fieldWidget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
map.put(name, fieldWidget);
}
}
}
}
}
return map;
}
// 兜底走新结构: mobile.widgetList -> childWidgetList -> bindData.columnName
JSONArray widgetList = mobile.getJSONArray("widgetList");
if (widgetList != null) {
for (int i = 0; i < widgetList.size(); i++) {
JSONObject containerWidget = widgetList.getJSONObject(i);
JSONArray childWidgetList = containerWidget.getJSONArray("childWidgetList");
if (childWidgetList == null) {
continue;
}
for (int j = 0; j < childWidgetList.size(); j++) {
JSONObject fieldWidget = childWidgetList.getJSONObject(j);
JSONObject bindData = fieldWidget.getJSONObject("bindData");
if (bindData != null && bindData.containsKey("columnName")) {
String name = bindData.getString("columnName");
if (StrUtil.isNotEmpty(name)) {
map.put(name, fieldWidget);
}
}
}
}
}
return map;
}
/**
* 在 widgetJson 中查找匹配 tableName 的子表控件(widgetType=100),回填 bindData.tableId。
*
* @param widgetJson 在线表单的 widgetJson。
* @param tableName 子表名称。
* @param tableId 子表Id。
*/
public static void backfillTableId(JSONObject widgetJson, String tableName, Long tableId) {
walkAllModes(widgetJson, widget -> {
if (widget.getInteger("widgetType") != null && 100 == widget.getInteger("widgetType")) {
JSONObject bindData = widget.getJSONObject("bindData");
if (bindData != null && tableName.equals(bindData.getString("tableName"))) {
bindData.put("tableId", tableId);
}
}
});
}
/**
* 遍历 "pc" 和 "mobile" 两个模式的控件树。
*/
private static void walkAllModes(JSONObject widgetJson, Consumer<JSONObject> visitor) {
for (String mode : new String[]{"pc", "mobile"}) {
JSONObject modeObj = widgetJson.getJSONObject(mode);
if (modeObj != null) {
walkWidgets(modeObj, visitor);
}
}
}
/**
* 递归遍历控件树,访问每个控件及其子控件(widgetList)。
*/
private static void walkWidgets(JSONObject node, Consumer<JSONObject> visitor) {
if (node == null) {
return;
}
visitor.accept(node);
JSONArray children = node.getJSONArray("widgetList");
if (children != null) {
for (int i = 0; i < children.size(); i++) {
walkWidgets(children.getJSONObject(i), visitor);
}
}
}
}
Loading…
Cancel
Save