Files
gongxue-base/docs/refactor/pocketbase-real-data-migration-runbook.md

251 lines
9.4 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.

# PocketBase 真实数据迁移验收 Runbook
更新时间2026-06-30
这份文档用于把旧 PocketBase 生产数据迁移到新的 Supabase/PostgreSQL 多租户题库 SaaS。目标不是简单把 JSON 塞进新库,而是证明旧系统的用户、题库、订单、权益、学习数据和运营配置进入新模型后仍能支撑业务上线。
## 适用范围
本流程适用于正式切换前的迁移演练、预生产验收和最终切换。旧 PocketBase/React 项目只作为导出来源和功能参照,新系统以 `apps/api``apps/worker``supabase/migrations``apps/taro` 为准。
迁移覆盖:
- 用户、租户成员、学生资料、身份映射。
- 地区、入口、分类、科目、题目、题目版本、题库集合、练习蓝图。
- 错题、收藏、最近练习、背单词进度和收藏。
- 单词、知识手册、分数线、题目视频、资料资源台账。
- 订单、支付、权益、SVIP 套餐、激活码、优惠券。
- Banner、公告、FAQ、考试日期、勋章、CRM、推广关系和统计数据。
## 迁移原则
- 不直接复用 PocketBase 的不规范字段作为长期模型,全部经过导入器规范化。
-`id` 必须保留到 `legacy_id` 或相关 legacy 字段,方便追溯和二次修复。
- 所有租户内数据必须带 `tenant_id`,不得出现跨租户共享业务行。
- 密码、token、secret、openid、unionid、短信验证码、支付密钥等不得进入 public schema。
- 私有资料、PDF、题图、视频不能变成长效 URL必须进入 `content_assets` 台账并由后端签名。
- 导入前 dry-run 有 blocker 时禁止正式导入;上线切换前 `pb:import:validate` 不能有 FAIL。
## 环境准备
本地或预生产环境需要:
- Node.js 20+
- Docker Desktop
- Supabase CLI
- 已执行最新数据库迁移
建议使用独立迁移验收库,不要混用日常开发 smoke 数据库。迁移演练开始前:
```bash
npm install
npm run supabase:start
npm run supabase:reset
```
如果要验证 API 主链路,可以在真实数据导入后再补充最小管理账号或临时测试账号。不要在真实迁移验收库中先跑 `npm run db:smoke-seed`,否则统计和抽样验收会被模拟数据污染。
## 导出目录
把 PocketBase 导出的集合 JSON 放到仓库根目录:
```text
pb_export/
users.json
regions.json
region_modules.json
module_nodes.json
subjects.json
categories.json
questions.json
orders.json
svip_plans.json
codes.json
code_batches.json
vocabulary_units.json
vocabulary.json
handbook_subjects.json
handbook_chapters.json
handbook_entries.json
scoreline_schools.json
scoreline_majors.json
scoreline_fields.json
scoreline_records.json
video_explanations.json
question_videos.json
```
可以用 `PB_EXPORT_DIR` 指向其他目录:
```powershell
$env:PB_EXPORT_DIR="F:\migration\pb_export_20260630"
npm run pb:import:dry-run
```
## 阶段 1静态 Dry-Run
先运行不写数据库的静态报告:
```bash
npm run pb:import:dry-run
```
生成机器可读报告:
```bash
npm run pb:import:dry-run -- --json > migration-dry-run-report.json
```
严格模式会把 warning 也作为阻断条件,建议预生产验收和 CI 使用:
```bash
npm run pb:import:dry-run -- --json --fail-on-warnings
```
dry-run 会检查:
- 导出目录是否存在。
- JSON 是否可解析,集合是否是数组或 `{ items: [] }` / `{ records: [] }`
- 核心集合是否缺失。
- 旧记录 `id` 是否缺失或重复。
- `docs/pb_schema.json` 中的 relation 是否能解析到导出数据。
- 敏感字段是否出现在旧导出中。
- 未映射集合是否需要补 mapper。
- 用户、题目、订单、SVIP、激活码、单词、手册、分数线、视频等业务数量。
准入标准:
- `blockers = 0`
- 正式切换前建议 `warnings = 0`;如确有历史脏数据,需要记录处理结论、影响范围和接受人。
- `businessCounts` 与旧后台统计口径差异必须能解释。
## 阶段 2正式导入演练
确认目标租户:
```powershell
$env:TENANT_ID="00000000-0000-0000-0000-000000000001"
$env:TENANT_SLUG="master"
$env:TENANT_NAME="工学题库主租户"
```
执行导入:
```bash
npm run pb:import:json
```
默认不会把旧系统密钥值写入新库,只会记录脱敏和问题项。只有在迁移受控密钥到 `app_private.tenant_secrets` 时才允许临时打开:
```powershell
$env:IMPORT_SECRET_VALUES="true"
npm run pb:import:json
Remove-Item Env:\IMPORT_SECRET_VALUES
```
密钥导入后必须立即执行生产就绪检查和人工抽查,确保 public 表不含密钥明文。
## 阶段 3导入后校验
导入完成后运行:
```bash
npm run pb:import:validate
```
正式切换前建议使用 warning 阻断:
```powershell
$env:FAIL_ON_WARNINGS="true"
npm run pb:import:validate
Remove-Item Env:\FAIL_ON_WARNINGS
```
必须通过的关键检查:
- 租户存在。
- 最新一次导入的核心 raw records 已规范化。
- 每道题都有 current version。
- `question_versions.tenant_id` 与题目一致。
- 已支付订单有 payment 行。
- 权益关联到有效用户。
- `platform_users.raw_profile` 不含敏感身份字段。
- `tenant_settings.public_config` 不含密钥。
- `crm_config.secret_ref` 只引用 `app_private.tenant_secrets`
## 阶段 4抽样验收
每次演练都应至少抽样下面数据:
| 范围 | 抽样建议 | 验收点 |
| --- | --- | --- |
| 用户 | 随机 20 个学生、5 个管理员/销售/教师 | 手机号、昵称、角色、禁用状态、地区/院校目标、会员状态 |
| 题库 | 每个地区至少 2 个入口,每个入口抽 2 条路径 | 入口、分类层级、考试意向标记、题目数量 |
| 题目 | 每种题型至少 10 道,含阅读理解/案例分析 | 题干、选项、答案、解析、子题、图片/公式、难度、标签 |
| 练习 | 顺序、随机、全真模拟各 3 次 | 组卷、答题、判分、错题、收藏、报告、复盘 |
| 单词 | 每个地区/科目抽 2 个单元 | 单词、音标、释义、例句、收藏、进度 |
| 知识手册 | 每个手册抽 2 个章节 | Markdown、图片、公式、目录、权限 |
| 分数线 | 每个地区抽 2 所学校、2 个专业 | 年份、动态字段、趋势查询 |
| 视频 | 抽 20 道带视频题 | 权限、次数扣减、签名 URL、水印 traceId、播放日志 |
| 资料 | 抽 PDF/图片各 10 个 | `content_assets` 台账、扫描状态、预览/下载短签名、水印 |
| 订单权益 | 抽 20 个付费订单、20 个激活码 | 订单状态、payment、entitlement、地区/范围、过期时间 |
| 营销 | 抽优惠券、激活码批次、勋章 | 规则、核销记录、发放记录 |
| CRM/推广 | 抽销售、代理、自然流用户 | 首绑保护、推广来源、队列 payload、分佣归因 |
抽样结果建议保存到 `docs/refactor/migration-reports/`。真实数据报告可能包含业务敏感信息提交前必须脱敏如包含用户手机号、订单号、openid、支付流水号不要提交到 Git。
## 阶段 5API 与 Taro 联调验收
迁移数据通过校验后,再启动 API
```bash
npm run dev:api
```
前端联调至少跑通:
- 学生 H5登录、首页、地区选择、题库入口、刷题、错题、收藏、背单词、知识手册、分数线、资料、视频、会员、订单、激活码、个人中心。
- 租户后台 H5数据看板、学生管理、题库内容、导入、公共题库采纳/同步、营销、财务、主题、权限、成员。
- 平台后台 H5租户、套餐、订阅、账单、用量、公共题库授权。
前端只允许通过 `apps/api` 获取业务数据和签名资源。不要在前端直接访问 Supabase 表或拼接对象存储私有 URL。
## 阶段 6最终切换前冻结
正式切换窗口建议:
1. 公告维护窗口。
2. 旧 PocketBase 进入只读或暂停写入。
3. 导出最终 JSON。
4. 执行 strict dry-run。
5. 重置目标生产库或清理目标租户迁移数据。
6. 执行正式导入。
7. 执行 `pb:import:validate` strict 模式。
8. 抽样验收核心链路。
9. 切换域名/API 配置。
10. 保留旧系统只读快照,至少覆盖一个完整售后周期。
## 回滚策略
切换后如果发现阻断级问题:
- 立即停止新系统写入或进入维护模式。
- 保留新库快照和 API 日志,便于定位导入器或业务 API 问题。
- 将域名/API 流量切回旧 PocketBase 只读或旧生产服务。
- 修复 mapper 或数据清洗规则后重新跑 dry-run、导入和抽样验收。
不要在问题未定位时手工批量修改生产表。所有批量修复应沉淀为可重复脚本或 importer mapper 修复,并保留审计记录。
## 上线准出标准
满足以下条件后,才建议进入生产切换:
- `npm run pb:import:dry-run -- --json --fail-on-warnings` 通过,或全部 warning 有签字确认的处理结论。
- `npm run pb:import:validate` 无 FAIL生产切换前 strict 模式无 WARN或 WARN 已确认。
- 核心业务抽样通过,尤其是题目答案解析、会员权益、订单支付、错题收藏、资料视频权限。
- `npm run readiness:production``npm run readiness:production:db` 通过。
- API/Taro 三端核心链路在迁移数据上跑通。
- 对象存储、短信、OAuth、支付、CRM webhook 使用生产 provider 或明确标记为暂不开启。
- 已准备数据库备份、旧系统快照、回滚步骤和负责人。