forked from wangziqi/gongxue-base
114 lines
4.4 KiB
Markdown
114 lines
4.4 KiB
Markdown
# 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
|
||
```
|
||
|
||
先执行不写数据库的 dry-run:
|
||
|
||
```bash
|
||
npm run import:dry-run
|
||
```
|
||
|
||
根目录也可以执行:
|
||
|
||
```bash
|
||
npm run pb:import:dry-run
|
||
```
|
||
|
||
dry-run 会检查:
|
||
|
||
- 导出目录和 JSON 文件格式。
|
||
- 核心集合是否缺失。
|
||
- 旧记录 `id` 是否缺失或重复。
|
||
- 旧 `pb_schema.json` 中的 relation 是否能在导出数据里找到目标记录。
|
||
- `settings`、`crm_config`、用户资料等位置是否存在敏感字段。
|
||
- 哪些集合当前已有规范化 mapper,哪些集合仍未映射。
|
||
|
||
存在 `blocker` 时不要执行正式导入;先修导出、补 mapper 或记录业务处理结论。
|
||
|
||
执行正式导入:
|
||
|
||
```bash
|
||
npm run import:json
|
||
```
|
||
|
||
导入后执行验证:
|
||
|
||
```bash
|
||
npm run import:validate
|
||
```
|
||
|
||
根目录也可以执行:
|
||
|
||
```bash
|
||
npm run pb:import:dry-run
|
||
npm run pb:import:json
|
||
npm run pb:import:validate
|
||
```
|
||
|
||
导入器会先把所有 JSON 放入 `pb_raw_records`,并默认对敏感字段脱敏;然后按依赖顺序标准化导入业务表。上线前必须处理 `FAIL` 项;`WARN` 项通常表示旧数据关系缺失,例如旧题目引用了不存在的章节,需要业务确认是否可接受。
|