feat: scaffold supabase multi-tenant backend

This commit is contained in:
Codex
2026-06-21 21:54:43 +08:00
commit c9c767c7bd
99 changed files with 36660 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
# PocketBase 到 Supabase 迁移映射
## 迁移原则
- 保留 PocketBase 旧 ID 到 `legacy_id`,新系统主键统一用 UUID。
- 所有租户内业务表都带 `tenant_id`
- 旧表原始 JSON 进入 `pb_raw_records` 前默认脱敏,保证可追溯但不泄露密钥。
- 旧集合里不符合商用规范的字段不得直接映射到正式表,必须经过清洗和拆表。
- 第一阶段标准化导入覆盖核心商用链路租户、用户、题库目录、题目、订单、支付、权益、激活码、优惠券、词库、手册、运营内容、分数线、视频解析、CRM 和推广关系。未覆盖集合先 raw import并在 `pb_import_issues` 里记录风险。
- `users.isSvip``svipExpiry``svipRegions` 最终迁移为 `entitlements`
- `users.stats.favorites``wrongBook` 最终迁移为 `favorite_questions``wrong_questions`
- `settings` 中的密钥类配置必须进入 `app_private.tenant_secrets` 或外部 Vault不进入 `public` schema。
## 核心映射
| PocketBase | PostgreSQL | 说明 |
| --- | --- | --- |
| `tenant_config` | `tenants` / `tenant_branding` / `tenant_settings` | 从单实例配置升级为平台租户配置 |
| `users` | `platform_users` / `tenant_memberships` / `student_profiles` / `user_identities` | 用户身份、租户角色、学生资料拆分 |
| `regions` | `regions` | 增加 `tenant_id` |
| `region_modules` | `region_modules` | 增加 `tenant_id` |
| `module_nodes` | `module_nodes` | 作为后续题库层级主结构 |
| `subjects` | `subjects` | 保留旧结构兼容,逐步和 `module_nodes` 对齐 |
| `categories` | `categories` | 保留旧结构兼容 |
| `questions` | `questions` / `question_versions` | 题目实体和题目内容版本拆分 |
| `orders` | `orders` / `order_items` / `payments` / `payment_events` | 订单与支付流水分离 |
| `svip_plans` | `svip_plans` | 金额改为分 |
| `codes` | `activation_codes` | 激活码表 |
| `code_batches` | `code_batches` | 批次表 |
| `coupons` | `coupons` | 优惠券 |
| `coupon_redemptions` | `coupon_redemptions` | 兑换流水 |
| `vocabulary_units` | `vocabulary_units` | 背单词单元 |
| `vocabulary` | `vocabulary_words` | 单词表 |
| `handbook_*` | `handbook_*` | 手册内容 |
| `banners` / `faqs` / `announcements` | 同名表 | 运营内容 |
| `settings` | `tenant_settings` / `tenant_payment_accounts` | 拆分公开配置、私密配置、支付配置 |
| `crm_config.secret` | `app_private.tenant_secrets` | 公共表只保留 `secret_ref` |
| `users.stats.favorites` | `favorite_questions` | 迁移为关系表 |
| `users.stats.wrongBook` | `wrong_questions` | 迁移为关系表 |
| `users.isSvip` / `svipExpiry` / `svipRegions` | `entitlements` | 迁移为租户/地区范围权益 |
| `user_word_progress` / `user_word_favorites` | 同名规范表 | 关联到 `platform_users``vocabulary_words` |
| `app_assets` / `images` | `content_assets` | 统一素材索引 |
## 导入命令
先安装导入器依赖:
```bash
cd scripts/import-pocketbase
copy .env.example .env
npm install
```
查看 schema 摘要:
```bash
npm run schema:summary
npm run schema:risk
```
把 PocketBase 导出的集合 JSON 放到 `pb_export`
```text
pb_export/
users.json
regions.json
questions.json
```
执行导入:
```bash
npm run import:json
```
导入后执行验证:
```bash
npm run import:validate
```
根目录也可以执行:
```bash
npm run pb:import:json
npm run pb:import:validate
```
导入器会先把所有 JSON 放入 `pb_raw_records`,并默认对敏感字段脱敏;然后按依赖顺序标准化导入业务表。上线前必须处理 `FAIL` 项;`WARN` 项通常表示旧数据关系缺失,例如旧题目引用了不存在的章节,需要业务确认是否可接受。