---
name: database-main
description: >
  当涉及 Prisma schema 变更、数据库迁移、数据结构设计或约束策略时使用，
  遵循多文件 schema、增量变更与迁移规范。
  也用于 schema 变更后的结构审查（数据类型/约束/索引/关系/隔离/迁移安全/范式）。
  触发短语：schema 变更、数据库迁移、数据结构设计、审查 schema、
  检查数据库结构、schema review、database review。
---

# 数据库与迁移技能（Prisma）

## 项目概览入口
- 项目定位、技术栈与目录入口请阅读：`../references/project-overview.md`

## 目的与触发
- 目的：安全、可审查地演进数据库 schema，确保约束正确、迁移合规、可验证。
- 触发：需要修改 Prisma schema、设计/调整数据结构、执行迁移或制定约束策略时。

## 快速守则
- 只通过 Prisma 迁移变更数据库，禁止手改生产库；禁止修改已应用/已合并迁移。
- 发生需求改动时，先按影响面更新 `docs/modules/{module}/` 对应文档（01-10，按需），再进行 schema/迁移实现。
- 多文件 schema（v6.7.0+），每业务域一个 `.prisma`，每个模型必须 `@@schema("schema_name")`。
- 增量式变更：先加列/表 → 回填 → 再加约束（NOT NULL / UNIQUE / FK / CHECK）。
- 文档/代码保持同步；最小差异修改，避免“聪明”但脆弱的设计。
- 迁移命名使用 Prisma 默认时间戳。

## 结构速览
- Prisma 目录：`backend/prisma/schema/`
- 多 schema 示例：`approval` / `form` / `hr` / `corp` / `shared`

## 工作流（顺序）
1) 定位受影响的业务域 `.prisma` 文件，按最小差异修改 schema（命名小写下划线，表名用复数名词）。  
2) 创建迁移（Prisma 标准流程，禁改历史迁移）。  
3) 更新 Prisma Client 相关代码与类型引用。  
4) 给出验证命令（migrate/dev 或 migrate/deploy，generate，测试）。  
5) 确认增量约束策略与回滚可行性。

## 检查清单
- 未修改已合并/已应用迁移；变更最小且增量约束。
- 每个模型声明 `@@schema("schema_name")`，命名规范。
- 提供验证命令与最小测试/复现步骤。
- 约束放数据库层（NOT NULL/UNIQUE/FK/CHECK），业务规则在服务层。
- 如有数据回填，步骤清晰、可重复。
- 新增/删除模型时，同步更新 `testing/backend/helpers/cleanup.helper.ts` 的清理表清单（注意 FK 依赖顺序）。
- 新增模块种子时，确保脚本幂等（upsert），并在 `backend/prisma/seeds/index.ts` 中按依赖顺序注册。

---

## Schema Review（数据库结构审查）

当 schema 变更完成后（迁移已生成），执行结构审查以发现潜在问题。
触发方式：用户说"审查一下 schema"、"检查数据库结构"、"schema review"。

### 审查流程

#### Step 1: 收集变更范围

```bash
# 查看本分支涉及的 schema 变更
git diff main -- backend/prisma/schema/ --stat
git diff main -- backend/prisma/schema/
# 查看生成的迁移
git diff main -- backend/prisma/migrations/ --stat
```

读取变更涉及的 `.prisma` 文件和迁移 SQL。

#### Step 2: 七维结构审查

对每个变更的模型/字段/关系，逐项检查：

**1. 数据类型合理性**

| 检查项 | 说明 |
|--------|------|
| ID 类型 | 推荐 `String @id @default(uuid())` 或 `BigInt`，避免自增 `Int` 用于分布式场景 |
| 时间字段 | 必须用 `DateTime`（Prisma 映射 `timestamptz`），不要用 `String` 存时间 |
| 金额/精度 | 使用 `Decimal` 而非 `Float`，避免精度丢失 |
| 长文本 vs 短文本 | `String` 即可（PostgreSQL 无 varchar/text 性能差异），但有长度约束需求时用 `@db.VarChar(N)` |
| 枚举 vs 字符串 | 固定值集合用 Prisma `enum`，不要用自由字符串 |
| JSON 字段 | 警惕 `Json` 类型——是否掩盖了应该建模为关系的数据？ |
| Boolean 默认值 | `Boolean` 字段必须有 `@default(false)` 或 `@default(true)`，避免 null 三态 |

**2. 约束完整性**

| 检查项 | 说明 |
|--------|------|
| 非空约束 | 业务必填字段是否标记为必填（无 `?`）？可选字段是否确实可选？ |
| 唯一约束 | 业务唯一性（如组织内唯一名称）是否有 `@@unique` 或 `@unique`？ |
| 外键完整性 | 每个 `@relation` 是否正确指向目标模型？级联策略是否合理？ |
| 组合唯一 | 多字段联合唯一是否用 `@@unique([field1, field2])`？ |
| 软删除+唯一 | `deletedAt` 与唯一约束组合是否有隐患？（推荐 partial unique index） |
| CHECK 约束 | 值域限制（如 `score >= 0 AND score <= 100`）是否在数据库层实现？ |
| 可空性变更级联 | 字段从必填改为可空时，所有使用该字段的聚合/统计查询（`SUM`/`AVG`/`COUNT`/`findMany` + 计算）是否加了 `{ not: null }` 过滤？未加会导致 null 值被 `Number()` 转为 NaN 或统计偏差 |

