04-custom-field.md 6.13 KB

切片 4 — 扩展:按租户添加自定义字段

xly 卖给许多客户。每个客户都想记录框架原本不知道的一些东西:自定义编码、内部备注、分类标签。受支持的做法是向一个定制表插入行,并在运行时把该表内容合并到基础表单之上;这样可以不 fork 代码库,也不修改共享元数据

本切片追踪该机制。这是首次覆盖 xly 的每租户覆盖模型,即单代码库 / 多客户 SaaS 得以运作的定制模型。

三层定制

xly 在每个基础表单之上叠加三张定制表。每张表作用域不同,回答的问题也不同。

作用域 覆盖层 dev 行数
gdsconfigformpersonalize 每租户(sBrandsId + sSubsidiaryId form-master,覆盖整个表单的 sSqlStr / sWhere / sOrder 18
gdsconfigformcustomslave 每租户 form-slave,给表单新增、隐藏或替换单个字段 0
gdsconfigformuserslave 每用户 form-slave,每用户字段微调(列顺序、隐藏列) 9

基础表单是 gdsconfigformmaster + gdsconfigformslave,即系统默认。上面三张表是运行时应用并合并的覆盖层

本切片聚焦中间行:gdsconfigformcustomslave,也就是“给租户 X 添加自定义字段”的规范通道。

示例会是什么样

假设租户“山东星海印务”想在客户列表表单上增加“客户内部编码”字段。客户或实施人员不修改 gdsconfigformslave,而是:

  1. 打开后台中编辑 gdsconfigformcustomslave 行的模块(某个系统管理页面,可能是 界面显示内容配置;需在后台中点击验证)。
  2. 新增一行:
    • sParentId = 表单 sId(与基础 slave 指向同一个表单)。
    • sName = 'sInternalCode'(字段列名)。
    • sChinese = '客户内部编码'
    • sControlName = '文本框'
    • sBrandsId + sSubsidiaryId = 该租户 ID。
  3. 可选地给底层物理表添加一列(手工 schema 迁移;框架不会自动 ALTER TABLE)。否则该字段只存在于表单上,保存时没有可绑定目标。

下次该租户的任意用户加载表单时,会看到额外列。其他租户仍看到未修改的基础表单。

dev 中为空的重要说明。 gdsconfigformcustomslavexlyweberp_saas_ai 中有 0 行。表已接入框架,但当前 dev DB 没有租户使用它。下面的追踪来自代码推导;实时观察仍待补。

运行时如何合并

前端不会看到三张表,只会看到一个合并表单。框架通过两个数据库视图读取基础 slave 和覆盖 slave 与 form-master 的连接:

服务层读取两者,按 sName 合并,并向 SPA 返回单一 slave 列表。如果租户的 gdsconfigformcustomslave 行与基础 slave 拥有相同 sName,则覆盖;新的 sName 会追加。隐藏 / 移除情况通过 bVisible 处理。

gdsconfigformpersonalize 的表单级覆盖以类似方式在 form-master 层合并:请求时运行时调用 configformpersonalizeService.getConfigformpersonalize(sBrandsId, sSubsidiaryId, gdsconfigformmasterId);如果返回行,就用其 sConfigSqlStr / sConfigWhere / sConfigOrder 替换基础表单的 sSqlStr / sWhere / sOrder

合并顺序如下:

gdsconfigformmaster              (系统默认表单)
  ↓ 覆盖
gdsconfigformpersonalize         (每租户整表单覆盖)
  ↓ 然后是基础从表
gdsconfigformslave               (系统默认字段)
  ↓ 覆盖 / 扩展
gdsconfigformcustomslave         (每租户字段)
  ↓ 可选继续微调
gdsconfigformuserslave           (每用户视图偏好)

这是“无 FK、语义 FK 现实”在框架自身配置上的应用:没有外键保证 gdsconfigformcustomslave.sParentId 真存在于 gdsconfigformmaster.sId。孤儿行可能存在,并在合并时被静默忽略。维护人员应增加审计脚本标记此类孤儿。

为什么不改代码也能工作

最终客户不用向工程师申请新列。他们打开后台构建器,增加一行,字段只在自己的前台中出现,其他租户不受影响。这个单代码库属性来自 xly 的数据驱动论点;代价是每次请求合并元数据的运行时成本,以及三张多数表单不会使用的定制表带来的 schema 膨胀。

本切片引入的概念

  • 定制层级:上面的三层覆盖模型、合并方式以及各层适用时间。
  • 无 schema 扩展限制:框架会渲染额外字段,但不会也不能 ALTER TABLE。基础表没有的自定义字段需要协调的手工 schema 迁移、宽表预留列,或 JSON 列模式。切片 5 覆盖需要结构变化时的每客户 SQL 覆盖通道。

本切片使用的参考

待验证项

  1. 找到实际编辑 gdsconfigformcustomslave后台 页面。最可能是侧边栏中的 界面显示内容配置
  2. 追踪合并代码。 确认合并是在 MyBatis(两个视图)中完成,还是在 Java(getFormSlaveData + getFormCustomSlaveData)中完成。
  3. bVisible = false 语义。 它是隐藏已有基础字段,还是只抑制覆盖行本身?很可能是前者,但需要确认。
  4. 真实示例。 在生产中找一个租户的实际 gdsconfigformcustomslave 行作为贯穿示例。