# Platform Master — 数据模型

> **module**: platform-master
> **doc_type**: DataModel
> **status**: Active (v3 PR2-4)
> **last_verified**: 2026-05-17

## 1. Schema 总览

PostgreSQL schema 名 `platform_master`。

```
L1a 主数据 (业务可变)
─ customers          + customer_contacts + customer_addresses
─ suppliers          + supplier_contacts
─ partners           + partner_contacts
─ locations          (self-ref hierarchy + customer 回引)

L1b 字典 / 参考数据 (全局静态)
─ currencies         (USD/CNY/EUR/AED ... + decimals + symbol)
─ countries          (CN/US/AE ... + iso3 + region)
─ geo_regions        (APAC/NA/MEA/EU ... + countries[])
─ units_of_measure   (PCS/KG/L/M ... + category)
─ dictionaries       (category+code 联合 unique；5 个 category)

L1c 通用业务对象
─ attachments        (多态 ownerType/ownerId + category)
```

## 2. L1a 主数据

### Customer

| 列 | 类型 | 说明 |
|---|---|---|
| `id` | UUID PK | |
| `code` | String unique | 业务码 |
| `name` | String | 客户名 |
| `type` | enum `CustomerType` | B2B / B2C / INTERNAL |
| `industry` | String? | 行业（也可引 Industry 字典）|
| `countryCode` | VarChar(2) | FK → countries.code |
| `taxId` | String? | 税号 / 统一社会信用代码 |
| `creditLimit` | Decimal(14,2)? | 信用额度 |
| `currencyCode` | VarChar(3) | 默认结算币 |
| `enabled` | Boolean | |
| `metadata` | Json | 模块扩展字段 |
| `organizationId` / `createdAt` / `updatedAt` / `createdById` / `deletedAt` | — | 标准字段 |

子表：
- **CustomerContact** — 外部联系人（name / role / phone / email / isPrimary）
- **CustomerAddress** — 地址（BILLING / SHIPPING / OFFICE / line1+line2+city+state+postalCode+countryCode+isDefault）

### Supplier

| 列 | 类型 | 说明 |
|---|---|---|
| `id` / `code` / `name` | | |
| `type` | enum `SupplierType` | MANUFACTURER / PARTS / LOGISTICS / SERVICE |
| `countryCode` / `currencyCode` | | |
| `paymentTerms` | String? | 如 "Net 30" |
| `leadTimeDays` | Int? | 交货周期 |
| `enabled` / `metadata` / 标准字段 | | |

子表：**SupplierContact**

### Partner

跨模块（销售 agent / mentor / mentee / 报关行 / 培训方）。

| 列 | 类型 | 说明 |
|---|---|---|
| `id` / `code` / `name` | | |
| `role` | enum `PartnerRole` | SALES_AGENT / MENTOR / MENTEE / CUSTOMS_BROKER / TRAINING_PARTNER |
| 其余字段同 Customer | | |

子表：**PartnerContact**

### Location

| 列 | 类型 | 说明 |
|---|---|---|
| `id` / `code` / `name` | | |
| `type` | enum `LocationType` | WAREHOUSE / CUSTOMER_SITE / FACTORY / BONDED_ZONE / IN_TRANSIT / SERVICE_CENTER |
| `countryCode` / `address` | | |
| `latitude` / `longitude` | Decimal(10,6)? | 可选坐标 |
| `parentLocationId` | UUID? | 自引用层级（仓库 → 库位）|
| `customerId` | UUID? | 客户站点回引 |
| 标准字段 | | |

## 3. L1b 字典

### Currency

`code` (3-letter unique) + `name` + `symbol?` + `decimals` (default 2) + `enabled`

Seed: USD / CNY / EUR / AED

### Country

`code` (2-letter unique) + `iso3` (3-letter unique) + `name` + `region?` + `enabled`

Seed: CN / US / AE / DE / GB

### GeoRegion

`code` (≤8 char unique) + `name` + `countries[]` (ISO2 列表)

Seed: APAC / NA / MEA / EU

### UnitOfMeasure

`code` (≤8 char unique) + `name` + `category?` (length/weight/volume/count)

Seed: PCS / KG / L / M

### Dictionary

`(category, code)` 联合 unique；`labelEn` + `labelZh?` + `sortOrder` + `enabled` + `metadata`。

支持的 category：
| category | 用途 | 数量 |
|---|---|---|
| `label_type` | 质量标签 7 种（BODY_FCC / BODY_MADE_IN_CN / BODY_SN / REMOTE_SN / BATTERY_SN / INSPECTION_SHEET / SHIPPING_BOX）| 7 |
| `tariff_type` | 关税类型 | 3 |
| `declaration_type` | 进口申报类型 | 2 |
| `service_issue_type` | 售后问题类型 | 5 |
| `industry` | 行业 | 5 |

判据：无专属字段 / 仅被 FK 引用 / 取值频次不高 → 归 Dictionary（standard 16 §5.1）。

## 4. L1c Attachment（多态）

替代 v2 `robot_attachments`，可挂在任意业务实体。

| 列 | 类型 | 说明 |
|---|---|---|
| `id` | UUID PK | |
| `ownerType` | String | robot_unit / sales_order / delivery_fulfillment / payment_record / rental_agreement / ... |
| `ownerId` | UUID | 被附着实体 ID |
| `category` | String? | SALES_AGREEMENT / COCREATION_SUPERONE / COCREATION_ROBOTIC / DEPOSIT_TRANSFER_FORM / INVOICE / INSPECTION_PHOTO / ... |
| `filename` / `mimeType` / `size` / `storagePath` | | |
| `uploadedById` / `uploadedAt` | | |
| 标准字段 + `deletedAt` | | |

索引：`(ownerType, ownerId)` + `(organizationId, deletedAt)` + `(category)`

## 5. 跨 schema FK / 索引

L1a 主数据被多模块 FK 引用（如 robot_manager.RobotUnit.originalSupplierId → suppliers.id）：
- DB 层建 FK constraint 保证引用完整性
- Prisma 层**不写 @relation**（避免跨 schema 类型污染）
- 主数据表 `organizationId` 索引存在（DataScope 经此索引隔离）

详见 [standard 16 §1.5](../../standards/16-data-layering-and-metadata-policy.md)。

## 6. DataScope 豁免

| 表 | 理由 |
|---|---|
| CustomerContact / CustomerAddress / SupplierContact / PartnerContact | 子表跟随父表 DataScope，无独立 organizationId |
| Attachment | ownerId 是多态 FK 不是 createdById 同义词；保留独立 createdById 审计 |
| 字典表（Currency / Country / GeoRegion / UnitOfMeasure / Dictionary）| 全局可见，organizationId nullable（DataScope ALL）|
