# 迁移历史 squash 运行手册（issue #213）

> 风险等级：中（只动 `_prisma_migrations` 元数据，不动业务表）
> 预计窗口：30-45 分钟
> 必须用户在场：是（PRD 步骤）

## 目标

把 `_prisma_migrations` 表里的 68 条历史迁移记录替换为 1 条 `00000000000000_baseline_squash` 记录，让 `prisma migrate deploy` 在空 DB 上一次到位。

## 前置条件

- [ ] 本 PR（chore/migration-squash-baseline）的 baseline 经 `prisma migrate diff --from-url <fresh DB> --to-schema-datamodel prisma/schema` 验证过等价
- [ ] checksum = `f62f54815f8c4dd8ac5c7309eebb0fc36d17ccf8739188a3dac0ed9248309307`（已写入 `apply-squash.sql`）
- [ ] 4 个环境（dev/UAT/staging/PRD）当前 `_prisma_migrations` 表都恰有 68 条记录

## 阶段 2.1 — Merge freeze + 备份（用户做）

```bash
# 1. 群里公告：DB squash 进行中，30 分钟，禁止合并任何含 prisma/ 改动的 PR
# 2. 临时把 develop 保护规则里的 required approvals 提一档（可选，软提示）

# 3. 同时打开 UAT + PRD 的 SSH（两个窗口）
ssh ffoa-uat
ssh ffoa-prd

# 4. 各自备份 _prisma_migrations 表
TS=$(date +%Y%m%d-%H%M)
docker exec ffoa-uat-postgres pg_dump -U <user> -d <db> -t _prisma_migrations \
  > ~/backup-prisma-mig-uat-$TS.sql
docker exec ffoa-prd-postgres pg_dump -U <user> -d <db> -t _prisma_migrations \
  > ~/backup-prisma-mig-prd-$TS.sql

# 5. 验证两份备份大小 > 0 且条数 = 68
grep -c "INSERT INTO" ~/backup-prisma-mig-uat-$TS.sql
grep -c "INSERT INTO" ~/backup-prisma-mig-prd-$TS.sql

# staging 同上（可顺手备份）
```

## 阶段 2.2 — 推 PR + 合并（AI 推 PR / 用户合）

```bash
git push -u origin chore/migration-squash-baseline
# 创建 PR，CI 跑 build / contract / migration-count（已验证只新增 1 个 migration.sql，CI 通过）
```

CI 全过后由**非 PR 作者**在 Gitea web UI 点 Approve → Merge（squash）。
（PR 作者不能批/合自己的 PR；详 `CLAUDE.md`「PR 合并规则」段。）

## 阶段 2.3 — dev DB 升级（AI 做，5 分钟）

```bash
docker exec -i $DEV_DB_CONTAINER psql -U $DEV_DB_USER -d $DEV_DB_NAME \
  -v ON_ERROR_STOP=1 < scripts/ops/squash/apply-squash.sql

# 验证
cd backend && DATABASE_URL=$DEV_DATABASE_URL npx prisma migrate status
# 应输出 "Database schema is up to date!" 且只有 1 条已应用迁移
```

## 阶段 2.4 — UAT + PRD 并行（用户做，5 分钟）

**两个窗口同时执行**：

```bash
# UAT 窗口
docker exec -i ffoa-uat-postgres psql -U <user> -d <db> \
  -v ON_ERROR_STOP=1 < scripts/ops/squash/apply-squash.sql

# PRD 窗口（同时；CLAUDE.md 铁律 PRD 必须用户亲自跑）
docker exec -i ffoa-prd-postgres psql -U <user> -d <db> \
  -v ON_ERROR_STOP=1 < scripts/ops/squash/apply-squash.sql
```

`apply-squash.sql` 内置三道安全网：

1. 若 `baseline_squash` 已存在 → 抛错，避免重复
2. 若历史不是 68 条 → 抛错，环境状态意外
3. 完成后再 count，必须 = 1

任一失败整个 BEGIN/COMMIT 会回滚，不会留下半成品。

## 阶段 2.5 — staging（用户做，30 秒）

同上同一份 SQL。

## 阶段 2.6 — 应用层验证

每个环境跑：

```bash
# 1. Prisma 层
cd backend && DATABASE_URL=<env_url> npx prisma migrate status
# 期望：1 migration applied, 0 pending

# 2. 应用层
curl <env_health_endpoint>  # 期望 200

# 3. 抽样写操作（任选一个表，比如读 platform_iam.users）
docker exec <env_pg> psql -U <user> -d <db> -c "SELECT count(*) FROM platform_iam.users;"
```

## 阶段 3 — 善后

- [ ] 解 merge freeze（恢复 Gitea 保护规则）
- [ ] 群里通知"squash 完成"
- [ ] 试一次真实迁移生成（确认未来 PR 流程不破）：
  ```bash
  cd backend
  # 临时给 schema 加一个测试字段
  npx prisma migrate dev --name test_migration_after_squash --create-only
  # 检查生成的 migration.sql 干净 → git checkout 回滚（不入仓）
  ```
- [ ] 删除 issue #213，记录 baseline commit SHA

## 失败回滚

详见 `scripts/ops/squash/rollback.sql` 文件头注释。流程：

```bash
docker exec -i <pg> psql -U <user> -d <db> -v ON_ERROR_STOP=1 -c "BEGIN; DELETE FROM _prisma_migrations; COMMIT;"
docker exec -i <pg> psql -U <user> -d <db> -v ON_ERROR_STOP=1 < ~/backup-prisma-mig-<env>-<ts>.sql

# 然后回滚代码
git revert <squash-PR-merge-commit>
git push origin develop
```

回滚耗时 < 1 分钟。
