项目尚无统一工程规范,AI 助手与研发各自发挥导致返工风险。建立 总纲 + 细则两层结构:AGENTS.md 只写硬约束与索引(14 条红线、技术栈 基线、仓库结构、命令契约),细则拆为 10 份文档覆盖 Git、代码风格、 格式化、API 与路由、数据库、测试与 CI、Review、日志、密钥、资产命名。 影响面:仅文档。约束力从本轮起对新建代码生效;与现状冲突处以文档为准。 回滚方式:git revert 本提交。
6.4 KiB
6.4 KiB
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-03Prisma 模型用 PascalCase +@@map/@map映射到 snake_case,禁止直接在库里用 camelCase 列名。
2 客户隔离(最高优先级)
DB-04所有业务表customer_id NOT NULL+ 外键;同时冗余industry_id便于按行业聚合。DB-05Repository 方法必须接收ScopeContext并在where中注入customer_id;禁止「先查全量再在内存过滤」(PRD R-04)。DB-06唯一约束必须包含customer_id(如客户内脚本编号唯一),跨客户唯一只允许编码类字段。DB-07跨客户写操作(跨客户复用授权)必须走显式授权表并写审计,禁止在业务 SQL 里放宽customer_id。DB-08P1 起启用 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-09JSONB 上默认不建 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-24prisma 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与检索策略