# 定制层级 在通道 1(元数据驱动定制,见[定制通道](customization-channels.md))内部,xly 支持**分层覆盖模型**。基础表单在系统级定义;租户和用户在其上叠加修改,且不修改基础表单。框架在请求时合并这些层,并向 SPA 返回一个合并后的表单。 自定义字段的贯穿示例见[切片 4](../slices/04-custom-field.md)。本页是规范概览。 ## 各层 ```text gdsconfigformmaster ← 系统默认(表单) ↓ 被覆盖 gdsconfigformpersonalize ← 每租户整表单覆盖 (替换 sSqlStr/sWhere/sOrder) ↓ 然后是基础从表 gdsconfigformslave ← 系统默认字段 ↓ 被覆盖 / 扩展 gdsconfigformcustomslave ← 每租户字段(新增、隐藏、覆盖) ↓ 可选地继续微调 gdsconfigformuserslave ← 每用户视图偏好 (列顺序、隐藏列) ``` 每一层都通过 `sParentId` 连接到上一层。没有任何连接由 FK 强制;见[无 FK 现实](semantic-fk.md)。 ## 每层回答的问题 | 层 | 作用域 | 回答 | |---|---|---| | `gdsconfigformmaster` | 系统级 | “这个表单默认长什么样?” | | `gdsconfigformpersonalize` | 每租户 | “这个租户是否要不同的 SQL / where / order?” | | `gdsconfigformslave` | 系统级 | “这个表单默认有哪些字段?” | | `gdsconfigformcustomslave` | 每租户 | “这个租户是否要额外字段、隐藏字段或改标签?” | | `gdsconfigformuserslave` | 每用户 | “这个用户是否在自己的表格里调整过列顺序或隐藏列?” | ## 合并如何发生 框架按顺序读取每层,并按 `sName`(字段名)合并。自定义从表行与基础从表拥有相同 `sName` 时:覆盖。新的 `sName`:追加。没有对应自定义行的基础从表:原样透传。合并发生在 `BusinessBaseServiceImpl.getModelBysId`(第 181 行)及其调用的 helper 中:`BusinessGdsconfigformsServiceImpl.getFormSlaveData` + `getFormCustomSlaveData`。 两个数据库**视图**通过连接 form-master 和相关 slave 表来支持合并: - `gdsconfigformslavemasterview` — 基础层。 - `gdsconfigformcustomslavemasterview` — 覆盖层。 服务代码直接读取这些视图;真正的合并逻辑在 Java 中,而不是 SQL 中。 ## 这些层做不到什么 覆盖表允许租户**向表单添加字段**,但不会也不能对底层物理表执行 `ALTER TABLE`。如果自定义字段超出了基础表已经提供的列,保存时就没有可绑定目标,除非满足以下条件之一: 1. 基础表刻意设计得很“宽”,预留了定制列。 2. 协调一次手工 schema 迁移来添加该列。 3. 定制只与视图相关(字段是计算或展示出来的,不存储)。 情况 (2) 是典型的工程师主导路径。情况 (1) 解释了 schema 中一些看起来异常宽泛的表。当定制需要超过覆盖能力的*结构性*变化时,[切片 5(每客户 SQL 覆盖)](../slices/05-customer-sql-override.md)就是下一个通道。