# 主从单据模式

> **这个代码库里有两个互不相关的“主 / 从”概念。**
> 本页讨论的是**单据行**模式：报价、销售订单、生产工单等业务单据由 1 行表头 + N 行明细组成。
> **DataSource** 层的 master / slave（`xlyApi` 和 `xlyInterface` 中通过 `MasterDataSourceConfig` / `SlaveDataSourceConfig` 做写库 / 读库连接路由，并配套这些服务内的 `mastermapper/MasterBaseMapper.xml` / `slavemapper/SlaveBaseMapper.xml`）是另一个概念，见[技术栈](../reference/maintainer/tech-stack.md#persistence)中 HikariCP / 数据源相关说明，也会在运行时页面间接涉及。两者只是名字重叠。

xly 中几乎所有业务单据，例如报价单、销售订单、工单、付款凭证，都是以**一行表头 + N 行明细**存储。xly 对此的术语是 **master / slave**。master 保存单据身份和汇总；每个 slave 行是一条明细、一段阶段、一项材料、一个产品或一笔费用。

这个模式无处不在：

| 单据 | Master | Slave 表族 |
|---|---|---|
| 报价单 | `quoQuotationMaster` | `quoQuotationSlave`、`quoQuotationMaterials`、`quoQuotationProcess`、`quoQuotationFreight`、`quoQuotationParam`、`quoQuotationControl`、`quoQuotationControlCombine`、`quoQuotationPacking`，以及编辑中使用的并行 `*_tmp` 表族。 |
| 工单 | `mftWorkOrderMaster` | `mftWorkOrderSlave`、`mftWorkOrderMaterials`、`mftWorkOrderProcess`、`mftWorkOrderPacking`、`mftWorkOrderControl`、`mftWorkOrderControlCombine`、`mftWorkOrderParam`、`mftWorkOrderChangeApply`，以及若干 `*_tmp` 表族。 |
| 销售订单 | `salSalesOrderMaster` | `salSalesOrderSlave`、`salSalesOrderMaterials`、`salSalesOrderProcess`、`salSalesOrderControl`、`salSalesOrderControlCombine`、`salSalesOrderAddress`、`salSalesOrderParam` 等。 |
| 成本分析 | `accOrderCostAnalysis`（无 `Master` 后缀） | `accOrderCostAnalysisOperation` |
| 框架自身表单 | `gdsconfigformmaster` | `gdsconfigformslave`、`gdsconfigformcustomslave`、`gdsconfigformuserslave` |

这个约定足够普遍，因此 Wiki 使用 **master** 和 **slave** 作为专门术语。它们分别表示表头和明细；命名来自旧代码库，值得保留，因为它逐字出现在 14k+ 表名和列名中。

## 模式内约定

- 从表行的连接列通常叫 `sMasterId` 或 `sParentId`（这是[语义 FK](semantic-fk.md)，不强制）。有时会使用更具体的名称，例如报价从表中的 `sQuoId`、工单从表中的 `sWoId`；以从表列为准。
- Master 及其所有 slave 共享相同的多租户作用域列（`sBrandsId`、`sSubsidiaryId`）。
- “Control” 变体从表（例如 `mftWorkOrderControl`、`quoQuotationControl`）保存复合或聚合状态：汇总、状态标志、计算列。它们仍然是 master 的从表。
- 子明细表通过额外后缀命名，例如 `...SlaveChildren` 或 `...ControlCombine`，并连接到某个从表行。

## 为什么这个模式对 Wiki 重要

- [切片 1](../slices/01-hello-world.md) 风格导航中的几乎每个表单，都是某个单据表族的 *master*。当选中 master 行时，slave 会作为嵌套表格或第二表单加载。
- 框架自身的表单元数据（[`gdsconfigformmaster`](../auto-catalog/tables/gdsconfigformmaster.md) / [`gdsconfigformslave`](../auto-catalog/tables/gdsconfigformslave.md)）本身就是该模式实例：框架用要求 PM 遵循的同一约定来描述自己。
- 定制覆盖（[`gdsconfigformcustomslave`](../auto-catalog/tables/gdsconfigformcustomslave.md)、[`gdsconfigformuserslave`](../auto-catalog/tables/gdsconfigformuserslave.md)）是同级 slave 表，不是更深层模式。见[切片 4](../slices/04-custom-field.md)。

## “Slave” 命名说明

这个词在英文中带有中文“主表 / 从表”没有的额外含义。Wiki 保留 “slave” 原词，是因为代码库、schema 和自动目录中有 14k+ 标识符逐字使用它；翻译成其他词会让文档偏离开发者实际 grep 到的内容。

保留这个词也有代价。最初命名本身并不好：`主表 / 从表` 完全可以翻成 `master / detail` 或 `header / line`，既符合英文开发习惯，也更贴近关系语义。继续保留 “slave” 的成本，会由每个需要阅读或输入这个词的英文维护者承担，也会由未来任何一次全 schema 改名承担。Wiki 在这里说明一次，并不能消除成本，只是把它明确写出来。
