|
|
|
@ -12,8 +12,12 @@ import apelet.common.core.util.MyPageUtil; |
|
|
|
import apelet.common.core.validator.UpdateGroup; |
|
|
|
import apelet.common.core.validator.UpdateGroup; |
|
|
|
import apelet.common.dbutil.object.SqlTable; |
|
|
|
import apelet.common.dbutil.object.SqlTable; |
|
|
|
import apelet.common.dbutil.object.SqlTableColumn; |
|
|
|
import apelet.common.dbutil.object.SqlTableColumn; |
|
|
|
|
|
|
|
import apelet.common.generator.model.OnlFormField; |
|
|
|
import apelet.common.generator.model.OnlFormHead; |
|
|
|
import apelet.common.generator.model.OnlFormHead; |
|
|
|
|
|
|
|
import apelet.common.generator.service.IOnlFormFieldService; |
|
|
|
import apelet.common.generator.service.IOnlFormHeadService; |
|
|
|
import apelet.common.generator.service.IOnlFormHeadService; |
|
|
|
|
|
|
|
import apelet.common.generator.utils.CustomUtil; |
|
|
|
|
|
|
|
import apelet.common.online.util.WidgetFieldTypeMapping; |
|
|
|
import apelet.common.log.annotation.OperationLog; |
|
|
|
import apelet.common.log.annotation.OperationLog; |
|
|
|
import apelet.common.log.model.constant.SysOperationLogType; |
|
|
|
import apelet.common.log.model.constant.SysOperationLogType; |
|
|
|
import apelet.common.online.config.OnlineProperties; |
|
|
|
import apelet.common.online.config.OnlineProperties; |
|
|
|
@ -49,6 +53,7 @@ import javax.annotation.Resource; |
|
|
|
import javax.validation.groups.Default; |
|
|
|
import javax.validation.groups.Default; |
|
|
|
import java.lang.reflect.Method; |
|
|
|
import java.lang.reflect.Method; |
|
|
|
import java.util.*; |
|
|
|
import java.util.*; |
|
|
|
|
|
|
|
import java.util.function.Consumer; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -83,6 +88,8 @@ public class OnlineFormController { |
|
|
|
@Autowired |
|
|
|
@Autowired |
|
|
|
IOnlFormHeadService onlFormHeadService; |
|
|
|
IOnlFormHeadService onlFormHeadService; |
|
|
|
@Autowired |
|
|
|
@Autowired |
|
|
|
|
|
|
|
IOnlFormFieldService onlFormFieldService; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
OnlinePageService onlinePageService; |
|
|
|
OnlinePageService onlinePageService; |
|
|
|
@Autowired |
|
|
|
@Autowired |
|
|
|
private OnlineProperties properties; |
|
|
|
private OnlineProperties properties; |
|
|
|
@ -326,6 +333,13 @@ public class OnlineFormController { |
|
|
|
onlineForm.setWidgetJson(jsonObject.toJSONString()); |
|
|
|
onlineForm.setWidgetJson(jsonObject.toJSONString()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ★ 新增: 处理新增字段和子表
|
|
|
|
|
|
|
|
JSONObject updatedJson = syncNewFieldsAndSubTables(onlineForm, onlineFormDto, JSON.parseObject(onlineForm.getWidgetJson())); |
|
|
|
|
|
|
|
if (updatedJson != null) { |
|
|
|
|
|
|
|
onlineForm.setWidgetJson(updatedJson.toJSONString()); |
|
|
|
|
|
|
|
onlineFormDto.setWidgetJson(updatedJson.toJSONString()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!onlineFormService.update(onlineForm, originalOnlineForm, datasourceIdSet)) { |
|
|
|
if (!onlineFormService.update(onlineForm, originalOnlineForm, datasourceIdSet)) { |
|
|
|
redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineFormKey(onlineForm.getFormId())).delete(); |
|
|
|
redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineFormKey(onlineForm.getFormId())).delete(); |
|
|
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); |
|
|
|
return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); |
|
|
|
@ -685,4 +699,283 @@ public class OnlineFormController { |
|
|
|
} |
|
|
|
} |
|
|
|
return ResponseResult.success(onlineFormList); |
|
|
|
return ResponseResult.success(onlineFormList); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ============ 字段diff+同步 相关私有方法 ============
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 对比 widgetJson 与现有 OnlFormField / OnlFormHead 识别新增字段和子表, |
|
|
|
|
|
|
|
* 创建 OnlFormField 记录、同步数据库、创建 OnlineColumn、回填 columnId/relationId。 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @return 修改后的 widgetJson (含回填的 columnId/relationId),无变更则返回 null |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private JSONObject syncNewFieldsAndSubTables(OnlineForm form, OnlineFormDto dto, JSONObject widgetJson) { |
|
|
|
|
|
|
|
// 1. 获取数据源 → OnlineTable → OnlFormHead
|
|
|
|
|
|
|
|
OnlineDatasource datasource = onlineDatasourceService.getById(dto.getDatasourceIdList().get(0)); |
|
|
|
|
|
|
|
OnlineTable onlineTable = onlineTableService.getById(datasource.getMasterTableId()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OnlFormHead onlFormHead = onlFormHeadService.getOne( |
|
|
|
|
|
|
|
new LambdaQueryWrapper<OnlFormHead>() |
|
|
|
|
|
|
|
.eq(OnlFormHead::getTableName, onlineTable.getTableName())); |
|
|
|
|
|
|
|
if (onlFormHead == null) { |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean changed = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// === Diff 新增字段 ===
|
|
|
|
|
|
|
|
Set<String> widgetColumnNames = collectAllBindColumnNames(widgetJson); |
|
|
|
|
|
|
|
List<OnlFormField> existingFields = onlFormFieldService.list( |
|
|
|
|
|
|
|
new LambdaQueryWrapper<OnlFormField>().eq(OnlFormField::getHeadId, onlFormHead.getId())); |
|
|
|
|
|
|
|
Set<String> existingFieldNames = existingFields.stream() |
|
|
|
|
|
|
|
.map(OnlFormField::getFieldName).collect(Collectors.toSet()); |
|
|
|
|
|
|
|
Set<String> newColumnNames = new HashSet<>(widgetColumnNames); |
|
|
|
|
|
|
|
newColumnNames.removeAll(existingFieldNames); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!newColumnNames.isEmpty()) { |
|
|
|
|
|
|
|
Map<String, JSONObject> nameToWidget = mapColumnNameToWidget(widgetJson); |
|
|
|
|
|
|
|
OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 预校验:过滤掉 widgetType 不在映射中的控件,避免 getFieldTypeId 抛异常导致部分数据已入库
|
|
|
|
|
|
|
|
Set<String> validColumnNames = new LinkedHashSet<>(); |
|
|
|
|
|
|
|
for (String fieldName : newColumnNames) { |
|
|
|
|
|
|
|
JSONObject widget = nameToWidget.get(fieldName); |
|
|
|
|
|
|
|
Integer widgetType = widget != null ? widget.getInteger("widgetType") : null; |
|
|
|
|
|
|
|
if (widgetType == null || !WidgetFieldTypeMapping.isDataWidget(widgetType)) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
validColumnNames.add(fieldName); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (validColumnNames.isEmpty()) { |
|
|
|
|
|
|
|
return changed ? widgetJson : null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (String fieldName : validColumnNames) { |
|
|
|
|
|
|
|
JSONObject widget = nameToWidget.get(fieldName); |
|
|
|
|
|
|
|
Integer widgetType = widget.getInteger("widgetType"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OnlFormField field = new OnlFormField(); |
|
|
|
|
|
|
|
field.setHeadId(onlFormHead.getId()); |
|
|
|
|
|
|
|
field.setFieldName(fieldName); |
|
|
|
|
|
|
|
field.setFieldType(WidgetFieldTypeMapping.getFieldTypeId(widgetType)); |
|
|
|
|
|
|
|
Integer length = WidgetFieldTypeMapping.getDefaultLength(widgetType); |
|
|
|
|
|
|
|
if (length != null) { |
|
|
|
|
|
|
|
field.setFieldLength(length); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
field.setIsEmpty(1); |
|
|
|
|
|
|
|
field.setIsDefault(0); |
|
|
|
|
|
|
|
field.setRowDisabled(0); |
|
|
|
|
|
|
|
field.setSyncFlag(0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 从 widget 的 bindData.columnComment 设置字段备注
|
|
|
|
|
|
|
|
JSONObject bindData = widget.getJSONObject("bindData"); |
|
|
|
|
|
|
|
if (bindData != null && bindData.containsKey("columnComment")) { |
|
|
|
|
|
|
|
field.setFieldRemark(bindData.getString("columnComment")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
onlFormFieldService.save(field); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ALTER TABLE ADD COLUMN
|
|
|
|
|
|
|
|
onlFormHeadService.syncDB(onlFormHead, 0L); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 批量获取所有列信息,避免 N+1 查询
|
|
|
|
|
|
|
|
List<SqlTableColumn> allColumns = onlineDblinkService.getDblinkTableColumnList( |
|
|
|
|
|
|
|
dblink, onlineTable.getTableName()); |
|
|
|
|
|
|
|
Map<String, SqlTableColumn> columnMap = new HashMap<>(); |
|
|
|
|
|
|
|
if (CollUtil.isNotEmpty(allColumns)) { |
|
|
|
|
|
|
|
for (SqlTableColumn col : allColumns) { |
|
|
|
|
|
|
|
if (col.getColumnName() != null) { |
|
|
|
|
|
|
|
columnMap.put(col.getColumnName().toLowerCase(), col); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 增量创建 OnlineColumn + 回填 columnId 到 widgetJson
|
|
|
|
|
|
|
|
for (String fieldName : validColumnNames) { |
|
|
|
|
|
|
|
SqlTableColumn sqlCol = columnMap.get(fieldName.toLowerCase()); |
|
|
|
|
|
|
|
if (sqlCol != null) { |
|
|
|
|
|
|
|
OnlineColumn newCol = new OnlineColumn(); |
|
|
|
|
|
|
|
newCol.setColumnName(sqlCol.getColumnName()); |
|
|
|
|
|
|
|
newCol.setColumnType(sqlCol.getColumnType()); |
|
|
|
|
|
|
|
newCol.setFullColumnType(sqlCol.getFullColumnType()); |
|
|
|
|
|
|
|
if (sqlCol.getColumnDefault() != null) { |
|
|
|
|
|
|
|
newCol.setColumnDefault(sqlCol.getColumnDefault().toString()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
newCol.setNullable(sqlCol.getNullable()); |
|
|
|
|
|
|
|
newCol.setTableId(onlineTable.getTableId()); |
|
|
|
|
|
|
|
onlineColumnService.save(newCol); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 回填 columnId 到 widgetJson
|
|
|
|
|
|
|
|
JSONObject widget = nameToWidget.get(fieldName); |
|
|
|
|
|
|
|
if (widget != null) { |
|
|
|
|
|
|
|
JSONObject bindData2 = widget.getJSONObject("bindData"); |
|
|
|
|
|
|
|
if (bindData2 != null) { |
|
|
|
|
|
|
|
bindData2.put("columnId", newCol.getColumnId()); |
|
|
|
|
|
|
|
bindData2.put("tableId", onlineTable.getTableId()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
changed = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// === Diff 新增子表 ===
|
|
|
|
|
|
|
|
Set<String> widgetSubTables = collectSubTableNames(widgetJson); |
|
|
|
|
|
|
|
List<OnlFormHead> existingSubTables = onlFormHeadService.list( |
|
|
|
|
|
|
|
new LambdaQueryWrapper<OnlFormHead>() |
|
|
|
|
|
|
|
.eq(OnlFormHead::getSort, onlFormHead.getTableName()) |
|
|
|
|
|
|
|
.eq(OnlFormHead::getLine, 1)); |
|
|
|
|
|
|
|
Set<String> existingSubTableNames = existingSubTables.stream() |
|
|
|
|
|
|
|
.map(OnlFormHead::getTableName).collect(Collectors.toSet()); |
|
|
|
|
|
|
|
Set<String> newSubTableNames = new HashSet<>(widgetSubTables); |
|
|
|
|
|
|
|
newSubTableNames.removeAll(existingSubTableNames); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!newSubTableNames.isEmpty()) { |
|
|
|
|
|
|
|
// 获取主表的主键字段(id)作为关联字段
|
|
|
|
|
|
|
|
OnlineColumn masterPkColumn = onlineColumnService.getOne( |
|
|
|
|
|
|
|
new LambdaQueryWrapper<OnlineColumn>() |
|
|
|
|
|
|
|
.eq(OnlineColumn::getTableId, onlineTable.getTableId()) |
|
|
|
|
|
|
|
.eq(OnlineColumn::getColumnName, "id")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (String subTableName : newSubTableNames) { |
|
|
|
|
|
|
|
OnlFormHead subHead = onlFormHeadService.createMetaAndSync( |
|
|
|
|
|
|
|
subTableName, |
|
|
|
|
|
|
|
onlFormHead.getTableTxt() + "附表", |
|
|
|
|
|
|
|
CustomUtil.MetaType_Entry, |
|
|
|
|
|
|
|
onlFormHead.getTableName()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 获取子表的 OnlineTable
|
|
|
|
|
|
|
|
OnlineTable subOnlineTable = onlineTableService.getOne( |
|
|
|
|
|
|
|
new LambdaQueryWrapper<OnlineTable>() |
|
|
|
|
|
|
|
.eq(OnlineTable::getTableName, subTableName.toLowerCase())); |
|
|
|
|
|
|
|
if (subOnlineTable == null) { |
|
|
|
|
|
|
|
// 从DB获取表结构并创建 OnlineTable
|
|
|
|
|
|
|
|
SqlTable subSqlTable = onlineDblinkService.getDblinkTable( |
|
|
|
|
|
|
|
onlineDblinkService.getById(datasource.getDblinkId()), subTableName); |
|
|
|
|
|
|
|
if (subSqlTable != null) { |
|
|
|
|
|
|
|
subOnlineTable = onlineTableService.saveNewFromSqlTable(subSqlTable); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建子表数据源
|
|
|
|
|
|
|
|
OnlineDatasource subDatasource = new OnlineDatasource(); |
|
|
|
|
|
|
|
subDatasource.setDblinkId(datasource.getDblinkId()); |
|
|
|
|
|
|
|
if (subOnlineTable != null) { |
|
|
|
|
|
|
|
subDatasource.setMasterTableId(subOnlineTable.getTableId()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
onlineDatasourceService.save(subDatasource); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建 1:N 关系
|
|
|
|
|
|
|
|
OnlineDatasourceRelation relation = new OnlineDatasourceRelation(); |
|
|
|
|
|
|
|
relation.setDatasourceId(datasource.getDatasourceId()); |
|
|
|
|
|
|
|
relation.setRelationType(1); |
|
|
|
|
|
|
|
if (subOnlineTable != null) { |
|
|
|
|
|
|
|
relation.setSlaveTableId(subOnlineTable.getTableId()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (masterPkColumn != null) { |
|
|
|
|
|
|
|
relation.setMasterColumnId(masterPkColumn.getColumnId()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
onlineDatasourceRelationService.save(relation); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backfillRelationId(widgetJson, subTableName, relation.getRelationId()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
changed = true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return changed ? widgetJson : null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 遍历 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=403 (TableContainer) 的 props.tableName 值。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private Set<String> collectSubTableNames(JSONObject widgetJson) { |
|
|
|
|
|
|
|
Set<String> names = new HashSet<>(); |
|
|
|
|
|
|
|
walkAllModes(widgetJson, widget -> { |
|
|
|
|
|
|
|
if (widget.getInteger("widgetType") != null && 403 == widget.getInteger("widgetType")) { |
|
|
|
|
|
|
|
JSONObject props = widget.getJSONObject("props"); |
|
|
|
|
|
|
|
if (props != null && props.containsKey("tableName")) { |
|
|
|
|
|
|
|
String tn = props.getString("tableName"); |
|
|
|
|
|
|
|
if (StrUtil.isNotEmpty(tn)) { |
|
|
|
|
|
|
|
names.add(tn); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
return names; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 构建 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; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 在 widgetJson 中查找匹配 tableName 的 TableContainer 控件,回填 relationId。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void backfillRelationId(JSONObject widgetJson, String tableName, Long relationId) { |
|
|
|
|
|
|
|
walkAllModes(widgetJson, widget -> { |
|
|
|
|
|
|
|
if (widget.getInteger("widgetType") != null && 403 == widget.getInteger("widgetType")) { |
|
|
|
|
|
|
|
JSONObject props = widget.getJSONObject("props"); |
|
|
|
|
|
|
|
if (props != null && tableName.equals(props.getString("tableName"))) { |
|
|
|
|
|
|
|
props.put("relationId", relationId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 遍历 "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); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|