# 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 先运行不写数据库的静态报告。默认是 `development` profile,适合开发环境快速发现 JSON 形态、关系和敏感字段问题: ```bash npm run pb:import:dry-run ``` 生成机器可读报告: ```bash npm run pb:import:dry-run -- --json > migration-dry-run-report.json ``` 预生产验收、最终切换和 CI 必须使用 `production` profile,并建议同时打开 warning 阻断: ```bash npm run pb:import:dry-run -- --profile=production --json --fail-on-warnings ``` 也可以用环境变量指定 profile: ```powershell $env:PB_DRY_RUN_PROFILE="production" npm run pb:import:dry-run -- --json --fail-on-warnings Remove-Item Env:\PB_DRY_RUN_PROFILE ``` `--profile` 只接受 `development` 或 `production`,拼写错误会 fail closed 并返回 blocker,避免正式迁移时误用开发模式。 dry-run 会检查: - 导出目录是否存在。 - JSON 是否可解析,集合是否是数组或 `{ items: [] }` / `{ records: [] }`。 - 核心集合是否缺失。 - 旧记录 `id` 是否缺失或重复。 - `docs/pb_schema.json` 中的 relation 是否能解析到导出数据。 - 敏感字段是否出现在旧导出中。 - 未映射集合是否需要补 mapper。 - 用户、题目、订单、SVIP、激活码、单词、手册、分数线、视频等业务数量。 - `production` profile 会额外检查生产迁移必需集合:`users`、`questions`、`subjects`、`categories`、`orders`、`svip_plans`、`codes`、`vocabulary_units`、`vocabulary`、`handbook_subjects`、`handbook_chapters`、`handbook_entries`。 - `migrationReadiness.criticalFieldCoverage` 会统计关键字段覆盖率,例如 `users.phone`、`questions.subjectId/categoryId/content`、`orders.userId/planId/status`、`codes.code`、单词和手册的归属字段;生产模式下低于阈值会变成 blocker。 准入标准: - `blockers = 0`。 - 正式切换前建议 `warnings = 0`;如确有历史脏数据,需要记录处理结论、影响范围和接受人。 - `businessCounts` 与旧后台统计口径差异必须能解释。 - 生产切换前 `migrationProfile` 必须是 `production`,且 `migrationReadiness.requiredCollections` 不能有缺失或记录数不足。 ## 阶段 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。 ## 阶段 5:API 与 Taro 联调验收 迁移数据通过校验后,再启动 API: ```bash npm run dev:api ``` 前端联调至少跑通: - 学生 H5:登录、首页、地区选择、题库入口、刷题、错题、收藏、背单词、知识手册、分数线、资料、视频、会员、订单、激活码、个人中心。 - 租户后台 H5:数据看板、学生管理、题库内容、导入、公共题库采纳/同步、营销、财务、主题、权限、成员。 - 平台后台 H5:租户、套餐、订阅、账单、用量、公共题库授权。 前端只允许通过 `apps/api` 获取业务数据和签名资源。不要在前端直接访问 Supabase 表或拼接对象存储私有 URL。 ## 阶段 6:最终切换前冻结 正式切换窗口建议: 1. 公告维护窗口。 2. 旧 PocketBase 进入只读或暂停写入。 3. 导出最终 JSON。 4. 执行 production 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 -- --profile=production --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 或明确标记为暂不开启。 - 已准备数据库备份、旧系统快照、回滚步骤和负责人。