**3. 索引策略**

| 检查项 | 说明 |
|--------|------|
| 外键索引 | **所有外键字段必须有索引**（Prisma 不自动创建） |
| 查询驱动索引 | 高频查询的 WHERE/ORDER BY 字段是否有索引？ |
| 组合索引顺序 | 组合索引字段顺序是否与查询模式匹配（高选择性字段在前）？ |
| 部分索引 | 软删除场景是否使用 partial index（`WHERE deleted_at IS NULL`）？ |
| 过度索引 | 是否添加了不必要的索引？（写多读少的表慎加） |

**4. 关系设计**

| 检查项 | 说明 |
|--------|------|
| 关系方向 | 一对多的外键是否在"多"侧？ |
| 多对多 | 是否使用了显式中间表（而非隐式 `@relation`）？推荐显式以便扩展 |
| 级联删除 | `onDelete` 策略是否合理？父删除时子记录应 Cascade/SetNull/Restrict？ |
| 自引用 | 树形结构（如部门层级）的 `parentId` 是否有 `@relation` 且允许 null？ |
| 循环依赖 | 模型之间是否存在循环外键？如有，是否必要？ |

**5. 多租户与隔离**

| 检查项 | 说明 |
|--------|------|
| organizationId | 业务数据表是否都有 `organizationId` 字段？ |
| 组合唯一含租户 | 唯一约束是否包含 `organizationId`（如 `@@unique([organizationId, name])`）？ |
| 查询隔离 | 查询是否都带 `organizationId` 条件？是否有跨组织泄漏风险？ |

**6. 迁移安全**

| 检查项 | 说明 |
|--------|------|
| 向后兼容 | 迁移是否可零停机执行？（先加列→回填→再加约束） |
| 表锁风险 | 是否有 `ALTER TABLE` 操作会锁大表？（如加 NOT NULL 到有数据的列） |
| 回滚可行 | 如果迁移失败，能否安全回滚？ |
| 数据迁移 | 是否需要数据回填脚本？回填是否幂等？ |

**7. 范式与反模式**

| 检查项 | 说明 |
|--------|------|
| 第三范式 | 是否存在传递依赖（非键字段依赖另一个非键字段）？ |
| 宽表警告 | 单表超过 20 个字段是否合理？是否应该拆分？ |
| 命名一致性 | 字段命名是否全部 `camelCase`？表名是否全部复数 `PascalCase`？ |
| 审计字段 | 是否包含 `createdAt`/`updatedAt`？需要时是否有 `createdBy`/`updatedBy`？ |
| 冗余字段 | 是否存在可从关系派生的冗余存储？如有，是否有充分的性能理由？ |
| **同步中心复用**（红灯阻断） | 新增表名/职责形如 `*SyncRun` / `*SyncLog` / `*SyncHistory` / `*SyncTask` 时，必须先确认 `platform_automation.AutomationTask` + `AutomationExecution` 不够用。理由：所有外部系统同步的执行记录、任务定义、调度都应复用同步中心，不得并行造轮子。详见 `docs/standards/02-backend-architecture.md` "外部数据同步"。 |
| **同步源数据 schema 归属**（红灯阻断） | 来源于外部系统的镜像数据表（如 `DingtalkEmployee` / `AdpPtoSchedule`）必须放在 `platform_automation` schema，不得放在业务 schema（如 `corp_hr`、`platform_meeting_attendance`）下。 |

#### Step 3: 输出审查报告

```
## Schema Review 报告

### 变更摘要
- 涉及 schema 文件: N 个
- 新增模型: ...
- 修改模型: ...
- 新增迁移: ...

### 发现

#### 🔴 阻断级 (必须修复)
- [文件:行号] 问题描述 → 修复建议

#### 🟡 建议级 (推荐修复)
- [文件:行号] 问题描述 → 修复建议

#### ✅ 确认项 (已通过)
- 外键索引: 全部已添加
- 组织隔离: 全部包含 organizationId
- ...

### 结论
- `pass` / `pass-with-suggestions` / `needs-fix` / `block`
```

#### Step 4: 可选——查询性能预检

如果变更涉及新的查询模式，用数据库连接验证索引有效性：

```bash
# 检查现有索引
docker exec ffoa-dev-postgres psql -U ffws_dev -d ffws_dev -c "\di+ <table_name>*"

# 对关键查询做 EXPLAIN
docker exec ffoa-dev-postgres psql -U ffws_dev -d ffws_dev -c "EXPLAIN ANALYZE <query>"
```

标记缺失索引导致的 Seq Scan，给出 `CREATE INDEX` 建议。

---

## 参考
- `./references/database-standards.md`
- `docs/standards/04-database-architecture.md`
- `references/schema-design.md`
- `references/naming-conventions.md`
- `references/migration-guide.md`
