Files
atlas-seller-kit/docs/architecture/foundation-data-model.md
T

65 lines
7.5 KiB
Markdown
Raw 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.
# 云端基础模块:数据模型与数据库边界
> 状态:设计稿。数据库为 PostgreSQL;基础 DDL 见 [`sql/001_foundation.sql`](./sql/001_foundation.sql),采集箱、商品编辑、发布与在线商品 DDL 见 [`sql/002_product-domain.sql`](./sql/002_product-domain.sql)。本阶段只产出设计和 SQL,不连接、不创建实际数据库。
## 1. 核心边界
```text
账户 User ── Workspace(租户)── Membership
├─ Store ── Store secret / Warehouse / Watermark binding
├─ Watermark template ── versions / uploaded asset
└─ Pricing policy ── Quote / quote options
Platform(全局) ── Platform product category ── Platform commission category mapping
└─ Commission price tiers
Logistics provider(全局) ── Rate card ── Product type × transport method × rate rule
```
- **工作空间即租户**:新账户创建一个工作空间,创建者是 `owner`。用户、工作空间、成员关系分离,以支持未来多人协作和一个用户管理多个工作空间。
- **平台、市场与当前店铺是全局上下文**:店铺属于“工作空间 + 平台”,且一个门店只能绑定一个平台;每个平台最多一个默认店铺。市场/国家、默认语言、支持语言和支持币种属于平台能力,不属于门店。首期 Ozon 使用平台默认市场 `RU`;未来多市场时由顶部全局上下文选择或使用平台默认值。所有门店业务数据均携带 `workspace_id`。
- **两套类目均强绑定平台**:平台商品类目来自 Ozon 等平台的商品分类;平台佣金类目来自该平台的佣金规则树。它们在同一平台内独立维护,并由版本化映射关联;SQL 使用 `platform_id + category_id` 的复合外键阻止跨平台映射,绝不按名称或 ID 猜测。
- **CEL 价表是全局规则数据**:由后台维护版本和生效期;报价只引用确定版本。首期仅 Ozon-rFBS + CEL,香港空运不录入首期规则。
- **报价是快照**:定价方案可以变,CEL/佣金规则可以更新,但已确认 SKU 报价始终保留当时输入、费率、规则版本和费用拆分。
## 2. 关键表的产品映射
| 领域 | 表 | 说明 |
|---|---|---|
| 账户与租户 | `users`、`workspaces`、`workspace_memberships`、`user_sessions` | 邮箱密码登录;首期不校验邮箱,`email_verified_at` 为后续验证预留。手机号、短信验证和风控后续以独立 migration 扩展。 |
| 可靠写入与异步投递 | `idempotency_requests`、`outbox_events`、`operations` | 关键写操作按幂等键安全重放;异步业务事务同时写入 Outbox,由 Dispatcher 补投 BullMQ。`operations` 是用户可见的任务状态,不以 Redis 队列作为唯一事实。 |
| 平台与门店 | `platforms`、`currencies`、`platform_currency_support`、`stores`、`store_secrets`、`store_warehouses` | 平台维护支持币种;门店只绑定一个平台,并保存默认计价币种和连接凭据。市场/国家与语言属于待补充的平台能力配置;密钥过期时间、商品限额无数据时使用 `NULL`,界面显示 `—`。 |
| 水印与素材 | `media_assets`、`media_upload_intents`、`watermark_fonts`、`watermark_templates`、`watermark_template_versions`、`store_watermark_bindings` | 素材保存 `storage_provider + bucket + object_key` 及元数据,不保存长期访问 URL;浏览器直传先建立持久化上传意图,再校验并落为素材。店铺绑定一个当前模板,发布任务未来固化模板版本,而不修改原图。 |
| 类目佣金 | `platform_product_categories`、`commission_categories`、`commission_category_rules`、`platform_product_category_commission_mappings` | 平台商品类目和平台佣金类目都强绑定同一平台;佣金按销售价 RUB 阶梯计费,抓取数据以 `competitor_capture` 来源和待审核版本录入。 |
| 跨境物流 | `logistics_providers`、`logistics_rate_cards`、`logistics_product_types`、`logistics_transport_methods`、`logistics_rate_rules` | CEL 产品类型、运输方式、货值/重量/尺寸/抛重规则、费率和版本分开保存。 |
| 定价 | `pricing_policies`、`pricing_policy_transport_methods`、`pricing_quotes`、`pricing_quote_options` | 店铺默认方案、SKU 单次临时选择、所有候选运输方式结果与最终确认结果。方案可明确控制“缺少正式佣金规则时是否允许本次临时覆盖”。 |
| 汇率 | `exchange_rates` | 全局不可变汇率历史;首期系统汇率为 CBR 的 CNY→RUB 官方参考汇率。固定汇率和本次覆盖仅固化到报价快照。 |
## 3. 必须由服务端保证的规则
1. 每个请求事务先验证当前用户的工作空间成员身份,再执行 `SET LOCAL app.workspace_id = '<uuid>'`;PostgreSQL RLS 是第二道隔离,不能替代业务授权。
2. API 和 Worker 必须使用无表所有权、无 `BYPASSRLS` 属性的专用应用登录角色;迁移只能由表所有者(迁移角色)执行。应用绝不能连接为建表/迁移角色。
3. 所有带 `workspace_id` 的业务表同时 `ENABLE` 和 `FORCE ROW LEVEL SECURITY`。这使表所有者的普通数据访问也受策略约束;PostgreSQL 超级用户及显式拥有 `BYPASSRLS` 的角色仍可绕过,不能用于应用连接。
4. `store_secrets.secret_ciphertext` 只保存信封加密后的密文、nonce 和密钥版本。不得写日志、返回 API 响应或发往浏览器缓存。
5. 一个工作空间在同一平台只有一个活动默认店铺;切换默认店铺必须在同一事务内完成。
6. 定价引擎枚举“佣金档位 × CEL 产品类型 × 运输方式”,回验最终销售价的 RUB 佣金档和 CNY 商品货值档;不能沿用旧项目的固定 12% / 18% 佣金。
7. `pricing_quotes.product_variant_id` 暂为逻辑外键:商品/SKU DDL 落地时会补为指向 `product_variants` 的真实外键。报价本身可在没有 SKU 的“即时试算”中存在。
8. 关键可重试写操作使用 `idempotency_requests`;启动异步工作的业务事务同时写入 `outbox_events`,由 Dispatcher 至少一次投递到 BullMQ。完整规则见 [`transactional-outbox-and-idempotency.md`](./transactional-outbox-and-idempotency.md)。
## 4. 初始数据与迁移顺序
1. 执行 `001_foundation.sql`。
2. 初始化 `OZON` 平台、`CNY` / `RUB` 币种和平台币种支持关系。
3. 建立 Ozon 平台商品类目和 Ozon 平台佣金类目树,再人工审核并导入同平台映射与阶梯佣金规则。
4. 配置 CBR CNY→RUB 汇率 Worker,成功写入第一条 `exchange_rates` 后才启用“系统汇率”报价;详见 [`exchange-rates.md`](./exchange-rates.md)。
5. 将已 Review 的 CEL Ozon-rFBS 规则录入一个 `draft` rate card,用真实 CEL 报价复核后发布为 `active`。
6. 执行 `002_product-domain.sql`,它会补上 `pricing_quotes.product_variant_id` 的真实外键和 `product_platform_profile_id` 关联。
7. 启动 Outbox Dispatcher 与过期幂等记录/已投递事件的清理 Worker;在 staging 按文档模拟投递故障并验证可恢复性。
## 5. 仍不在此 DDL 内的内容
- 短信发送、验证码、风控供应商和密码找回流程;
- Ozon API 的实际同步 Worker、密钥轮换 Worker 和对象存储上传;
- 商品编辑和在线商品页面的具体列表字段、筛选与交互(表结构已预留,需求仍可继续细化);
- 平台/物流/佣金规则的后台管理 UI 与导入程序。