feat(deploy): 财务上线数据阶段编排与运维加固

- 新增 deploy/post-deploy.sh:回填 → 业务配置 → 同步 → 验收四阶段编排,支持 --only/--dry-run/窗口参数
- 新增 tools 镜像与 scripts/verify-finance-launch.sql(15 项上线验收)
- 验收口径:成本闸门二选一、要求已过账凭证、备用金支出关联、成本覆盖 WARN
- 加固:参数校验、工具镜像每次刷新、docker run 兜底超时、迁移前缀探测、验收失败退出码 2
- 工具镜像预建 /app/data,消除日志 EACCES
- 回填后与 restore-db 恢复后自动 VACUUM (ANALYZE),修复订单列表分页 COUNT 全表扫描(41 万单实测 27s → 0.17s)
- 文档:41 万单同步用 7 天窗 + 180s 超时;回填/恢复后刷新统计
This commit is contained in:
yml2213
2026-10-28 11:52:42 +08:00
parent 2ddc26091c
commit 08318ef8d9
8 changed files with 977 additions and 8 deletions
+7 -1
View File
@@ -97,7 +97,13 @@ crontab -e
bash deploy/restore-db.sh
```
流程:下载最新备份并强制校验 sha256 → 停 backend/web 并确认已停止 → `pg_restore --clean --if-exists` → 执行迁移补差 → 重启并等待健康检查。任一步失败会尝试恢复原先运行的服务,并明确报告数据库可能处于部分恢复状态。执行前需交互输入 `RESTORE` 确认(非交互终端用 `RESTORE_CONFIRM=1` 或 `--yes`)。
流程:下载最新备份并强制校验 sha256 → 停 backend/web 并确认已停止 → `pg_restore --clean --if-exists` → 执行迁移补差 → `VACUUM (ANALYZE)` 刷新统计信息与可见性 → 重启并等待健康检查。任一步失败会尝试恢复原先运行的服务,并明确报告数据库可能处于部分恢复状态。执行前需交互输入 `RESTORE` 确认(非交互终端用 `RESTORE_CONFIRM=1` 或 `--yes`)。
> 恢复后必须 `VACUUM (ANALYZE)`:dump 不带 `pg_statistic`,没有统计信息时订单列表的 `COUNT` 会被规划成 `orders` 全表扫描,41 万单冷缓存实测 14–27 秒(默认语句超时 15 秒会直接 500)。`restore-db.sh` 已自动执行;手工 `pg_restore` 请补跑:
>
> ```bash
> docker compose exec -T postgres psql -U postgres -d order_site -c 'VACUUM (ANALYZE)'
> ```
如需同时恢复 `apps/backend/data` 配置:
@@ -167,6 +167,15 @@ npm run fulfillment:backfill # 只填 NULL(可选,报表用)
npm run finance:sync -- --mode=all --from=2026-07-10 --to=2026-10-22
```
> 回填完成后必须刷新统计信息,否则订单列表的分页总数会被规划成 `orders` 全表扫描:
>
> ```sql
> VACUUM (ANALYZE) order_items;
> VACUUM (ANALYZE) orders;
> ```
>
> `bash deploy/post-deploy.sh --only=backfill` 已自动执行这一步;手工回填请补跑。
验证:
```sql
@@ -174,6 +183,15 @@ SELECT COUNT(*) FROM order_items WHERE revenue_basis = 'unknown'; -- 退款上
```
长区间按 30 天窗口分段执行(默认 `DATABASE_STATEMENT_TIMEOUT_MS=15000`)。
> 41 万单规模的实测口径:30 天窗口的单条语句会超过 60 秒,被 statement timeout 打断。
> 这种体量请用 **7 天窗口 + 180 秒超时**:
>
> ```bash
> DATABASE_STATEMENT_TIMEOUT_MS=180000 bash deploy/post-deploy.sh \
> --only=sync --from=2026-09-27 --to=2026-10-27 --window-days=7
> ```
注意:人工历史成本按既定口径**不补录**,历史人工毛利仍不可用。
---
+52 -7
View File
@@ -219,19 +219,64 @@ docker compose ps
docker compose logs -f backend
```
## 财务/成本上线后的初始化(迁移 102–111)
## 财务/成本上线后的初始化(迁移 103–105)
代码与迁移只负责建表;**备用金账户、积分余额、人工 SKU 配置不会自动创建**。
首次上线后按 [财务上线初始化与验收-2026-10-26.md](财务上线初始化与验收-2026-10-26.md) 执行:
代码与迁移只负责建表;**存量订单回填、备用金账户、积分余额、商品目录、人工 SKU 不会自动创建**。
部署成功后按「回填 → 商品初始化 → 财务同步 → 验收」四步走:
### 1. 数据回填
```bash
bash deploy/post-deploy.sh --dry-run # 先看计划与待回填量
bash deploy/post-deploy.sh --only=backfill # ① 收入回填 + ② 履约方式回填
```
> 不要在商品目录为空时直接跑 `--only=sync`:财务同步会为每个未归一订单项写一条
> `product_mapping_missing` 成本异常(生产规模 90 天实测约 36 万条),后台异常队列会被淹没。
脚本包含 `revenue:backfill`、`fulfillment:backfill`、`finance:sync` 与只读验收四段;任何一步都能重跑,
`--only=backfill|sync|verify` 可单独执行。
> 生产 backend 镜像没有 `tsx` 与 `scripts/`,脚本通过 `tools` 镜像执行
> ([deploy/docker/tools.Dockerfile](../deploy/docker/tools.Dockerfile),`docker compose --profile tools`)。
### 2. 业务配置:后台 UI
1. 钱包管理 → 补建客服备用金账户 → 按需拨付;
2. 商品与成本 → 积分钱包 → 给客服发放三平台积分;
3. 商品与成本 → 人工 SKU 配置 → 扫描并逐项配置(保存后自动回填订单项);
4. 打开「财务 → 总账与对账」,同步总账、检查试算平衡;按需关账/导入平台账单;
5. 用手册 §二 的验收 SQL 检查:配置覆盖、人工成本快照、`manual_cost_missing`、销售事实与总账对平。
3. 商品与成本 → 标准商品 → 从接单模板初始化 → 按工单回填商品 → 归一人工商品;
4. 商品与成本 → 人工 SKU 配置 → 扫描并逐项配置(保存后自动回填订单项)。
> 第 3 步的商品目录/工单回填必须先于下一节 `--only=sync`。商品目录为空时同步会为每个
> 未归一订单项写一条 `product_mapping_missing` 异常(实测 90 天约 36 万条)。
### 3. 财务同步
商品归一完成后执行:
```bash
bash deploy/post-deploy.sh --only=sync # ③ 成本快照 / 销售事实
```
> 工具镜像每次运行都会重建(层缓存下只需几秒),确保 `git pull` 后执行的是当前脚本。
### 4. 总账与验收
5. 财务 → 总账与对账 → 同步销售事实到总账 → 检查试算平衡;按需关账/导入平台账单。
```bash
bash deploy/post-deploy.sh --only=verify # ④ 只读验收
```
退出码:`0` = 完成且验收无 FAIL、`1` = 执行失败、`2` = 验收未通过。
15 项检查全部 PASS 才算上线完成。若 15 号「近 7 天非人工销售事实成本覆盖」显示 WARN,
说明自动发货/接单平台仍有成本缺口,需要先归一商品并配置成本版本;WARN 不阻断验收,
但会直接影响毛利口径。
存量初始化完成后,日常重跑可直接 `bash deploy/post-deploy.sh`(回填 + 同步 + 验收一条龙)。
> 历史人工成本不补录;人工成本监控从第一次创建人工履约配置的时间开始。
> 定时任务 `cost-finance-sync` 每 5 分钟运行,负责履约归集、人工 SKU 回填、成本快照与销售事实。
> 定时任务 `cost-finance-sync` 每 5 分钟运行,但**只覆盖最近 7 天**;更早历史靠 `post-deploy.sh` 的分窗同步。
---