feat(cost-center): 成本异常 60 天归档

cost_exceptions 只增不减:本地快照 260,253 行(resolved 151,939 / open 108,314),
已处理的行只是审计痕迹,留在热表会拖慢「状态 + 时间」分页,并让唯一索引持续膨胀。

- 迁移 108:新增 cost_exceptions_archive(保留原 id 与时间字段,加 archived_at),
  热表补部分索引 idx_cost_exceptions_archivable
  (COALESCE(resolved_at, created_at)) WHERE status <> 'open',让每日任务不扫全表;
- cost-exception-archive-service:只归档 status <> 'open' 且超期的行,每批 1000 行,
  DELETE ... RETURNING + INSERT 在同一语句内完成,单批原子,不存在「删了没归档」;
- data-retention 每日任务接入;保留期走 COST_EXCEPTION_RETENTION_DAYS(默认 60);
- 新增 npm run cost-exceptions:archive(--dry-run / --days / --batch-size)。

为什么 open 行不归档:open 是待办队列,超期不等于已解决,归档等于把问题藏起来。

验证:
- 3 条 resolved 回拨到 90 天前 → 归档 3 条,热表 resolved 151,939 → 151,936;
- 1 条 open 回拨到 90 天前 → 仍在热表,未被归档;
- 归档表保留原 id 与时间,archived_at 记录归档时刻;
- 恢复测试数据后计数回到 151,939 / 108,314。
This commit is contained in:
yml2213
2026-10-25 12:09:20 +08:00
parent fcd9b607d4
commit d90c066888
15 changed files with 300 additions and 0 deletions
+1
View File
@@ -85,6 +85,7 @@
| 履约方式统一 | **已实施 P0** | 迁移 `105`;375,447 项全部回填:接单平台 153,148、人工 115,194、历史平台 53,901、自动发货 53,204 |
| 人工商品归一 | **已实施 P1(本次快照恢复执行)** | 人工 115,194 项已归一为 1,195 个 `man:<内部SKU>` 商品;历史人工支出仍不补录 |
| 影子总账 | 未实施 | 现有财务页仍是 `UNION ALL` 业务事件拼装,不是复式总账 |
| 成本异常归档 | **已实施** | 迁移 `108`;已处理异常超 60 天搬入 `cost_exceptions_archive`,open 行不归档;`COST_EXCEPTION_RETENTION_DAYS` 可调 |
| 退款冲减 / 平台对账 / 期间关账 | 未实施 | 仅方案 |
> 分支状态(2026-10-25):`dev @ 465a9f65` 之上完成迁移收敛(10 → 5,`103`–`107`),并已与 `main` 的 worker-hall 改动合并。本地库按「清 `schema_migrations` 103–112 后重跑」对齐,升级结果与全新构建的 schema 完全一致;生产 101 → 107 的路径已验证。**未推送、未部署生产**;生产口径仍以 `main` 为准。
+16
View File
@@ -468,6 +468,22 @@ npm run revenue:backfill -- --batch-size=2000 --max-batches=100
| `idx_work_orders_rule_cover` | 成本版本页按接单规则聚合 |
| `idx_order_items_revenue_cover` | 收入聚合 |
| `idx_work_categories_name` | 商品目录按分类名 lateral join |
| `idx_cost_exceptions_archivable` | 归档任务扫描「已处理且超期」的异常(部分索引,`status <> 'open'`) |
### 9.1 成本异常归档(60 天)
`cost_exceptions` 只增不减:本地快照 260,253 行(resolved 151,939 / open 108,314)。已处理的行只是审计痕迹,继续留在热表会拖慢按状态+时间的分页,并让唯一索引持续膨胀。
| 项 | 规则 |
| --- | --- |
| 归档对象 | `status <> 'open'` 且 `COALESCE(resolved_at, created_at)` 超过保留期 |
| open 行 | **永不归档**——它是待办队列,超期也要留在页面上 |
| 保留期 | `COST_EXCEPTION_RETENTION_DAYS`,默认 **60** 天 |
| 落点 | `cost_exceptions_archive`(保留原 id 与时间字段,加 `archived_at`) |
| 执行 | `data-retention` 每日任务;每批 1000 行,`DELETE ... RETURNING` + `INSERT` 同语句,单批原子 |
| 手动 | `npm run cost-exceptions:archive -- --dry-run` / `-- --days=90` |
归档不删除审计痕迹,但热表查询与页面上不会再出现这些历史行;需要追溯时查归档表。
---