From 39e1d5ce3d0d318dbbb8c689f8bd8fc8fb2425d2 Mon Sep 17 00:00:00 2001 From: chenchuchuan <13554600537@163.com> Date: Fri, 31 Jul 2026 18:12:59 +0800 Subject: [PATCH] =?UTF-8?q?feat(common):=20=E6=94=AF=E6=8C=81=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E7=AB=AF=E8=A1=A8=E5=8D=95=E5=AD=97=E6=AE=B5=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 collectMobileBindColumnNames 方法收集移动端列名 - 新增 mapMobileColumnNameToWidget 方法构建移动端列名到组件映射 - 实现 createNewSubTables 方法统一处理子表创建逻辑 - 添加 syncMobileSubTables 方法同步移动端子表和字段 - 实现 backfillMobileSubTableId 方法回写移动端子表ID - 完善 syncMobileSubTableNewFields 方法处理移动端子表新增字段 - 重构 SysUserService 接口移除组织ID参数 - 优化 TaskInfoVo 类型添加解析后任务标题字段 --- .../upms/controller/SysUserController.java | 28 +- .../tenantadmin/upms/service/SysUserService.java | 10 +- .../upms/service/impl/SysUserServiceImpl.java | 15 +- .../java/apelet/common/flow/vo/TaskInfoVo.java | 6 + .../online/controller/OnlineFormController.java | 380 +++++++++++++++++++-- 5 files changed, 365 insertions(+), 74 deletions(-) diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java index 060bae5..1c3c15a 100644 --- a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/controller/SysUserController.java @@ -70,7 +70,6 @@ public class SysUserController { * @param deptPostIdListString 逗号分隔的部门岗位Id列表。 * @param dataPermIdListString 逗号分隔的数据权限Id列表。 * @param roleIdListString 逗号分隔的角色Id列表。 - * @param deptIdListString 逗号分隔的组织Id列表(可选,写入SysUserRole.deptId)。 * @return 应答结果对象,包含新增用户的主键Id。 */ @ApiOperationSupport(ignoreParameters = { @@ -83,26 +82,21 @@ public class SysUserController { @MyRequestBody SysUserDto sysUserDto, @MyRequestBody String deptPostIdListString, @MyRequestBody String dataPermIdListString, - @MyRequestBody String roleIdListString, - @MyRequestBody String deptIdListString) { + @MyRequestBody String roleIdListString) { String errorMessage = MyCommonUtil.getModelValidationError(sysUserDto); if (errorMessage != null) { return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); } SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class); CallResult result = sysUserService.verifyRelatedData( - sysUser, null, roleIdListString, deptPostIdListString, dataPermIdListString, deptIdListString); + sysUser, null, roleIdListString, deptPostIdListString, dataPermIdListString); if (!result.isSuccess()) { return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); } Set deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference>(){}); Set roleIdSet = result.getData().getObject("roleIdSet", new TypeReference>(){}); Set dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference>(){}); - Set deptIdSet = null; - if (result.getData().containsKey("deptIdSet")) { - deptIdSet = result.getData().getObject("deptIdSet", new TypeReference>(){}); - } - sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet, deptIdSet); + sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet); return ResponseResult.success(sysUser.getUserId()); } @@ -126,14 +120,14 @@ public class SysUserController { } SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class); CallResult result = sysUserService.verifyRelatedData( - sysUser, null, "1801273688285270000", "1821796396504723500", "1821788867364139000", null); + sysUser, null, "1801273688285270000", "1821796396504723500", "1821788867364139000"); if (!result.isSuccess()) { return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); } Set deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference>(){}); Set roleIdSet = result.getData().getObject("roleIdSet", new TypeReference>(){}); Set dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference>(){}); - SysUser sysUserO = sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet, null); + SysUser sysUserO = sysUserService.saveNew(sysUser, roleIdSet, deptPostIdSet, dataPermIdSet); try { ObjectValue userInfoObj = new ObjectValue("sys_user_info"); @@ -160,7 +154,6 @@ public class SysUserController { * @param deptPostIdListString 逗号分隔的部门岗位Id列表。 * @param dataPermIdListString 逗号分隔的数据权限Id列表。 * @param roleIdListString 逗号分隔的角色Id列表。 - * @param deptIdListString 逗号分隔的组织Id列表(可选,写入SysUserRole.deptId)。 * @return 应答结果对象。 */ @ApiOperationSupport(ignoreParameters = { @@ -172,8 +165,7 @@ public class SysUserController { @MyRequestBody SysUserDto sysUserDto, @MyRequestBody String deptPostIdListString, @MyRequestBody String dataPermIdListString, - @MyRequestBody String roleIdListString, - @MyRequestBody String deptIdListString) { + @MyRequestBody String roleIdListString) { String errorMessage = MyCommonUtil.getModelValidationError(sysUserDto, true); if (errorMessage != null) { return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); @@ -184,18 +176,14 @@ public class SysUserController { } SysUser sysUser = MyModelUtil.copyTo(sysUserDto, SysUser.class); CallResult result = sysUserService.verifyRelatedData( - sysUser, originalUser, roleIdListString, deptPostIdListString, dataPermIdListString, deptIdListString); + sysUser, originalUser, roleIdListString, deptPostIdListString, dataPermIdListString); if (!result.isSuccess()) { return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, result.getErrorMessage()); } Set roleIdSet = result.getData().getObject("roleIdSet", new TypeReference>(){}); Set deptPostIdSet = result.getData().getObject("deptPostIdSet", new TypeReference>(){}); Set dataPermIdSet = result.getData().getObject("dataPermIdSet", new TypeReference>(){}); - Set deptIdSet = null; - if (result.getData().containsKey("deptIdSet")) { - deptIdSet = result.getData().getObject("deptIdSet", new TypeReference>(){}); - } - if (!sysUserService.update(sysUser, originalUser, roleIdSet, deptPostIdSet, dataPermIdSet, deptIdSet)) { + if (!sysUserService.update(sysUser, originalUser, roleIdSet, deptPostIdSet, dataPermIdSet)) { return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); } return ResponseResult.success(); diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java index 691470a..693ddd3 100644 --- a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/SysUserService.java @@ -32,11 +32,10 @@ public interface SysUserService extends IBaseService { * @param user 新增的用户对象。 * @param roleIdSet 用户角色Id集合。 * @param deptPostIdSet 部门岗位Id集合。 - * @param dataPermIdSet 数据权限Id集合。 * @param deptIdSet 组织Id集合(写入SysUserRole.deptId)。 * @return 新增后的用户对象。 */ - SysUser saveNew(SysUser user, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet, Set deptIdSet); + SysUser saveNew(SysUser user, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet); /** * 更新用户对象。 @@ -46,10 +45,9 @@ public interface SysUserService extends IBaseService { * @param roleIdSet 用户角色Id列表。 * @param deptPostIdSet 部门岗位Id集合。 * @param dataPermIdSet 数据权限Id集合。 - * @param deptIdSet 组织Id集合(写入SysUserRole.deptId)。 * @return 更新成功返回true,否则false。 */ - boolean update(SysUser user, SysUser originalUser, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet, Set deptIdSet); + boolean update(SysUser user, SysUser originalUser, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet); /** * 修改用户密码。 @@ -229,11 +227,9 @@ public interface SysUserService extends IBaseService { * @param roleIds 逗号分隔的角色Id列表字符串。 * @param deptPostIds 逗号分隔的部门岗位Id列表字符串。 * @param dataPermIds 逗号分隔的数据权限Id列表字符串。 - * @param deptIds 逗号分隔的组织Id列表字符串(可选,写入SysUserRole.deptId)。 * @return 验证结果。 */ - CallResult verifyRelatedData( - SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds, String deptIds); + CallResult verifyRelatedData(SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds); /** * 根据云之家openId获取当前用户信息 diff --git a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java index d4c40d4..0f8572b 100644 --- a/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java +++ b/application-tenant/tenant-admin/src/main/java/apelet/tenantadmin/upms/service/impl/SysUserServiceImpl.java @@ -198,7 +198,7 @@ public class SysUserServiceImpl extends BaseService implements Sy */ @Transactional(rollbackFor = Exception.class) @Override - public SysUser saveNew(SysUser user, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet, Set deptIdSet) { + public SysUser saveNew(SysUser user, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet) { user.setUserId(idGenerator.nextLongId()); user.setPassword(passwordEncoder.encode(user.getPassword())); user.setUserStatus(SysUserStatus.STATUS_NORMAL); @@ -246,7 +246,7 @@ public class SysUserServiceImpl extends BaseService implements Sy */ @Transactional(rollbackFor = Exception.class) @Override - public boolean update(SysUser user, SysUser originalUser, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet, Set deptIdSet) { + public boolean update(SysUser user, SysUser originalUser, Set roleIdSet, Set deptPostIdSet, Set dataPermIdSet) { user.setLoginName(originalUser.getLoginName()); user.setPassword(originalUser.getPassword()); MyModelUtil.fillCommonsForUpdate(user, originalUser); @@ -567,12 +567,11 @@ public class SysUserServiceImpl extends BaseService implements Sy * @param originalSysUser 原有对象。 * @param roleIds 逗号分隔的角色Id列表字符串。 * @param deptPostIds 逗号分隔的部门岗位Id列表字符串。 - * @param dataPermIds 逗号分隔的数据权限Id列表字符串。 * @return 验证结果。 */ @Override public CallResult verifyRelatedData( - SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds, String deptIds) { + SysUser sysUser, SysUser originalSysUser, String roleIds, String deptPostIds, String dataPermIds) { JSONObject jsonObject = new JSONObject(); if (StrUtil.isBlank(deptPostIds)) { return CallResult.error("数据验证失败,用户的部门岗位数据不能为空!"); @@ -605,14 +604,6 @@ public class SysUserServiceImpl extends BaseService implements Sy && !sysDeptService.existId(sysUser.getDeptId())) { return CallResult.error("数据验证失败,关联的用户部门Id并不存在,请刷新后重试!"); } - // 验证组织ID(可选,写入SysUserRole.deptId) - if (StrUtil.isNotBlank(deptIds)) { - Set deptIdSet = Arrays.stream(deptIds.split(",")).map(Long::valueOf).collect(Collectors.toSet()); - if (!sysDeptService.existAllPrimaryKeys(deptIdSet)) { - return CallResult.error("数据验证失败,存在不合法的组织数据,请刷新后重试!"); - } - jsonObject.put("deptIdSet", deptIdSet); - } return CallResult.ok(jsonObject); } diff --git a/common/common-flow/src/main/java/apelet/common/flow/vo/TaskInfoVo.java b/common/common-flow/src/main/java/apelet/common/flow/vo/TaskInfoVo.java index e495e83..c497baf 100644 --- a/common/common-flow/src/main/java/apelet/common/flow/vo/TaskInfoVo.java +++ b/common/common-flow/src/main/java/apelet/common/flow/vo/TaskInfoVo.java @@ -76,4 +76,10 @@ public class TaskInfoVo { */ @Schema(description = "任务节点的自定义变量列表") List variableList; + + /** + * 解析后的任务标题。 + */ + @Schema(description = "解析后的任务标题") + private String title; } diff --git a/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java b/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java index 4c29f2b..9493494 100644 --- a/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java +++ b/common/common-online/src/main/java/apelet/common/online/controller/OnlineFormController.java @@ -760,7 +760,11 @@ public class OnlineFormController { boolean changed = false; // === Diff 新增字段 === + // 这里是取pc 的 还要取一下 moblie的 Set widgetColumnNames = collectAllBindColumnNames(widgetJson); + // 收集 mobile 端的列名(遍历 childWidgetList 路径) + widgetColumnNames.addAll(collectMobileBindColumnNames(widgetJson)); + List existingFields = onlFormFieldService.list( new LambdaQueryWrapper().eq(OnlFormField::getHeadId, onlFormHead.getId())); Set existingFieldNames = existingFields.stream() @@ -786,12 +790,17 @@ public class OnlineFormController { } if (!newColumnNames.isEmpty()) { Map nameToWidget = mapColumnNameToWidget(widgetJson); + // 收集 mobile 端的 widget 映射(遍历 childWidgetList 路径) + Map mobileNameToWidget = mapMobileColumnNameToWidget(widgetJson); OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); // 预校验:过滤掉 widgetType 不在映射中的控件,避免 getFieldTypeId 抛异常导致部分数据已入库 Set validColumnNames = new LinkedHashSet<>(); for (String fieldName : newColumnNames) { JSONObject widget = nameToWidget.get(fieldName); + if (widget == null) { + widget = mobileNameToWidget.get(fieldName); + } Integer widgetType = widget != null ? widget.getInteger("widgetType") : null; if (widgetType == null || !WidgetFieldTypeMapping.isDataWidget(widgetType)) { continue; @@ -804,6 +813,9 @@ public class OnlineFormController { for (String fieldName : validColumnNames) { JSONObject widget = nameToWidget.get(fieldName); + if (widget == null) { + widget = mobileNameToWidget.get(fieldName); + } // 从 widget 的 bindData.columnComment 设置字段备注 JSONObject bindData = widget.getJSONObject("bindData"); String ref = bindData.containsKey("slaveTableId") ? bindData.getString("slaveTableId") : null; @@ -829,15 +841,26 @@ public class OnlineFormController { for (String fieldName : validColumnNames) { SqlTableColumn sqlCol = columnMap.get(fieldName.toLowerCase()); if (sqlCol != null) { - // 回填 columnId 到 widgetJson JSONObject widget = nameToWidget.get(fieldName); - String showName = widget == null ? null : widget.getString("showName"); + JSONObject mobileWidget = mobileNameToWidget.get(fieldName); + String showName = widget != null ? widget.getString("showName") : (mobileWidget != null ? mobileWidget.getString("showName") : null); long columnId = onlineColumnService.saveBySqlTable(sqlCol, onlineTable.getTableId(), showName); + // 回写 pc 端 widget if (widget != null) { JSONObject bindData2 = widget.getJSONObject("bindData"); if (bindData2 != null) { bindData2.put("columnId", columnId); bindData2.put("tableId", onlineTable.getTableId()); + bindData2.put("columnFieldName", fieldName); + } + } + // 回写 mobile 端 widget + if (mobileWidget != null) { + JSONObject mobileBindData = mobileWidget.getJSONObject("bindData"); + if (mobileBindData != null) { + mobileBindData.put("columnId", columnId); + mobileBindData.put("tableId", onlineTable.getTableId()); + mobileBindData.put("columnFieldName", fieldName); } } } @@ -857,7 +880,7 @@ public class OnlineFormController { if (MapUtil.isNotEmpty(tableIdAndPropMap)) { //搜集的所有tableId, 要么是 tableId(已存在),要么是 tableName(不存在) Set allTableIdSet = tableIdAndPropMap.keySet(); - Set newSubTableNames = new HashSet<>(); + Set newSubTableNames = new HashSet<>(); Set existingSubTableIdSet = new HashSet<>(); for (String tableId : allTableIdSet) { if (tableId.matches("\\d+")) { @@ -968,43 +991,63 @@ public class OnlineFormController { } } //step2 : 不存在的附表,新增 - if (newSubTableNames.isEmpty()) { - //如果他为空,说明我们走tableId取值没取到,我们就去一下 tableName; - newSubTableNames = this.collectSubTableName(widgetJson); - } - if (!newSubTableNames.isEmpty()) { - - for (String subTableName : newSubTableNames) { - OnlFormHead subHead = onlFormHeadService.createMetaAndSync(subTableName, onlFormHead.getTableTxt() + "附表", CustomUtil.MetaType_Entry, onlFormHead.getTableName()); - // 获取子表的 OnlineTable - OnlineTable subOnlineTable = onlineTableService.getOne(new LambdaQueryWrapper().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); - } - } - OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); - onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, datasource, null, dblink); + changed |= createNewSubTables(widgetJson, onlFormHead, datasource, newSubTableNames); + } else { + // tableIdAndPropMap 为空,说明走 tableId 取值没取到,兜底通过 tableName 收集附表并创建 + changed |= createNewSubTables(widgetJson, onlFormHead, datasource, new HashSet<>()); + } + // === Diff mobile 端子表 === + // 路径: mobile -> widgetList -> [widgetType=102] -> bindData -> tableName/tableId + changed |= syncMobileSubTables(widgetJson, onlFormHead, datasource); + return changed ? widgetJson : null; + } - if (subOnlineTable != null) { - backfillRelationId(widgetJson, subTableName, subOnlineTable.getTableId()); - } + /** + * 创建不存在的附表:新建 OnlFormHead/OnlineTable、绑定数据源关系、回填 relationId 并追加 childName。 + * 若 newSubTableNames 为空(tableIdAndPropMap 走 tableId 取值没取到),兜底通过 tableName 收集附表。 + * + * @param widgetJson 表单 JSON + * @param onlFormHead 主表 + * @param datasource 数据源 + * @param newSubTableNames 附表名集合(可为空) + * @return 是否有变更 + */ + private boolean createNewSubTables(JSONObject widgetJson, OnlFormHead onlFormHead, OnlineDatasource datasource, Set newSubTableNames) { + if (newSubTableNames.isEmpty()) { + // 如果他为空,说明我们走tableId取值没取到,我们就去一下 tableName; + newSubTableNames = this.collectSubTableName(widgetJson); + } + if (newSubTableNames.isEmpty()) { + return false; + } + for (String subTableName : newSubTableNames) { + OnlFormHead subHead = onlFormHeadService.createMetaAndSync(subTableName, onlFormHead.getTableTxt() + "附表", CustomUtil.MetaType_Entry, onlFormHead.getTableName()); + // 获取子表的 OnlineTable + OnlineTable subOnlineTable = onlineTableService.getOne(new LambdaQueryWrapper().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); } + } + OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); + onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, datasource, null, dblink); - //这里要将 所有的附表名称 加到 apelet.common.generator.model.OnlFormHead.childName 后面 - String newSubTableName = String.join(",", newSubTableNames); - if (StringUtils.isNotBlank(onlFormHead.getChildName())) { - onlFormHead.setChildName(onlFormHead.getChildName() + "," + newSubTableName); - } else { - onlFormHead.setChildName(newSubTableName); - } - onlFormHeadService.updateById(onlFormHead); - changed = true; + if (subOnlineTable != null) { + backfillRelationId(widgetJson, subTableName, subOnlineTable.getTableId()); } } - return changed ? widgetJson : null; + + //这里要将 所有的附表名称 加到 apelet.common.generator.model.OnlFormHead.childName 后面 + String newSubTableName = String.join(",", newSubTableNames); + if (StringUtils.isNotBlank(onlFormHead.getChildName())) { + onlFormHead.setChildName(onlFormHead.getChildName() + "," + newSubTableName); + } else { + onlFormHead.setChildName(newSubTableName); + } + onlFormHeadService.updateById(onlFormHead); + return true; } @@ -1232,4 +1275,271 @@ public class OnlineFormController { } } + // ============ mobile 端子表同步相关方法 ============ + + /** + * 同步 mobile 端的子表(新增子表 + 已有子表的新增字段)。 + * 子表: mobile -> widgetList -> [widgetType=102] -> bindData -> tableName/tableId + * 字段: mobile -> widgetList -> childWidgetList -> childWidgetList -> bindData -> columnName + */ + private boolean syncMobileSubTables(JSONObject widgetJson, OnlFormHead onlFormHead, OnlineDatasource datasource) { + JSONObject mobile = widgetJson.getJSONObject("mobile"); + if (mobile == null) { + return false; + } + JSONArray mobileWidgetList = mobile.getJSONArray("widgetList"); + if (mobileWidgetList == null) { + return false; + } + boolean changed = false; + Set newSubTableNames = new LinkedHashSet<>(); + Set existingSubTableIds = new LinkedHashSet<>(); + for (int i = 0; i < mobileWidgetList.size(); i++) { + JSONObject widget = mobileWidgetList.getJSONObject(i); + if (widget.getInteger("widgetType") == null || widget.getInteger("widgetType") != 102) { + continue; + } + JSONObject bindData = widget.getJSONObject("bindData"); + if (bindData == null) { + continue; + } + String tableName = bindData.getString("tableName"); + String tableIdStr = bindData.getString("tableId"); + if (StrUtil.isNotEmpty(tableName) && StrUtil.isEmpty(tableIdStr)) { + newSubTableNames.add(tableName); + } else if (StrUtil.isNotEmpty(tableIdStr)) { + existingSubTableIds.add(Long.valueOf(tableIdStr)); + } + } + // 处理新子表: 创建后回写 tableId + masterColumnName + if (!newSubTableNames.isEmpty()) { + changed |= createNewSubTables(widgetJson, onlFormHead, datasource, newSubTableNames); + backfillMobileSubTableId(widgetJson, newSubTableNames); + } + // 处理已有子表的新增字段 + if (!existingSubTableIds.isEmpty()) { + changed |= syncMobileSubTableNewFields(widgetJson, datasource, existingSubTableIds); + } + return changed; + } + + /** + * 回写 mobile 端子表的 tableId 和 masterColumnName。 + * 路径: mobile -> widgetList -> [widgetType=102] -> bindData + */ + private void backfillMobileSubTableId(JSONObject widgetJson, Set newSubTableNames) { + JSONObject mobile = widgetJson.getJSONObject("mobile"); + if (mobile == null) { + return; + } + JSONArray mobileWidgetList = mobile.getJSONArray("widgetList"); + if (mobileWidgetList == null) { + return; + } + for (int i = 0; i < mobileWidgetList.size(); i++) { + JSONObject widget = mobileWidgetList.getJSONObject(i); + if (widget.getInteger("widgetType") == null || widget.getInteger("widgetType") != 102) { + continue; + } + JSONObject bindData = widget.getJSONObject("bindData"); + if (bindData == null) { + continue; + } + String tableName = bindData.getString("tableName"); + if (StrUtil.isEmpty(tableName) || !newSubTableNames.contains(tableName)) { + continue; + } + // createNewSubTables 已通过 backfillRelationId 回写了 tableId,这里只需补 masterColumnName + if (!bindData.containsKey("tableId")) { + OnlineTable subTable = onlineTableService.getOne(new LambdaQueryWrapper().eq(OnlineTable::getTableName, tableName.toLowerCase())); + if (subTable != null) { + bindData.put("tableId", subTable.getTableId()); + } + } + bindData.put("masterColumnName", "id"); + } + } + + /** + * 同步 mobile 端已有子表的新增字段。 + * 字段路径: mobile -> widgetList -> [widgetType=102] -> childWidgetList -> childWidgetList -> bindData -> columnName + */ + private boolean syncMobileSubTableNewFields(JSONObject widgetJson, OnlineDatasource datasource, Set existingSubTableIds) { + // 查询已有子表信息 + List subTables = onlineTableService.listByIds(existingSubTableIds); + if (CollUtil.isEmpty(subTables)) { + return false; + } + Map tableIdToName = subTables.stream().collect(Collectors.toMap(OnlineTable::getTableId, OnlineTable::getTableName)); + List tableNames = subTables.stream().map(OnlineTable::getTableName).collect(Collectors.toList()); + List subHeads = onlFormHeadService.list(new LambdaQueryWrapper().in(OnlFormHead::getTableName, tableNames)); + Map tableNameToHead = subHeads.stream().collect(Collectors.toMap(OnlFormHead::getTableName, Function.identity())); + OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); + + // 遍历 mobile 端已有子表 widget, 搜集新增字段 + JSONObject mobile = widgetJson.getJSONObject("mobile"); + JSONArray mobileWidgetList = mobile.getJSONArray("widgetList"); + boolean changed = false; + for (int i = 0; i < mobileWidgetList.size(); i++) { + JSONObject widget = mobileWidgetList.getJSONObject(i); + if (widget.getInteger("widgetType") == null || widget.getInteger("widgetType") != 102) { + continue; + } + JSONObject bindData = widget.getJSONObject("bindData"); + if (bindData == null) { + continue; + } + String tableIdStr = bindData.getString("tableId"); + if (StrUtil.isEmpty(tableIdStr)) { + continue; + } + Long tableId = Long.valueOf(tableIdStr); + String tableName = tableIdToName.get(tableId); + if (tableName == null) { + continue; + } + OnlFormHead subHead = tableNameToHead.get(tableName); + if (subHead == null) { + continue; + } + // 搜集该子表下无 columnId 的字段 + List newFieldWidgets = new ArrayList<>(); + JSONArray cardList = widget.getJSONArray("childWidgetList"); + if (cardList != null) { + for (int j = 0; j < cardList.size(); j++) { + JSONObject card = cardList.getJSONObject(j); + JSONArray fieldList = card.getJSONArray("childWidgetList"); + if (fieldList == null) { + continue; + } + for (int k = 0; k < fieldList.size(); k++) { + JSONObject field = fieldList.getJSONObject(k); + JSONObject fieldBd = field.getJSONObject("bindData"); + if (fieldBd != null && fieldBd.containsKey("columnName") && !fieldBd.containsKey("columnId")) { + newFieldWidgets.add(field); + } + } + } + } + if (newFieldWidgets.isEmpty()) { + continue; + } + // 创建字段记录 + for (JSONObject fieldWidget : newFieldWidgets) { + JSONObject fieldBd = fieldWidget.getJSONObject("bindData"); + String columnName = fieldBd.getString("columnName"); + String showName = fieldWidget.getString("showName"); + this.saveField(fieldWidget.getInteger("widgetType"), fieldBd.getString("columnComment"), showName, subHead, columnName, null); + } + // ALTER TABLE ADD COLUMN + onlFormHeadService.syncDB(subHead, 0L); + // 批量获取列信息 + List allColumns = onlineDblinkService.getDblinkTableColumnList(dblink, tableName); + Map columnMap = new HashMap<>(); + if (CollUtil.isNotEmpty(allColumns)) { + for (SqlTableColumn col : allColumns) { + if (col.getColumnName() != null) { + columnMap.put(col.getColumnName().toLowerCase(), col); + } + } + } + // 创建 OnlineColumn + 回写 + for (JSONObject fieldWidget : newFieldWidgets) { + JSONObject fieldBd = fieldWidget.getJSONObject("bindData"); + String columnName = fieldBd.getString("columnName"); + SqlTableColumn sqlCol = columnMap.get(columnName.toLowerCase()); + if (sqlCol != null) { + String showName = fieldWidget.getString("showName"); + long columnId = onlineColumnService.saveBySqlTable(sqlCol, tableId, showName); + fieldBd.put("columnId", columnId); + fieldBd.put("tableId", tableId); + fieldBd.put("columnFieldName", columnName); + } + } + // 删除缓存 + redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineTableKey(tableId)).delete(); + redissonClient.getBucket(CacheKey.makeTableKey(tableName)).delete(); + redissonClient.getBucket(CacheKey.makeChildFieldListKey(subHead.getId())).delete(); + metaCacheService.removeData(tableName); + changed = true; + } + return changed; + } + + /** + * 遍历 mobile 端 widget 树,收集所有 bindData.columnName 值。 + * 路径: mobile -> tableWidget -> childWidgetList -> childWidgetList -> bindData -> columnName + */ + private Set collectMobileBindColumnNames(JSONObject widgetJson) { + Set 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 mapMobileColumnNameToWidget(JSONObject widgetJson) { + Map 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; + } + }