# `@drama/db` 表关系设计纪律 > 本文件兑现任务文档 §2 验收标准 6:列出每张业务表到 `users` 表的外键可达路径,证明「user 级联可整体删除 / 可导出」的口子(架构基线 §10.1「数据可删除」)在一期就以**纯纪律**留好——二期实现 `cascadeDeleteUser(db, userId)` / `exportUser(db, userId)` 时无需穿透改表结构。 ## 根锚:`users` `users` 是全库唯一的级联根。本包 `src/db/tables/users.cjs` 提供这一张跨域共享的根表。其余业务表由各领域包(`@drama/auth` / `@drama/account` / `@drama/billing` / `@drama/payment` / `@drama/email` / ...)自己的 `src/db/tables/*` 提供。 ## 硬约束(领域包建表时必须遵守) 每张业务表必须满足下列其一,保证到 `users.id` 有可遍历的外键路径: 1. **直接挂**:表含 `user_id BIGINT` 外键,`references users.id`。适用于 user 直属数据(资料、积分账户、订阅、播放历史、收藏、创建的 drama)。 2. **一层可达**:表经父表的外键间接可达 `users`。例如 `episode.drama_id → drama.id`,而 `drama.user_id → users.id`;则 `episode` 经 `drama` 一层可达。 3. **全局共享、无属主**:不挂 user 的纯字典 / 素材池(如官方种子素材、系统枚举)显式标注「无属主」,级联删除时跳过、导出时不含。这类表必须在其领域包 `MODEL.md` 中明示,避免被误判为「漏挂 user_id」。 ## 级联可达路径示意(领域表二期落地后填充) 一期本包只锚定根表,下表是约定示意(实际由各领域包 table definition 落地并在各自文档登记其可达路径): - `users` ← `drama.user_id`(直接) - `drama` ← `episode.drama_id`(一层经 drama 到 users) - `drama` ← `play_record.drama_id` + `play_record.user_id`(直接 + 冗余直挂,便于按 user 整段删) - `users` ← `subscription.user_id` / `credit_account.user_id` / `favorite.user_id`(直接) ## 软删除约定 `deleted_at TIMESTAMP NULL` 为软删除标记,与 `helpers.notDeleted(qb)`(`where deleted_at is null`)配套。需要软删除语义的表统一用此列名,保证 helper 复用。 ## 二期实现留口子(本包不实现,仅此设计支撑) - `cascadeDeleteUser(db, userId)`:从 `users.id` 出发,沿上述外键路径自顶向下删除(或软删)所有归属该 user 的行。 - `exportUser(db, userId)`:同一遍历,收集而非删除,产出可导出的 user 数据快照(GDPR/CCPA 数据导出)。 只要领域包建表时遵守上述硬约束,这两个遍历器二期可纯靠外键元数据驱动实现,无需回头改表结构。