Files
lingxi/docs/工程规范/05-数据库规范.md
Stev_Wang 00ae12435e docs(specs): add AGENTS.md and engineering guidelines
项目尚无统一工程规范,AI 助手与研发各自发挥导致返工风险。建立
总纲 + 细则两层结构:AGENTS.md 只写硬约束与索引(14 条红线、技术栈
基线、仓库结构、命令契约),细则拆为 10 份文档覆盖 Git、代码风格、
格式化、API 与路由、数据库、测试与 CI、Review、日志、密钥、资产命名。

影响面:仅文档。约束力从本轮起对新建代码生效;与现状冲突处以文档为准。
回滚方式:git revert 本提交。
2026-11-02 15:09:51 +08:00

122 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 05 · 数据库规范
| 项 | 内容 |
| --- | --- |
| 文档版本 | V1.0(2026-11-02) |
| 依据 | 技术架构 §5(数据模型)、§8.3 / §8.4(ADR) |
| 适用范围 | `prisma/`、所有 SQL、数据迁移脚本 |
| 强制级别 | 强制 |
---
## 1 命名与主键
| 对象 | 规则 | 示例 |
| --- | --- | --- |
| 表 | 复数 snake_case | `media_tasks`、`brief_card_index` |
| 列 | snake_case | `customer_id`、`created_at` |
| 主键 | `id`,**应用侧生成 ULID**,`char(26)` | `01JA8Z...` |
| 外键 | `<单数实体>_id` | `script_id`、`customer_id` |
| 布尔 | `is_` / `has_` 前缀 | `is_archived` |
| 时间 | `_at` 后缀,`timestamptz` | `reviewed_at` |
| 金额 / 成本 | `_cost`、`_amount`,`numeric(18,6)` | `unit_cost` |
| JSON | `_json` 或语义名(`params`、`snapshot`) | `output_params` |
| 索引 | `idx_<表>_<列>`;唯一 `uq_*`;部分 `idx_*_partial` | `idx_media_tasks_customer_created` |
- `DB-01` **禁止库内生成主键**(不用 `gen_random_uuid()` / `uuidv7()`);主键统一由应用生成 ULID,便于排序与分片。
- `DB-02` 每张业务表必须含 `created_at`、`updated_at`;需要软删的加 `deleted_at`。
- `DB-03` Prisma 模型用 PascalCase + `@@map` / `@map` 映射到 snake_case,**禁止**直接在库里用 camelCase 列名。
---
## 2 客户隔离(最高优先级)
- `DB-04` 所有业务表 `customer_id NOT NULL` + 外键;同时冗余 `industry_id` 便于按行业聚合。
- `DB-05` Repository 方法必须接收 `ScopeContext` 并在 `where` 中注入 `customer_id`;**禁止**「先查全量再在内存过滤」(PRD R-04)。
- `DB-06` 唯一约束必须包含 `customer_id`(如客户内脚本编号唯一),跨客户唯一只允许编码类字段。
- `DB-07` 跨客户写操作(跨客户复用授权)必须走显式授权表并写审计,**禁止**在业务 SQL 里放宽 `customer_id`。
- `DB-08` P1 起启用 PG 行级安全(RLS)兜底;启用前必须在 staging 验证「范围外查询返回 0 行」。
---
## 3 字段与类型
| 场景 | 规则 |
| --- | --- |
| 枚举 | 优先字典表 + 数据行;表内保存 `*_snapshot`(名称快照),避免字典变更影响历史 |
| Prisma enum | 仅用于**永不扩展**的封闭集合,且需迁移评审说明 |
| 动态字段 | JSONB + `schema_version`;可检索字段物化到 `brief_card_index`(D-03) |
| 大文本 | `text`;禁止 `varchar(255)` 装富文本 |
| 时长 | 毫秒整数(`duration_ms`),禁止小数秒 |
| 比例 | `numeric(6,4)`;禁止 `float` 存比率 |
| 标签 | 关联表或 `text[]`(仅查询简单场景);**禁止** JSONB 存关联 ID(PRD R-25) |
| 文件 | 只存对象存储 key(`storage_key`)+ 元数据,**禁止存二进制或外链签名 URL** |
| 密码 / 密钥 | 只存哈希 / 密文;AI 供应商密钥用 AES-256-GCM 加密列 |
- `DB-09` JSONB 上默认不建 GIN 索引;仅在确有按字段查询且 `EXPLAIN` 证明需要时建。
- `DB-10` 禁止 `SELECT *` 返回含大 JSONB 的列表;查询必须显式 `select`。
---
## 4 索引与查询
- `DB-11` 所有外键列建索引。
- `DB-12` 列表页统一复合索引:`(customer_id, created_at DESC, id DESC)`;游标分页基于 `(created_at, id)`(技术架构 §5.7)。
- `DB-13` 唯一约束配合软删用**部分索引**:`WHERE deleted_at IS NULL`。
- `DB-14` 禁止无条件的 `COUNT(*)` 大表统计;计数走预聚合表或 `EXPLAIN` 验证代价。
- `DB-15` 禁止 OFFSET 深翻页;分页一律游标。
- `DB-16` 禁止 N+1:列表查询用 `include`/`select` 显式取关联,或批量 `IN` 查询。
- `DB-17` 批量写入单批 ≤ 1000 行,超出分片;`createMany` 需注意不触发单条钩子。
- `DB-18` 慢查询(> 200ms)必须记录到日志并带 `trace_id`,纳入每周巡检。
---
## 5 迁移(Prisma Migrate)
- `DB-19` 迁移文件命名 `YYYYMMDDHHmmss_<动词>_<对象>`,如 `20261102153000_add_material_specs`。
- `DB-20` **禁止修改已合并的迁移文件**;修正用新迁移。
- `DB-21` 采用 **expand → migrate → contract** 三步;**禁止**同一次发布内「删字段 + 改代码」(技术架构 §4.6)。
- `DB-22` 生产迁移必须幂等、可重入;大表加列用可空列或带默认值的在线加列,禁止锁表长事务。
- `DB-23` 迁移前必须备份,并在 staging 演练;回滚脚本随迁移一起提交(`down` 说明)。
- `DB-24` `prisma db push` 仅限本地探索,**禁止**用于任何共享环境。
- `DB-25` 迁移涉及数据回填时,回填脚本独立于 schema 迁移,可重复执行。
---
## 6 事务与并发
- `DB-26` 事务边界在 Service 层;跨表写必须同事务。
- `DB-27` 事务内**禁止外部 IO**(AI 调用、HTTP、对象存储、ffmpeg)。
- `DB-28` 编号 / 序号类唯一值(脚本编号、批次号)必须用**原子取号**(`UPDATE ... RETURNING` 或序列表 + 行锁),禁止「先查后写」。
- `DB-29` 状态迁移用条件更新(`WHERE status = 'pending'`)避免并发覆盖;受影响行数为 0 视为冲突并返回 409。
- `DB-30` 避免长事务(> 5s);批量任务状态更新按批提交。
---
## 7 删除与保留
| 数据 | 策略 |
| --- | --- |
| 业务实体 | 软删 `deleted_at`;被引用的资产**只能停用不能删除**(PRD R-02) |
| 审计 / 账本 / 任务事件 | **append-only**,禁止 UPDATE / DELETE |
| 任务临时产物 | `temp` 桶 30 天自动清理 |
| 导出包 | `exports` 桶 3 天 |
| 归档 | `archive` 桶 ≥ 12 个月 |
| 客户数据删除 | 只按 PRD 约定的保留期与流程执行,**禁止**开发者自行清理 |
- `DB-31` 硬删只允许:临时文件、失败任务产物、测试数据;且必须有清理任务与日志。
- `DB-32` 审计表权限:应用账号仅 `INSERT` + `SELECT`,无 `UPDATE` / `DELETE` 授权。
---
## 8 检查清单
- [ ] 表 / 列 snake_case,主键 ULID,`created_at` / `updated_at` 齐全
- [ ] 业务表有 `customer_id` + 外键 + 冗余 `industry_id`
- [ ] Repository 接收 `ScopeContext`,SQL 带客户维度
- [ ] 列表查询走复合索引 + 游标分页,无 `SELECT *`、无 N+1
- [ ] 迁移 expand/contract,未改历史迁移,staging 已演练
- [ ] 事务内无外部 IO;编号原子取号;状态迁移带条件
- [ ] 审计 append-only;临时产物有清理策略
- [ ] 新增 JSONB 字段有 `schema_version` 与检索策略