diff --git a/application-tenant/tenant-admin/src/main/resources/application-config.yml b/application-tenant/tenant-admin/src/main/resources/application-config.yml new file mode 100644 index 0000000..158d786 --- /dev/null +++ b/application-tenant/tenant-admin/src/main/resources/application-config.yml @@ -0,0 +1,161 @@ +server: + tomcat: + uri-encoding: UTF-8 + threads: + max: 100 + min-spare: 10 + servlet: + encoding: + force: true + charset: UTF-8 + enabled: true + +logging: + config: classpath:logback-spring.xml + +spring: + servlet: + multipart: + max-file-size: 50MB + max-request-size: 50MB + mvc: + converters: + preferred-json-mapper: fastjson + freemarker: + template-loader-path: classpath:/template/ + cache: false + charset: UTF-8 + check-template-location: true + content-type: text/html + expose-request-attributes: false + expose-session-attributes: false + request-context-attribute: request + suffix: .ftl + cloud: + sentinel: + transport: + # sentinel控制台地址。 + # 由于8080端口容易与其他服务端口冲突,可以在启动sentinel控制台时动态修改,如: + # java -Dserver.port=8858 -jar sentinel-dashboard-$VERSION.jar + # 改为8858后,下面的配置端口也需要改为:localhost:8858。 + dashboard: localhost:8858 + +# feign 配置 +feign: + sentinel: + enabled: false + httpclient: + enabled: true + max-connections: 200 + max-connections-per-route: 50 + client: + config: + default: + # 输出feignclient的日志,缺省值为none,可选值为none/basic/headers/full + # 注意:需要把feignclient类所在包的日志级别设置为debug时才生效。如: + # logging: + # level: + # com.demo.multi: info + # com.demo.multi.coursepaperinterface.client: debug + loggerLevel: full + connectTimeout: 5000 + readTimeout: 10000 + compression: + request: + enabled: true + response: + enabled: true + +common-core: + # 可选值为 mysql / postgresql / oracle / dm8 / kingbase + databaseType: mysql + +swagger: + # 当enabled为false的时候,则可禁用swagger。 + enabled: true + # 工程的基础包名。 + basePackage: apelet + # 工程服务的基础包名。 + serviceBasePackage: + title: 小猿微服务工程 + description: 小猿微服务工程详情 + version: 1.0 +knife4j: + # 打开knife4j增强模式。 + enable: true + +aliyun: + oss: + enabled: false + expireSeconds: 1000 + # 下面几项均需在申请阿里云OSS后,根据自己的实际情况进行配置。 + endpoint: + accessKey: + secretKey: + bucketName: + +minio: + enabled: false + endpoint: + accessKey: + secretKey: + bucketName: + +# 暴露监控端点 +management: + metrics: + tags: + application: ${spring.application.name} + endpoints: + web: + exposure: + include: '*' + jmx: + exposure: + include: '*' + endpoint: + # 与中间件相关的健康详情也会被展示 + health: + show-details: always + configprops: + # 在/actuator/configprops中,所有包含password的配置,将用 * 隐藏。 + # 如果不想隐藏任何配置项的值,可以直接使用如下被注释的空值。 + # keys-to-sanitize: + keys-to-sanitize: password + server: + base-path: "/" + +# 存储session数据的Redis,所有服务均需要,因此放到公共配置中。 +# 根据实际情况,该Redis也可以用于存储其他数据。 +common-redis: + # redisson的配置。每个服务可以自己的配置文件中覆盖此选项。 + redisson: + # 如果该值为false,系统将不会创建RedissionClient的bean。 + enabled: true + # mode的可用值为,single/cluster/sentinel/master-slave + mode: single + # single: 单机模式 + # address: redis://localhost:6379 + # cluster: 集群模式 + # 每个节点逗号分隔,同时每个节点前必须以redis://开头。 + # address: redis://localhost:6379,redis://localhost:6378,... + # sentinel: + # 每个节点逗号分隔,同时每个节点前必须以redis://开头。 + # address: redis://localhost:6379,redis://localhost:6378,... + # master-slave: + # 每个节点逗号分隔,第一个为主节点,其余为从节点。同时每个节点前必须以redis://开头。 + # address: redis://localhost:6379,redis://localhost:6378,... + address: redis://192.168.100.25:6379 + # 链接超时,单位毫秒。 + timeout: 6000 + # 单位毫秒。分布式锁的超时检测时长。 + # 如果一次锁内操作超该毫秒数,或在释放锁之前异常退出,Redis会在该时长之后主动删除该锁使用的key。 + lockWatchdogTimeout: 60000 + # redis 密码,空可以不填。 + password: + database: 0 + pool: + # 连接池数量。 + poolSize: 20 + # 连接池中最小空闲数量。 + minIdle: 5 diff --git a/application-tenant/tenant-admin/src/main/resources/bootstrap.yml b/application-tenant/tenant-admin/src/main/resources/bootstrap.yml index e6112a8..f1901e3 100644 --- a/application-tenant/tenant-admin/src/main/resources/bootstrap.yml +++ b/application-tenant/tenant-admin/src/main/resources/bootstrap.yml @@ -3,20 +3,14 @@ spring: name: tenant-admin profiles: active: @profile.name@ - cloud: - nacos: - discovery: - server-addr: @discovery.server-addr@ - config: - server-addr: @config.server-addr@ - group: @config.group@ - namespace: @config.namespace@ - file-extension: yaml - # 共享配置文件,排序越高后,优先级越高。 - shared-configs: - - data-id: application-${spring.profiles.active}.yaml - group: @config.group@ - refresh: true main: allow-bean-definition-overriding: true allow-circular-references: true + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration + - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure + config: + import: + - classpath:tenant-admin-dev.yml + - classpath:application-config.yml # 引入共享配置 diff --git a/application-tenant/tenant-admin/src/main/resources/logback-spring.xml b/application-tenant/tenant-admin/src/main/resources/logback-spring.xml index 759d941..68533c6 100644 --- a/application-tenant/tenant-admin/src/main/resources/logback-spring.xml +++ b/application-tenant/tenant-admin/src/main/resources/logback-spring.xml @@ -16,6 +16,7 @@ debug:当此属性设置为true时,将打印出logback内部日志信息, + @@ -89,48 +91,48 @@ debug:当此属性设置为true时,将打印出logback内部日志信息, - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java b/common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java index 0782aa4..0172fe3 100644 --- a/common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java +++ b/common/common-generator/src/main/java/apelet/common/generator/service/IOnlFormHeadService.java @@ -119,4 +119,15 @@ public interface IOnlFormHeadService extends IService { * @return */ OnlFormHead getHeadTableNameCache(String tableName); + + /** + * 创建元数据(含默认字段)并同步数据库. 返回创建后的 OnlFormHead. + * + * @param tableName 表名 + * @param tableTxt 表描述 + * @param metaType 表类型: "0"=附表(分录), "1"=主表(业务单据), "2"=单表(基础资料) + * @param sortFiled 关联字段(附表时传主表表名) + * @return OnlFormHead + */ + OnlFormHead createMetaAndSync(String tableName, String tableTxt, String metaType, String sortFiled); } diff --git a/common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java b/common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java index 4f54f5a..bb63a20 100644 --- a/common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java +++ b/common/common-generator/src/main/java/apelet/common/generator/service/impl/OnlFormHeadServiceImpl.java @@ -1660,4 +1660,43 @@ public class OnlFormHeadServiceImpl extends ServiceImpl list) { onlFormHeadMapper.saveEasDict(list); } + + @Override + @Transactional(rollbackFor = Exception.class) + public OnlFormHead createMetaAndSync(String tableName, String tableTxt, String metaType, String sortFiled) { + OnlFormHead head = new OnlFormHead(); + head.setId(idGenerator.nextLongId()); + head.setTableName(tableName); + head.setTableTxt(tableTxt); + head.setTableType(metaType); + head.setStatus("0"); // 未同步状态 + head.setLine(CustomUtil.MetaType_Entry.equals(metaType) ? 1 : 0); + if (StringUtils.isNotEmpty(sortFiled)) { + head.setSort(sortFiled); + head.setSortFiled("parent_id"); + } + + // 初始化审计字段(createUserId/updateUserId/createTime/updateTime/deletedFlag) + addOrUpdateBaseData(head, head.getId()); + + // Build DTO + OnlFormHeadDto dto = new OnlFormHeadDto(); + BeanUtil.copyProperties(head, dto); + + // Generate default fields + List defaultFields = new ArrayList<>(); + defaultData(defaultFields, null, null, metaType); + dto.setOnlFormFieldList(defaultFields); + + // Save + this.addAll(dto); + + // Reload to get full info + OnlFormHead saved = this.getById(head.getId()); + + // Sync database (type=0: CREATE TABLE or ALTER TABLE ADD COLUMN) + this.syncDB(saved, 0L); + + return saved; + } } 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 1a1571c..3d98cbd 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 @@ -3,6 +3,7 @@ package apelet.common.online.controller; import apelet.common.core.annotation.MyRequestBody; import apelet.common.core.annotation.NoAuthInterface; import apelet.common.core.cache.CacheConfig; +import apelet.common.core.cache.CacheKey; import apelet.common.core.constant.AppDeviceType; import apelet.common.core.constant.ErrorCodeEnum; import apelet.common.core.object.*; @@ -10,8 +11,14 @@ import apelet.common.core.util.MyCommonUtil; import apelet.common.core.util.MyModelUtil; import apelet.common.core.util.MyPageUtil; import apelet.common.core.validator.UpdateGroup; +import apelet.common.dbutil.object.SqlTable; +import apelet.common.dbutil.object.SqlTableColumn; +import apelet.common.generator.model.OnlFormField; import apelet.common.generator.model.OnlFormHead; +import apelet.common.generator.service.IOnlFormFieldService; import apelet.common.generator.service.IOnlFormHeadService; +import apelet.common.generator.utils.CustomUtil; +import apelet.common.generator.utils.cache.MetaCacheService; import apelet.common.log.annotation.OperationLog; import apelet.common.log.model.constant.SysOperationLogType; import apelet.common.online.config.OnlineProperties; @@ -20,6 +27,7 @@ import apelet.common.online.dto.OnlineFormDto; import apelet.common.online.model.*; import apelet.common.online.service.*; import apelet.common.online.util.OnlineRedisKeyUtil; +import apelet.common.online.util.WidgetFieldTypeMapping; import apelet.common.online.vo.OnlineFormVo; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; @@ -47,6 +55,8 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.validation.groups.Default; import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; /** @@ -81,12 +91,17 @@ public class OnlineFormController { @Autowired IOnlFormHeadService onlFormHeadService; @Autowired + IOnlFormFieldService onlFormFieldService; + @Autowired OnlinePageService onlinePageService; @Autowired private OnlineProperties properties; @Resource(name = "caffeineCacheManager") private CacheManager cacheManager; + @Autowired + private MetaCacheService metaCacheService; + @Autowired(required = false) private FormButtonPermissionChecker buttonPermissionChecker; @@ -327,6 +342,13 @@ public class OnlineFormController { 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)) { redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineFormKey(onlineForm.getFormId())).delete(); return ResponseResult.error(ErrorCodeEnum.DATA_NOT_EXIST); @@ -591,6 +613,7 @@ public class OnlineFormController { List eventPluginExecuteVoList = onlineFormService.eventPluginExecute(onlineEventPluginExecuteDto); return ResponseResult.success(eventPluginExecuteVoList); +// return ResponseResult.success(new ArrayList<>()); } private Long findNotExistDictId(Set originalDictIdSet, Set dictIdSet) { @@ -713,4 +736,404 @@ public class OnlineFormController { } return ResponseResult.success(onlineFormService.queryListByFormType(Lists.newArrayList(formType))); } + + // ============ 字段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() + .eq(OnlFormHead::getTableName, onlineTable.getTableName())); + if (onlFormHead == null) { + return null; + } + + boolean changed = false; + + // === Diff 新增字段 === + Set widgetColumnNames = collectAllBindColumnNames(widgetJson); + List existingFields = onlFormFieldService.list( + new LambdaQueryWrapper().eq(OnlFormField::getHeadId, onlFormHead.getId())); + Set existingFieldNames = existingFields.stream() + .map(OnlFormField::getFieldName).collect(Collectors.toSet()); + Set newColumnNames = new HashSet<>(widgetColumnNames); + newColumnNames.removeAll(existingFieldNames); + + if (!newColumnNames.isEmpty()) { + Map nameToWidget = mapColumnNameToWidget(widgetJson); + OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); + + // 预校验:过滤掉 widgetType 不在映射中的控件,避免 getFieldTypeId 抛异常导致部分数据已入库 + Set 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); + // 从 widget 的 bindData.columnComment 设置字段备注 + JSONObject bindData = widget.getJSONObject("bindData"); + this.saveField(widget.getInteger("widgetType"), bindData.getString("columnComment"), widget.getString("showName"), onlFormHead, fieldName); + } + + // ALTER TABLE ADD COLUMN + onlFormHeadService.syncDB(onlFormHead, 0L); + + // 批量获取所有列信息,避免 N+1 查询 + List allColumns = onlineDblinkService.getDblinkTableColumnList( + dblink, onlineTable.getTableName()); + Map 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) { + // 回填 columnId 到 widgetJson + JSONObject widget = nameToWidget.get(fieldName); + String showName = widget == null ? null : widget.getString("showName"); + long columnId = onlineColumnService.saveBySqlTable(sqlCol, onlineTable.getTableId(), showName); + if (widget != null) { + JSONObject bindData2 = widget.getJSONObject("bindData"); + if (bindData2 != null) { + bindData2.put("columnId", columnId); + bindData2.put("tableId", onlineTable.getTableId()); + } + } + } + } + redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineTableKey(onlineTable.getTableId())).delete(); + redissonClient.getBucket(CacheKey.makeTableKey(onlineTable.getTableName())).delete(); + redissonClient.getBucket(CacheKey.makeChildFieldListKey(onlFormHead.getId())).delete(); + metaCacheService.removeData(onlineTable.getTableName()); + changed = true; + } + + // === Diff 新增子表 === + // 取 widgetList->bindData -> tableId 在去 zz_online_table 查询,这样就可以判断哪些是新增,哪些是 已有的表 + // 如果存在附表 + Map tableIdAndPropMap = collectSubTableAndPropMap(widgetJson); + + if (MapUtil.isNotEmpty(tableIdAndPropMap)) { + //搜集的所有tableId, 要么是 tableId(已存在),要么是 tableName(不存在) + Set allTableIdSet = tableIdAndPropMap.keySet(); + Set newSubTableNames = new HashSet<>(); + Set existingSubTableIdSet = new HashSet<>(); + for (String tableId : allTableIdSet) { + if (tableId.matches("\\d+")) { + existingSubTableIdSet.add(Long.parseLong(tableId)); + } else { + newSubTableNames.add(tableId); + } + } + //分两步, + //step1 : 看看存在的附表,是否存在不存在的字段 + if (!existingSubTableIdSet.isEmpty()) { + // 拿到tableId 下面的所有字段,先查询 zz_online_table ,在根据 tableName 查询 onl_form_head 表 + List tableList = onlineTableService.listByIds(existingSubTableIdSet); + Map tableIdAndNameMap = tableList.stream().collect(Collectors.toMap(OnlineTable::getTableId, OnlineTable::getTableName)); + List tableNames = tableList.stream().map(OnlineTable::getTableName).collect(Collectors.toList()); + List onlFormHeadList = onlFormHeadService.list(new LambdaQueryWrapper().in(OnlFormHead::getTableName, tableNames)); + Map tableNameAndHeadIdMap = onlFormHeadList.stream().collect(Collectors.toMap(OnlFormHead::getTableName, Function.identity())); + //因为这里是zz_online_column.column_id 的id和 列名的混合,我只要列名 + Map nameToWidget = mapColumnIdAndWidget(widgetJson); + Set newSubColumnNameSet = nameToWidget.keySet().stream().filter(s -> !s.matches("\\d+")).collect(Collectors.toSet()); + //这里要走 新增逻辑 + if (!newSubColumnNameSet.isEmpty()) { + // ALTER TABLE ADD COLUMN, 这里取第一个就行,因为绑定的是一个表 + JSONObject columnInfo = nameToWidget.values().iterator().next(); + Long tableId = columnInfo.getLong("tableId"); + String tableName = tableIdAndNameMap.get(tableId); + OnlFormHead entryHead = tableNameAndHeadIdMap.get(tableName); + for (String newSubColumnName : newSubColumnNameSet) { + //这里取出来的结构是 widgetList -》 props -》 tableColumnList + JSONObject columnInfoJson = nameToWidget.get(newSubColumnName); + //step 1 : 先保存 field 表 + String showName = columnInfoJson.getString("showName"); + // 默认是字符串类型 + this.saveField(750, showName, null, entryHead, newSubColumnName); + } + onlFormHeadService.syncDB(entryHead, 0L); + //step 2 :在保存 OnlineColumn 表 + OnlineDblink dblink = onlineDblinkService.getById(datasource.getDblinkId()); + List allColumns = onlineDblinkService.getDblinkTableColumnList(dblink, tableName); + Map columnMap = new HashMap<>(); + if (CollUtil.isNotEmpty(allColumns)) { + columnMap = allColumns.stream().collect(Collectors.toMap(SqlTableColumn::getColumnName, Function.identity())); + } + for (String fieldName : newSubColumnNameSet) { + SqlTableColumn sqlCol = columnMap.get(fieldName.toLowerCase()); + if (sqlCol != null) { + // 回填 columnId 到 widgetJson + JSONObject widget = nameToWidget.get(fieldName); + String showName = widget == null ? null : widget.getString("showName"); + long columnId = onlineColumnService.saveBySqlTable(sqlCol, tableId, showName); + if (widget != null) { + //step 3 :回填 columnId + showFieldName 到 json里面去 + widget.put("columnId", columnId); + widget.put("showFieldName", fieldName); + } + } + } + //step 4 :删除缓存 + redissonClient.getBucket(OnlineRedisKeyUtil.makeOnlineTableKey(tableId)).delete(); + redissonClient.getBucket(CacheKey.makeTableKey(tableName)).delete(); + redissonClient.getBucket(CacheKey.makeChildFieldListKey(entryHead.getId())).delete(); + metaCacheService.removeData(tableName); + changed = true; + } + } + //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); + + if (subOnlineTable != null) { + backfillRelationId(widgetJson, subTableName, subOnlineTable.getTableId()); + } + } + changed = true; + } + } + return changed ? widgetJson : null; + } + + + private void saveField(Integer widgetType, String columnComment, String showName, OnlFormHead onlFormHead, String fieldName) { + 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 设置字段备注 + field.setFieldRemark(columnComment); + if (StringUtils.isNotBlank(showName) && (StringUtils.isBlank(field.getFieldRemark()) || StringUtils.equals("null", field.getFieldRemark()))) { + field.setFieldRemark(showName); + } + onlFormFieldService.save(field); + } + + /** + * 遍历 pc/mobile 的 widget 树,收集所有 bindData.columnName 值。 + */ + private Set collectAllBindColumnNames(JSONObject widgetJson) { + Set 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 collectSubTableAndPropMap(JSONObject widgetJson) { + Map 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 collectSubTableName(JSONObject widgetJson) { + Set 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 mapColumnNameToWidget(JSONObject widgetJson) { + Map 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; + } + + private Map mapColumnIdAndWidget(JSONObject widgetJson) { + Map 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); + 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 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 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); + } + } + } } diff --git a/common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java b/common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java index 666ea6d..8d137a7 100644 --- a/common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java +++ b/common/common-online/src/main/java/apelet/common/online/controller/OnlinePageController.java @@ -16,7 +16,10 @@ import apelet.common.generator.service.IOnlFormHeadService; import apelet.common.generator.utils.CustomUtil; import apelet.common.log.annotation.OperationLog; import apelet.common.log.model.constant.SysOperationLogType; -import apelet.common.online.dto.*; +import apelet.common.online.dto.OnlineDatasourceDto; +import apelet.common.online.dto.OnlineDatasourceRelationDto; +import apelet.common.online.dto.OnlinePageDatasourceDto; +import apelet.common.online.dto.OnlinePageDto; import apelet.common.online.model.*; import apelet.common.online.model.constant.PageStatus; import apelet.common.online.service.*; @@ -36,6 +39,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.dao.DuplicateKeyException; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import javax.validation.groups.Default; @@ -74,6 +78,10 @@ public class OnlinePageController { @Autowired private IOnlFormFieldService onlFormFieldService; + //new=新建元数据,exist=现有元数据 + private static final String META_SOURCE_NEW = "new"; + private static final String META_SOURCE_EXIST = "exist"; + /** * 新增在线表单页面数据。 * @@ -82,41 +90,82 @@ public class OnlinePageController { */ @ApiOperationSupport(ignoreParameters = {"onlinePageDto.pageId"}) @OperationLog(type = SysOperationLogType.ADD) + @Transactional(rollbackFor = Exception.class) @PostMapping("/add") - public ResponseResult add(@MyRequestBody OnlinePageDto onlinePageDto) { + public ResponseResult add(@MyRequestBody OnlinePageDto onlinePageDto) { String errorMessage = MyCommonUtil.getModelValidationError(onlinePageDto); if (errorMessage != null) { return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); } + // Validate based on metaSource + String metaSource = onlinePageDto.getMetaSource(); + if (META_SOURCE_NEW.equals(metaSource)) { + // New metadata flow: metaType and pageCode are required + if (StrUtil.isEmpty(onlinePageDto.getMetaType())) { + errorMessage = "数据验证失败,新建元数据时表类型(metaType)不能为空!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + if (StrUtil.isEmpty(onlinePageDto.getPageCode())) { + errorMessage = "数据验证失败,新建元数据时页面编码不能为空!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + } else { + // Existing metadata flow: masterTableId is required + if (StrUtil.isEmpty(onlinePageDto.getMasterTableId())) { + errorMessage = "数据验证失败,表单元数据不能为空!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + } OnlinePage onlinePage = MyModelUtil.copyTo(onlinePageDto, OnlinePage.class); if (onlinePageService.existByPageCode(onlinePage.getPageCode())) { errorMessage = "数据验证失败,页面编码已经存在!"; return ResponseResult.error(ErrorCodeEnum.DUPLICATED_UNIQUE_KEY, errorMessage); } + + OnlFormHead onlFormHead; + + if (META_SOURCE_NEW.equals(metaSource)) { + // === New branch: create fresh metadata === + String metaType = onlinePageDto.getMetaType(); + String tableName = "t_" + onlinePageDto.getPageCode().toLowerCase(); + String tableTxt = onlinePageDto.getPageName(); + + onlFormHead = iOnlFormHeadService.createMetaAndSync(tableName, tableTxt, metaType, null); + Long masterTableId = onlFormHead.getId(); + onlinePage.setMasterTableId(String.valueOf(masterTableId)); + onlinePage.setMasterTableName(onlFormHead.getTableName()); + + // For business documents (metaType="1"): auto-create sub-table + if (CustomUtil.MetaType_Main.equals(metaType)) { + String entryTableName = tableName + "_entry"; + iOnlFormHeadService.createMetaAndSync(entryTableName, tableTxt + "附表", CustomUtil.MetaType_Entry, tableName); + } + } else { + // === Existing branch: use existing metadata === + String formMetadata = onlinePageDto.getMasterTableId(); + onlFormHead = iOnlFormHeadService.getById(formMetadata); + } + try { onlinePage = onlinePageService.saveNew(onlinePage); } catch (DuplicateKeyException e) { errorMessage = "数据验证失败,当前应用的页面编码已经存在!"; return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); } - //根据所传表单元数据id查出对应表单信息 - String formMetadata = onlinePageDto.getMasterTableId(); - //在线表单主表对象 - OnlFormHead onlFormHead = iOnlFormHeadService.getById(formMetadata); - //准备创建数据模型所需的数据 + + // 准备创建数据模型所需的数据 OnlineDatasourceDto onlineDatasourceDto = new OnlineDatasourceDto(); - //数据源标识-取表名驼峰 + // 数据源标识-取表名驼峰 onlineDatasourceDto.setVariableName(CustomUtil.UnderlineToCamel(onlFormHead.getTableName())); - //数据主表标识 + // 数据主表标识 onlineDatasourceDto.setMasterTableName(onlFormHead.getTableName()); - //数据源名称-取表名 + // 数据源名称-取表名 onlineDatasourceDto.setDatasourceName((onlFormHead.getTableName())); - - //目前系统只有一个数据源,所以默认1740180645717610505 + // 目前系统只有一个数据源,所以默认1740180645717610505 OnlineDblink onlineDblink = onlineDblinkService.getById(1740180645717610505L); onlineDatasourceDto.setDblinkId(1740180645717610505L); - //创建数据模型 + // 创建数据模型 SqlTable sqlTable = onlineDblinkService.getDblinkTable(onlineDblink, onlineDatasourceDto.getMasterTableName()); if (sqlTable == null) { errorMessage = "数据验证失败,指定的数据表名不存在!"; @@ -130,22 +179,20 @@ public class OnlinePageController { return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); } /** - *创建数据表关联关系 先查出改元数据表的所有附表关系 + * 创建数据表关联关系 先查出改元数据表的所有附表关系 * 一对多 - * */ - - addOnlineDatasourceRelation(onlFormHead, onlineDatasource, errorMessage, onlineDblink); - - - return ResponseResult.success(onlinePage.getPageId()); + onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHead, onlineDatasource, errorMessage, onlineDblink); + + return ResponseResult.success(onlinePage); } /** *创建数据表关联关系 先查出改元数据表的所有附表关系 - * + * 方法迁移☞ onlineDatasourceRelationService.addOnlineDatasourceRelation */ + @Deprecated public ResponseResult addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink) { //历史数据 --做比较新数据写入,已存在的不用管,不存在的删除 OnlineDatasourceRelation onlineDatasourceRelations = new OnlineDatasourceRelation(); @@ -399,7 +446,7 @@ public class OnlinePageController { onlineColumnService.saveNewList(sqlTableColumnList, onlineDatasourceList.get(0).getMasterTableId()); //写表关联关系 --一对多,一对一 onlineDatasourceService.getOnlineDatasourceListByPageId(onlinePageDto.getPageId(), null, null); - addOnlineDatasourceRelation(onlFormHeadList.get(0), onlineDatasourceList.get(0), errorMessage, onlineDblink); + onlineDatasourceRelationService.addOnlineDatasourceRelation(onlFormHeadList.get(0), onlineDatasourceList.get(0), errorMessage, onlineDblink); ArrayList onlineColumns = new ArrayList<>(); List filedByHeadIdCache = onlFormFieldService.getFiledByHeadIdCache(onlFormHeadList.get(0).getId()); diff --git a/common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java b/common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java index 59c2529..afa6f86 100644 --- a/common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java +++ b/common/common-online/src/main/java/apelet/common/online/dto/OnlinePageDto.java @@ -60,13 +60,23 @@ public class OnlinePageDto { * 表单元数据。 */ @Schema(description = "表单元数据") - @NotNull(message = "数据验证失败,表单元数据不能为空!") private String masterTableId; /** * 表单元数据。 */ @Schema(description = "表单元数据表名") -// @NotNull(message = "数据验证失败,表单元数据不能为空!") private String masterTableName; + + /** + * 数据来源: "new"=新建元数据, "existing"=使用已有元数据。 + */ + @Schema(description = "数据来源: new=新建元数据, existing=使用已有元数据") + private String metaSource; + + /** + * 表类型: "2"=基础资料(单表), "1"=业务单据(主表+附表)。仅在metaSource="new"时有效。 + */ + @Schema(description = "表类型: 2=基础资料(单表), 1=业务单据(主表+附表)。仅在metaSource=new时有效") + private String metaType; } diff --git a/common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java b/common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java index 983a086..4832343 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java +++ b/common/common-online/src/main/java/apelet/common/online/service/OnlineColumnService.java @@ -27,6 +27,16 @@ public interface OnlineColumnService extends IBaseService { List saveNewList(List columnList, Long onlineTableId); /** + * 保存新增数据表字段。 + * + * @param sqlTableColumn 新增数据表字段对象。 + * @param tableId 在线表对象的主键Id。 + * @param columnComment 字段名。 + * @return 插入的在线表字段数据。 + */ + long saveBySqlTable(SqlTableColumn sqlTableColumn, Long tableId, String columnComment); + + /** * 更新数据对象。 * * @param onlineColumn 更新的对象。 diff --git a/common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java b/common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java index afd9931..8a5afe9 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java +++ b/common/common-online/src/main/java/apelet/common/online/service/OnlineDatasourceRelationService.java @@ -1,9 +1,13 @@ package apelet.common.online.service; import apelet.common.core.base.service.IBaseService; +import apelet.common.core.object.ResponseResult; import apelet.common.dbutil.object.SqlTable; import apelet.common.dbutil.object.SqlTableColumn; +import apelet.common.generator.model.OnlFormHead; +import apelet.common.online.model.OnlineDatasource; import apelet.common.online.model.OnlineDatasourceRelation; +import apelet.common.online.model.OnlineDblink; import java.util.List; import java.util.Set; @@ -82,4 +86,17 @@ public interface OnlineDatasourceRelationService extends IBaseService getOnlineDatasourceRelationListWithRelation( OnlineDatasourceRelation filter, String orderBy); + + /*** + * 功能: 迁移 OnlinePageController#addOnlineDatasourceRelation(OnlFormHead, OnlineDatasource, String, OnlineDblink) + * 方法,创建数据表关联关系 先查出改元数据表的所有附表关系 + * + * @author ${chenchuchuan} + * @param onlFormHead 主表信息 + * @param onlineDatasource 数据库链接配置 + * @param errorMessage 错误描述 + * @param onlineDblink 数据库实际链接 + * @return ResponseResult + */ + ResponseResult addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink); } diff --git a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java index c66e247..2756598 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java +++ b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineColumnServiceImpl.java @@ -31,13 +31,17 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.github.pagehelper.Page; import com.google.common.base.CaseFormat; +import jodd.util.StringUtil; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.*; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; /** * 字段数据数据操作服务类。 @@ -106,6 +110,29 @@ public class OnlineColumnServiceImpl extends BaseService imp return onlineColumnList; } + + @Override + public long saveBySqlTable(SqlTableColumn sqlTableColumn, Long tableId, String columnComment) { + OnlineColumn onlineColumn = new OnlineColumn(); + BeanUtil.copyProperties(sqlTableColumn, onlineColumn, false); + onlineColumn.setColumnId(idGenerator.nextLongId()); + onlineColumn.setTableId(tableId); + // 传了描述,并且不等于原先的描述,并且原先的为空或者为"null",就赋值 + if (StringUtil.isNotBlank(columnComment) && !StringUtil.equals(columnComment, onlineColumn.getColumnComment()) + && (StringUtil.isBlank(onlineColumn.getColumnComment()) || StringUtil.equals("null", onlineColumn.getColumnComment()))) { + onlineColumn.setColumnComment(columnComment); + } + if (StringUtil.isBlank(onlineColumn.getColumnComment())) { + onlineColumn.setColumnComment(onlineColumn.getColumnName()); + } + this.setDefault(sqlTableColumn, onlineColumn); + if (super.save(onlineColumn)) { + sqlTableColumn.setColumnName(onlineColumn.getColumnName().toLowerCase()); + return onlineColumn.getColumnId(); + } + return 0; + } + /** * 更新数据对象。 * diff --git a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java index 7dd4f38..799d821 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java +++ b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceRelationServiceImpl.java @@ -4,21 +4,31 @@ import apelet.common.core.annotation.MyDataSourceResolver; import apelet.common.core.base.dao.BaseDaoMapper; import apelet.common.core.base.service.BaseService; import apelet.common.core.constant.ApplicationConstant; +import apelet.common.core.constant.ErrorCodeEnum; import apelet.common.core.object.CallResult; import apelet.common.core.object.MyRelationParam; +import apelet.common.core.object.ResponseResult; import apelet.common.core.object.TokenData; import apelet.common.core.util.DefaultDataSourceResolver; +import apelet.common.core.util.MyModelUtil; import apelet.common.dbutil.object.SqlTable; import apelet.common.dbutil.object.SqlTableColumn; +import apelet.common.generator.model.OnlFormField; +import apelet.common.generator.model.OnlFormHead; +import apelet.common.generator.service.IOnlFormFieldService; +import apelet.common.generator.service.IOnlFormHeadService; import apelet.common.online.dao.OnlineDatasourceRelationMapper; import apelet.common.online.dao.OnlineDatasourceTableMapper; +import apelet.common.online.dto.OnlineDatasourceRelationDto; import apelet.common.online.model.*; import apelet.common.online.service.*; import apelet.common.online.util.OnlineRedisKeyUtil; import apelet.common.redis.util.CommonRedisUtil; import apelet.common.sequence.wrapper.IdGeneratorWrapper; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONArray; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.github.pagehelper.Page; @@ -30,6 +40,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; +import java.util.stream.Collectors; /** * 数据源关联数据操作服务类。 @@ -61,6 +72,14 @@ public class OnlineDatasourceRelationServiceImpl private RedissonClient redissonClient; @Autowired private CommonRedisUtil commonRedisUtil; + @Autowired + private OnlineDblinkService onlineDblinkService; + @Autowired + private IOnlFormHeadService iOnlFormHeadService; + @Autowired + private OnlineDatasourceRelationService onlineDatasourceRelationService; + @Autowired + private IOnlFormFieldService onlFormFieldService; /** * 返回当前Service的主表Mapper对象。 @@ -289,4 +308,203 @@ public class OnlineDatasourceRelationServiceImpl } return CallResult.ok(); } + + @Override + public ResponseResult addOnlineDatasourceRelation(OnlFormHead onlFormHead, OnlineDatasource onlineDatasource, String errorMessage, OnlineDblink onlineDblink) { + //历史数据 --做比较新数据写入,已存在的不用管,不存在的删除 + OnlineDatasourceRelation onlineDatasourceRelations = new OnlineDatasourceRelation(); + onlineDatasourceRelations.setDatasourceId(onlineDatasource.getDatasourceId()); + List onlineDatasourceRelationList = + onlineDatasourceRelationService.getOnlineDatasourceRelationListWithRelation(onlineDatasourceRelations, null); + Map oldmap01 = + onlineDatasourceRelationList.stream().collect(Collectors.toMap(obj -> obj.getMasterColumnId() + "_" + obj.getRelationType() + "_" + obj.getSlaveTableId(), obj -> obj, + (key1, key2) -> key1 + )); + //查出表单元数据的所有附表关系 + //主表id + List onlineTableList = onlineTableService.list(new LambdaQueryWrapper().eq(OnlineTable::getTableName, onlFormHead.getTableName().toLowerCase())); + //创建一对多关系:1 + List oneToMoreObjsList = iOnlFormHeadService.list(new LambdaQueryWrapper().eq(OnlFormHead::getLine, 1).eq(OnlFormHead::getSort, onlFormHead.getTableName())); + if (!oneToMoreObjsList.isEmpty()) { + List tableIdList = onlineTableList.stream().map(OnlineTable::getTableId).collect(Collectors.toList()); +// //主表列id对象 + OnlineColumn onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper() + .eq(OnlineColumn::getColumnName, "id") + .in(OnlineColumn::getTableId, tableIdList)); + for (OnlFormHead formHead : oneToMoreObjsList) { + List onlineTableListfu = onlineTableService.list(new LambdaQueryWrapper().eq(OnlineTable::getTableName, formHead.getTableName().toLowerCase())); + //附表关联的主表字段+附表id + if ((onlineTableListfu == null || onlineTableListfu.size() < 1) || !oldmap01.containsKey(onlineColumn.getColumnId() + "_1_" + onlineTableListfu.get(0).getTableId())) { + OnlineDatasourceRelationDto onlineDatasourceRelationDto = new OnlineDatasourceRelationDto(); + onlineDatasourceRelationDto.setDatasourceId(onlineDatasource.getDatasourceId()); + //关联名称 + onlineDatasourceRelationDto.setRelationName(onlFormHead.getTableName() + "_" + formHead.getTableName()); + //关联标识 + onlineDatasourceRelationDto.setVariableName(onlFormHead.getTableName() + "_" + formHead.getTableName()); + //关联类型 + onlineDatasourceRelationDto.setRelationType(1); + //主键字段id + onlineDatasourceRelationDto.setMasterColumnId(onlineColumn.getColumnId()); + //从表名称 + onlineDatasourceRelationDto.setSlaveTableName(formHead.getTableName()); + //从表字段名称 + onlineDatasourceRelationDto.setSlaveColumnName(formHead.getSortFiled()); + //是否级联删除 + onlineDatasourceRelationDto.setCascadeDelete(true); + //是否左连接查询 + onlineDatasourceRelationDto.setLeftJoin(false); + OnlineDatasourceRelation onlineDatasourceRelation = + MyModelUtil.copyTo(onlineDatasourceRelationDto, OnlineDatasourceRelation.class); + String appCode = TokenData.takeFromRequest().getAppCode(); + if (!StrUtil.equals(onlineDatasource.getAppCode(), appCode)) { + errorMessage = "数据验证失败,当前应用并不包含该数据源Id!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } +// OnlineDblink onlineDblink = onlineDblinkService.getById(onlineDatasource.getDblinkId()); + SqlTable slaveTable = onlineDblinkService.getDblinkTable( + onlineDblink, onlineDatasourceRelationDto.getSlaveTableName()); + if (slaveTable == null) { + errorMessage = "数据验证失败,指定的数据表不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SqlTableColumn slaveColumn = null; + for (SqlTableColumn column : slaveTable.getColumnList()) { + if (column.getColumnName().equals(onlineDatasourceRelationDto.getSlaveColumnName())) { + slaveColumn = column; + break; + } + } + if (slaveColumn == null) { + errorMessage = "数据验证失败,指定的数据表字段 [" + onlineDatasourceRelationDto.getSlaveColumnName() + "] 不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + // 验证关联Id的数据合法性 + CallResult callResult = + onlineDatasourceRelationService.verifyRelatedData(onlineDatasourceRelation, null); + if (!callResult.isSuccess()) { + errorMessage = callResult.getErrorMessage(); + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + onlineDatasourceRelation = onlineDatasourceRelationService.saveNew(onlineDatasourceRelation, slaveTable, slaveColumn); + } else { + //更新表字段 + SqlTable slaveTable = onlineDblinkService.getDblinkTable( + onlineDblink, formHead.getTableName()); + if (slaveTable == null) { + errorMessage = "数据验证失败,指定的数据表不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + List sqlTableColumnList = slaveTable.getColumnList(); + Set tableids = new HashSet<>(); + tableids.add(onlineTableListfu.get(0).getTableId()); + List listColumn = onlineColumnService.getOnlineColumnListByTableIds(tableids); + List columnNames = listColumn.stream().map(OnlineColumn::getColumnName).collect(Collectors.toList()); + sqlTableColumnList = sqlTableColumnList.stream().filter(x -> !columnNames.contains(x.getColumnName().toLowerCase())).collect(Collectors.toList()); + onlineColumnService.saveNewList(sqlTableColumnList, onlineTableListfu.get(0).getTableId()); + } + } + } + //创建一对一关系:0 + List onlFormFieldList = onlFormFieldService.list(new LambdaQueryWrapper() + .eq(OnlFormField::getHeadId, onlFormHead.getId()).isNotNull(OnlFormField::getRefPropert)); + //List oneToMoreObjsList = iOnlFormHeadService.list(new LambdaQueryWrapper().eq(OnlFormHead::getLine, 1).eq(OnlFormHead::getSort, onlFormHead.getTableName())); + if (!onlFormFieldList.isEmpty()) { + Set updatedTable = new HashSet<>(); + //附表 + for (OnlFormField onlFormField : onlFormFieldList) { + //附表 + List onlFormHeadList = iOnlFormHeadService.list(new LambdaQueryWrapper().eq(OnlFormHead::getId, onlFormField.getRefPropert())); + if (onlFormHeadList.isEmpty()) { + continue; + } + OnlFormHead formHead = onlFormHeadList.get(0); + //根据表名去找在线附表 + List fuonlineTableList = onlineTableService.list(new LambdaQueryWrapper().eq(OnlineTable::getTableName, onlFormHeadList.get(0).getTableName())); + //主表id对象 + OnlineColumn onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper() + .eq(OnlineColumn::getColumnName, onlFormField.getFieldName()) + .in(OnlineColumn::getTableId, onlineTableList.get(0).getTableId())); + if (onlineColumn == null) { + onlineColumn = onlineColumnService.getOne(new LambdaQueryWrapper() + .eq(OnlineColumn::getColumnName, onlFormField.getFieldName().toLowerCase()) + .in(OnlineColumn::getTableId, onlineTableList.get(0).getTableId())); + } + //附表关联的主表字段+附表id + if (fuonlineTableList == null || fuonlineTableList.isEmpty() || (!oldmap01.containsKey(onlineColumn.getColumnId() + "_0_" + fuonlineTableList.get(0).getTableId()))) { + OnlineDatasourceRelationDto onlineDatasourceRelationDto = new OnlineDatasourceRelationDto(); + onlineDatasourceRelationDto.setDatasourceId(onlineDatasource.getDatasourceId()); + //关联名称 + onlineDatasourceRelationDto.setRelationName(onlFormHead.getTableName() + "_" + onlFormField.getFieldName() + "_" + formHead.getTableName()); + //关联标识 + onlineDatasourceRelationDto.setVariableName(onlFormHead.getTableName() + "_" + onlFormField.getFieldName() + "_" + formHead.getTableName()); + //关联类型 + onlineDatasourceRelationDto.setRelationType(0); + //主键字段id + onlineDatasourceRelationDto.setMasterColumnId(onlineColumn.getColumnId()); + //从表名称 + onlineDatasourceRelationDto.setSlaveTableName(formHead.getTableName()); + //从表字段名称 + onlineDatasourceRelationDto.setSlaveColumnName("id"); + //是否级联删除 + onlineDatasourceRelationDto.setCascadeDelete(true); + //是否左连接查询 + onlineDatasourceRelationDto.setLeftJoin(false); + + OnlineDatasourceRelation onlineDatasourceRelation = + MyModelUtil.copyTo(onlineDatasourceRelationDto, OnlineDatasourceRelation.class); + String appCode = TokenData.takeFromRequest().getAppCode(); + if (!StrUtil.equals(onlineDatasource.getAppCode(), appCode)) { + errorMessage = "数据验证失败,当前应用并不包含该数据源Id!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } +// OnlineDblink onlineDblink = onlineDblinkService.getById(onlineDatasource.getDblinkId()); + SqlTable slaveTable = onlineDblinkService.getDblinkTable( + onlineDblink, onlineDatasourceRelationDto.getSlaveTableName()); + if (slaveTable == null) { + errorMessage = "数据验证失败,指定的数据表不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + SqlTableColumn slaveColumn = null; + for (SqlTableColumn column : slaveTable.getColumnList()) { + if (column.getColumnName().equals(onlineDatasourceRelationDto.getSlaveColumnName())) { + slaveColumn = column; + break; + } + } + if (slaveColumn == null) { + errorMessage = "数据验证失败,指定的数据表字段 [" + onlineDatasourceRelationDto.getSlaveColumnName() + "] 不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + // 验证关联Id的数据合法性 + CallResult callResult = + onlineDatasourceRelationService.verifyRelatedData(onlineDatasourceRelation, null); + if (!callResult.isSuccess()) { + errorMessage = callResult.getErrorMessage(); + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + onlineDatasourceRelation = onlineDatasourceRelationService.saveNew(onlineDatasourceRelation, slaveTable, slaveColumn); + } else { + if (updatedTable.contains(formHead.getTableName())) { + continue; + } + updatedTable.add(formHead.getTableName()); + //更新表字段 + SqlTable slaveTable = onlineDblinkService.getDblinkTable( + onlineDblink, formHead.getTableName()); + if (slaveTable == null) { + errorMessage = "数据验证失败,指定的数据表不存在!"; + return ResponseResult.error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorMessage); + } + List sqlTableColumnList = slaveTable.getColumnList(); + Set tableids = new HashSet<>(); + tableids.add(fuonlineTableList.get(0).getTableId()); + List listColumn = onlineColumnService.getOnlineColumnListByTableIds(tableids); + List columnNames = listColumn.stream().map(OnlineColumn::getColumnName).collect(Collectors.toList()); + sqlTableColumnList = sqlTableColumnList.stream().filter(x -> !columnNames.contains(x.getColumnName().toLowerCase())).collect(Collectors.toList()); + onlineColumnService.saveNewList(sqlTableColumnList, fuonlineTableList.get(0).getTableId()); + } + } + } + return null; + } } diff --git a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java index 1009f9b..cfcd9f3 100644 --- a/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java +++ b/common/common-online/src/main/java/apelet/common/online/service/impl/OnlineDatasourceServiceImpl.java @@ -11,7 +11,10 @@ import apelet.common.dbutil.object.SqlTable; import apelet.common.online.dao.OnlineDatasourceMapper; import apelet.common.online.dao.OnlineDatasourceTableMapper; import apelet.common.online.dao.OnlinePageDatasourceMapper; -import apelet.common.online.model.*; +import apelet.common.online.model.OnlineDatasource; +import apelet.common.online.model.OnlineDatasourceTable; +import apelet.common.online.model.OnlinePageDatasource; +import apelet.common.online.model.OnlineTable; import apelet.common.online.service.OnlineDatasourceRelationService; import apelet.common.online.service.OnlineDatasourceService; import apelet.common.online.service.OnlineTableService; @@ -83,6 +86,7 @@ public class OnlineDatasourceServiceImpl extends BaseService WIDGET_TO_FIELD_TYPE = new HashMap<>(); + private static final Map DEFAULT_LENGTH = new HashMap<>(); + + static { + // 文本输入框 → varchar + WIDGET_TO_FIELD_TYPE.put(1, "0"); + DEFAULT_LENGTH.put(1, 255); + // 数字输入框 → int + WIDGET_TO_FIELD_TYPE.put(3, "1"); + // 开关组件 → int + WIDGET_TO_FIELD_TYPE.put(5, "1"); + // 滑块组件 → decimal + WIDGET_TO_FIELD_TYPE.put(6, "4"); + // 单选组件 → varchar + WIDGET_TO_FIELD_TYPE.put(7, "0"); + DEFAULT_LENGTH.put(7, 255); + // 复选框 → varchar + WIDGET_TO_FIELD_TYPE.put(8, "0"); + DEFAULT_LENGTH.put(8, 255); + // 下拉选择框 → varchar + WIDGET_TO_FIELD_TYPE.put(10, "0"); + DEFAULT_LENGTH.put(10, 255); + // 级联选择框 → varchar + WIDGET_TO_FIELD_TYPE.put(12, "0"); + DEFAULT_LENGTH.put(12, 255); + // 树形选择 → varchar + WIDGET_TO_FIELD_TYPE.put(13, "0"); + DEFAULT_LENGTH.put(13, 255); + // 评分组件 → int + WIDGET_TO_FIELD_TYPE.put(14, "1"); + DEFAULT_LENGTH.put(14, 5); + // 日期选择框 → datetime + WIDGET_TO_FIELD_TYPE.put(20, "3"); + // 颜色选择 → varchar + WIDGET_TO_FIELD_TYPE.put(30, "0"); + DEFAULT_LENGTH.put(30, 50); + // 附件上传 → varchar + WIDGET_TO_FIELD_TYPE.put(31, "0"); + DEFAULT_LENGTH.put(31, 255); + // 图片上传 → varchar + WIDGET_TO_FIELD_TYPE.put(653, "0"); + DEFAULT_LENGTH.put(653, 255); + // 富文本编辑 → varchar(2000) + WIDGET_TO_FIELD_TYPE.put(32, "0"); + DEFAULT_LENGTH.put(32, 2000); + // MD编辑器 → varchar(2000) + WIDGET_TO_FIELD_TYPE.put(760, "0"); + DEFAULT_LENGTH.put(760, 2000); + // 用户选择 → bigint + WIDGET_TO_FIELD_TYPE.put(400, "2"); + // 部门选择 → bigint + WIDGET_TO_FIELD_TYPE.put(401, "2"); + // 关联选择 → bigint + WIDGET_TO_FIELD_TYPE.put(402, "2"); + // 时间选择器 → datetime + WIDGET_TO_FIELD_TYPE.put(652, "3"); + // 文件上传 → varchar + WIDGET_TO_FIELD_TYPE.put(750, "0"); + DEFAULT_LENGTH.put(750, 255); + } + + public static String getFieldTypeId(Integer widgetType) { + String type = WIDGET_TO_FIELD_TYPE.get(widgetType); + if (type == null) { + throw new IllegalArgumentException("不支持的控件类型: " + widgetType); + } + return type; + } + + public static Integer getDefaultLength(Integer widgetType) { + return DEFAULT_LENGTH.get(widgetType); + } + + public static boolean isDataWidget(Integer widgetType) { + return WIDGET_TO_FIELD_TYPE.containsKey(widgetType); + } +} diff --git a/common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java b/common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java index dfce3fb..d6021f7 100644 --- a/common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java +++ b/common/common-tenant-online/src/main/java/apelet/common/tenant/online/controller/TenantOnlinePageController.java @@ -8,6 +8,7 @@ import apelet.common.online.controller.OnlinePageController; import apelet.common.online.dto.OnlineDatasourceDto; import apelet.common.online.dto.OnlinePageDatasourceDto; import apelet.common.online.dto.OnlinePageDto; +import apelet.common.online.model.OnlinePage; import apelet.common.online.vo.OnlineDatasourceVo; import apelet.common.online.vo.OnlinePageDatasourceVo; import apelet.common.online.vo.OnlinePageVo; @@ -43,7 +44,7 @@ public class TenantOnlinePageController extends OnlinePageController { @OperationLog(type = SysOperationLogType.ADD) @PostMapping("/add") @Override - public ResponseResult add(@MyRequestBody OnlinePageDto onlinePageDto) { + public ResponseResult add(@MyRequestBody OnlinePageDto onlinePageDto) { return super.add(onlinePageDto); }