docs(plan): add backup/rollback section, fix backfill run channel and sink semantics

This commit is contained in:
yeuimu
2026-09-02 09:36:17 +08:00
parent 6973f863b5
commit 3d431f8375
+41 -8
View File
@@ -24,7 +24,10 @@
- 所有 public 接口入参、出参 DTO 字段**一个都不能变**,只改内部逻辑 - 所有 public 接口入参、出参 DTO 字段**一个都不能变**,只改内部逻辑
- 已有测试必须全部通过,不允许跳过 - 已有测试必须全部通过,不允许跳过
- 共享开发库(deploy-v2-postgres-1):禁止 reset;迁移用「手写 SQL + db execute + migrate resolve」,先跑 `prisma migrate status` - 共享开发库(deploy-v2-postgres-1):禁止 reset;迁移用「手写 SQL + db execute + migrate resolve」,先跑 `prisma migrate status`
- 回滚保障`backups/inkreach_snapshot_20260901_185233.dump`(全库快照已做);代码回滚 = 删 feature 分支 - 回滚保障(均已就绪,见文末「备份与回滚」):
- DB 全库快照:`backups/inkreach_snapshot_20260901_185233.dump`pg_dump -Fc
- 运行容器快照:镜像 `inkreach-api-snapshot:20260901`docker commit 自 deploy-v2-api-1,含当前代码与依赖)
- 代码回滚 = 删 feature 分支
**执行环境(宿主机无 node,统一用容器跑):** **执行环境(宿主机无 node,统一用容器跑):**
@@ -38,9 +41,11 @@ docker run --rm --network deploy-v2_default \
**排序键定义(getGoods DEFAULT):** **排序键定义(getGoods DEFAULT):**
``` ```
① countries.sort_order(经 goods.country_id 缺失 → 排最后 ① countries.sort_order(经 goods.country_id 缺失 → MAX_SAFE_INTEGER
② 叶子.parent_category_id 的 sort_order(二级) 缺失 → 排最后 ② 叶子.parent_category_id 的 sort_order(二级) 缺失 → MAX_SAFE_INTEGER
③ 叶子自身 sort_order(三级,经 origin_goods.sds_category_id)缺失 → 排最后 ③ 叶子自身 sort_order(三级,经 origin_goods.sds_category_id)缺失 → MAX_SAFE_INTEGER
(缺失沉底发生在「所属国家分组内」:如防晒衣(137) 的 sds_category_id=2393 不在新树,
它会排到日本组末尾,不会破坏国家间顺序,可照常售卖/搜索)
④ good_priority desc, id asc(现有字段,不变) ④ good_priority desc, id asc(现有字段,不变)
``` ```
@@ -187,7 +192,7 @@ const norm = (s: string) => s.trim().replace(/\s+/g, '');
const codeOf = (s: string) => (s.trim().match(/^[A-Za-z0-9]+/) ?? [''])[0]; const codeOf = (s: string) => (s.trim().match(/^[A-Za-z0-9]+/) ?? [''])[0];
async function main() { async function main() {
const raw = readFileSync('/opt/inkreach-v2/排序表.md', 'utf8'); const raw = readFileSync(process.env.SORT_TABLE_PATH ?? '/repo/排序表.md', 'utf8');
let country: string | null = null; let country: string | null = null;
let l2: string | null = null; let l2: string | null = null;
const tree: Array<{ country: string; l2: string; l3: string }> = []; const tree: Array<{ country: string; l2: string; l3: string }> = [];
@@ -285,10 +290,13 @@ main().finally(() => prisma.$disconnect());
- [ ] **Step 2.2: 执行脚本** - [ ] **Step 2.2: 执行脚本**
```bash ```bash
# ts-node 不在 apps/api 依赖中,用 tsc 编译后以 node 运行;
# 仓库根只读挂载到 /repo 以读取排序表.md
docker run --rm --network deploy-v2_default \ docker run --rm --network deploy-v2_default \
-e DATABASE_URL='postgresql://inkreach:2628adbdf875727ae1b5556b08cb00452bb4e1f80490f6b6@postgres:5432/inkreach' \ -e DATABASE_URL='postgresql://inkreach:2628adbdf875727ae1b5556b08cb00452bb4e1f80490f6b6@postgres:5432/inkreach' \
-v /opt/inkreach-v2/apps/api:/app -w /app node:20-alpine \ -e SORT_TABLE_PATH=/repo/排序表.md \
npx ts-node prisma/backfill-category-sort-order.ts -v /opt/inkreach-v2:/repo:ro -v /opt/inkreach-v2/apps/api:/app -w /app node:20-alpine \
sh -c "npx tsc prisma/backfill-category-sort-order.ts --module commonjs --target es2020 --esModuleInterop --skipLibCheck --outDir /tmp/bf && node /tmp/bf/backfill-category-sort-order.js"
``` ```
Expected: `backfill done`,退出码 0。已知候选 UNMATCHED:美国/内衣 `DG170G170G女士无痕三角内裤`(库内为 `DG701 170G女士无痕三角内裤`,货号前缀 `DG170G170G` 不匹配)→ 将排序表.md 该行改为与库名一致后重跑;其余逐条人工核对。 Expected: `backfill done`,退出码 0。已知候选 UNMATCHED:美国/内衣 `DG170G170G女士无痕三角内裤`(库内为 `DG701 170G女士无痕三角内裤`,货号前缀 `DG170G170G` 不匹配)→ 将排序表.md 该行改为与库名一致后重跑;其余逐条人工核对。
@@ -693,7 +701,7 @@ Expected: 全部 PASS。任何失败必须修复(含其他模块被 schema 变
- [ ] `prisma migrate status` 干净(无未应用/未登记迁移) - [ ] `prisma migrate status` 干净(无未应用/未登记迁移)
- [ ] 回填 UNMATCHED 清单为空(或已逐条人工处理并记录) - [ ] 回填 UNMATCHED 清单为空(或已逐条人工处理并记录)
- [ ] 冒烟(真实调用形态):`/public/goods?pageSize=10` DEFAULT = 美国 DG001 族 → DG004 族 …;`/public/goods?categoryId=659`(女士服装,跨国)按国家顺序排;`/public/goods?categoryId=666` 同上;`/public/goods?countryId=49&categoryId=658` 只返回英国男装且按 ②③④;`/public/categories` 返回新树且沙特(空)不出现 - [ ] (先完成 Step 6.4 重建容器)冒烟(真实调用形态):`/public/goods?pageSize=10` DEFAULT = 美国 DG001 族 → DG004 族 …;`/public/goods?categoryId=659`(女士服装,跨国)按国家顺序排;`/public/goods?categoryId=666` 同上;`/public/goods?countryId=49&categoryId=658` 只返回英国男装且按 ②③④;`/public/categories` 返回新树且沙特(空)不出现
- [ ] DTO 字段逐个对比改动前后(PublicGoodDto / PublicCategoryNodeDto / 分页结构)无增删 - [ ] DTO 字段逐个对比改动前后(PublicGoodDto / PublicCategoryNodeDto / 分页结构)无增删
- [ ] `git status` 干净,全部提交 - [ ] `git status` 干净,全部提交
@@ -725,3 +733,28 @@ cd /opt/inkreach-v2/deploy && docker compose -f docker-compose.v2.yml up -d --bu
- 防晒衣(good_id=137) 的 sds_category_id=2393 修复(排序中沉底) - 防晒衣(good_id=137) 的 sds_category_id=2393 修复(排序中沉底)
- admin 后台分类拖拽排序 UI(本期用脚本回填;后续如需可视化调整再立项) - admin 后台分类拖拽排序 UI(本期用脚本回填;后续如需可视化调整再立项)
- 中东根更名后 SDS 同步覆盖名称的持久对抗(重跑回填脚本即可恢复) - 中东根更名后 SDS 同步覆盖名称的持久对抗(重跑回填脚本即可恢复)
---
## 备份与回滚(已就绪)
| 资产 | 位置 | 时间 |
|---|---|---|
| DB 全库快照 | `backups/inkreach_snapshot_20260901_185233.dump`pg_dump -Fc3.4M | 2026-09-01 |
| 运行容器快照 | docker 镜像 `inkreach-api-snapshot:20260901`711MBcommit 自 deploy-v2-api-1 | 2026-09-01 |
| 代码 | 分支 `feature/category-tree-sort`develop/refactor-v2 未动) | 实时 |
**DB 恢复(覆盖式,先停 api 容器避免写入竞争):**
```bash
cd /opt/inkreach-v2/deploy && docker compose -f docker-compose.v2.yml stop api
docker exec -i deploy-v2-postgres-1 pg_restore -U inkreach -d inkreach --clean --if-exists \
< /opt/inkreach-v2/backups/inkreach_snapshot_20260901_185233.dump
cd /opt/inkreach-v2/deploy && docker compose -f docker-compose.v2.yml start api
```
**api 容器恢复(快照镜像另起实例做比对/应急):**
```bash
docker run -d --name deploy-v2-api-snapshot --network deploy-v2_default inkreach-api-snapshot:20260901
```