# 数据库架构

> **最后更新**: 2026-04-06

---

## Multi-Schema 架构

FF AI Workspace 采用 Prisma Multi-Schema 架构，按业务领域划分 Schema：

```
corp_approval        # 审批流程
corp_hr              # 部门、岗位、区域
corp_request         # 业务请求
mfg_inventory        # 零件库存
platform_iam         # 用户、角色、权限
platform_form        # 表单定义与提交
platform_audit       # 审计日志
platform_logging     # 系统日志
platform_notify      # 通知
platform_automation  # 自动化规则
platform_ai          # AI 助手
platform_knowledge   # 知识库
platform_tickets     # 工单
platform_feedback    # 反馈
platform_devtracker  # 开发管理
platform_performance # 绩效管理
platform_meeting_attendance  # 会议考勤
platform_site_attendance     # 工地考勤
```

**为什么按业务域分**：逻辑隔离、权限控制更精细、便于横向扩展、数据迁移更安全。

---

## 命名规范

- **表名**：小写 + 下划线，复数形式
- **字段名**：小写 + 下划线
- **外键**：`{表名}_id`
- **索引**：`idx_{表名}_{字段}`
- **约束**：`fk_{表名}_{引用表}`

## 标准字段

### 所有新建业务表强制字段

| Prisma 字段 | DB 列 | 类型 | 说明 |
|---|---|---|---|
| `id` | `id` | UUID | 主键 |
| `createdAt` | `created_at` | TIMESTAMPTZ(3) | 创建时间 |
| `updatedAt` | `updated_at` | TIMESTAMPTZ(3) | 更新时间 |
| `createdById` | `created_by_id` | UUID | 创建人，**DataScope SELF 依据** |
| `organizationId` | `organization_id` | UUID | 所属组织，**DataScope ORGANIZATION 依据**；即便当前只有一个租户也必须建列，避免后期加列+回填的灾难 |

### 按业务需要的 DataScope 字段

| Prisma 字段 | DB 列 | 何时必须 |
|---|---|---|
| `departmentId` | `department_id` | 模块需要按部门隔离数据 |
| `regionId` | `region_id` | 模块需要按区域隔离 |

### 可选字段

| Prisma 字段 | DB 列 | 说明 |
|---|---|---|
| `deletedAt` | `deleted_at` | 软删除标记 |
| `version` | `version` | 乐观锁 |

### 必备索引

为所有启用的 DataScope 字段加索引（按实际存在的字段选）：

```prisma
@@index([organizationId])
@@index([departmentId])       // 仅启用 DEPARTMENT scope 时
@@index([createdById])        // 高频按创建人查询时
@@index([regionId])           // 启用 REGION scope 时
```

### 禁止

- 不得用 `creatorId` / `authorId` / `ownerId` / `submittedBy` / `userId` 等同义词代替 `createdById`
- 不得用 `orgId` / `tenantId` 等变体代替 `organizationId`
- 新表不得省略 `organizationId`——若业务上确实全局（字典、审计日志），仍保留列但允许为 `null`，DataScope 配置按 `ALL` 处理
- 字段类型不得偏离：ID 列统一 `@db.Uuid`，时间戳统一 `@db.Timestamptz(3)`

### 存量表豁免

- 存量业务表（如 Part、Meeting）**不强制改造**
- 需要接入 DataScope 时，**优先**在 `@DataScope` 上用 `fields` 映射到现有列
- **仅当业务必须按部门/组织隔离、且现有字段无法反推**时，才走"加列 + 数据回填"迁移，并在模块数据模型文档里记录原因

> 依据：`docs/standards/09-iam-security.md` 四层权限模型 Layer 4 的 DataScope 默认字段映射约定。
> 命中本约定的新模块，`@DataScope('xxx')` 零配置即可工作；不命中则必须在装饰器上写 `fields` 覆盖。

---

## 种子数据管理

种子数据按依赖关系分三层，严格按顺序加载：

| 层级 | 种子文件 | 依赖 | 说明 |
|------|---------|------|------|
| 1（基础） | `iam-seed.ts` + `roles.seed.ts` | 无 | 角色、权限、系统管理员 |
| 2（组织） | `org-seed.ts` | 层 1 | 组织、部门、测试用户及关系 |
| 3（模块） | `performance-seed.ts` 等 | 层 1 + 2 | 各业务模块基础配置数据 |

### 关键规则

- `cleanupAllData` 只清除层 3，不清除 IAM 和组织数据
- 模块种子必须幂等（upsert 而非 insert）
- 测试用户的 manager-subordinate 关系必须在层 2 定义
- 新增模块种子在 `index.ts` 注册，并说明被哪些测试依赖

> 来源：03-16 至 03-19 测试报告反复出现种子数据问题（模板被清空、用户缺关系导致多角色流程失败），本规范固化这些经验。

---

## 关键约束

- **禁止跨 Schema 外键**：模块间数据通过 UUID 关联查询，不建跨 schema FK，保持隔离
- **JSONB 使用规则**：JSONB 仅用于非结构化/半结构化数据（如表单字段配置、动态扩展属性），结构化业务数据必须用独立列
- **迁移安全**：不在单次迁移中删列或改列类型。先加新列 → 迁移数据 → 再删旧列
- **每次提交最多一个迁移文件**：一个功能涉及多个 schema 变更时，合并为一个迁移文件；禁止一个提交中包含多个迁移文件

---

## 相关文档

- [数据库设计 Review Checklist](./20-database-review-checklist.md) — **22 条原则清单**，schema PR / 模块 audit / 上线前 sanity check 必查
- [数据分层与元数据策略](./16-data-layering-and-metadata-policy.md) — 三层架构（L1/L2/L3）+ 默认不立元数据驱动 L4 + 加回 L4 的触发条件
- [数据库规范详情](../../.agents/skills/database-main/references/database-standards.md)
- [系统架构](./01-system-architecture.md)
