feat(finance): 统一内部 SKU 驱动的履约与财务分流

- 新增 internal_sku_mappings / internal_sku_channels,平台字段统一映射到内部中文 SKU
- 下单入口按内部 SKU 渠道决策修正默认人工路由,自动发货补全 dash sku
- 财务 channel_assign 读取统一渠道表;卡券/人工能力兜底,自动发货/接单要求真实载体
- carrier reconciliation 自动补自动发货任务和接单工单
- pending 落明确原因:无内部 SKU / 无渠道能力 / 无履约载体
- 合并、重判、旧 sku_code 规则清理、部署后 preflight 核对脚本
- 待分类队列按内部 SKU 聚合并展示原因
This commit is contained in:
yml2213
2026-11-02 14:17:08 +08:00
parent 531a23d78b
commit 5fe33b0b46
18 changed files with 1020 additions and 15 deletions
+141
View File
@@ -0,0 +1,141 @@
# 生产部署核对清单(履约与财务分流统一)
日期:2026-11-02
适用:生产当前在迁移 115,本次新增 116–119 + backend/frontend。
## 1. 部署前
1. 备份生产数据库(pg_dump,报告文件大小和 sha256)。
2. 记录当前:
- `schema_migrations` 最大版本;
- pending 数量及原因;
- `order_items` 渠道分布;
- `sku_channel_rules` 匹配键分布。
3. 确认没有正在跑的财务管道(或暂停定时任务)。
4. 本地/预发已通过:
- 后端测试、前端测试、build、lint、typecheck、SQL guard;
- 三平台 E2E(自动发货 / 卡券 / 接单 / 抖音自动发货);
- 成本、销售事实金额正确。
## 2. 迁移顺序
```text
116_voucher_internal_sku_backfill
117_internal_sku_mappings
118_internal_sku_channels
119_fulfillment_pending_reason
```
重点检查:
- 116:给已有 `tpv:` 商品补 capability 和 internal_sku 规则;
- 118:按商品能力/规则回填 `internal_sku_channels`;
- SQL 已做聚合,避免同名商品同渠道 `ON CONFLICT` 二次更新报错;
- 119:只加列和索引,风险低。
## 3. 部署顺序
1. 执行迁移 116–119;
2. 部署 backend;
3. 部署 frontend;
4. 启动/恢复定时任务。
## 4. 部署后核对
### 待分类
```sql
SELECT fulfillment_pending_reason, count(*)
FROM order_items
WHERE fulfillment_status = 'pending'
GROUP BY 1;
```
预期只剩:
- `no_internal_sku`
- `no_channel_capability`
- `no_carrier`
### 11月改名卡
```sql
SELECT oi.fulfillment_channel, count(*)
FROM order_items oi
WHERE oi.sku_name = '11月改名卡'
AND oi.fulfillment_status = 'assigned'
GROUP BY 1;
```
预期:`third_party_voucher`,不再 manual / pending。
### 财务与载体一致
```sql
SELECT
oi.fulfillment_channel,
count(*) FILTER (WHERE EXISTS (
SELECT 1 FROM work_orders wo WHERE wo.order_item_id = oi.id
)) AS work_orders,
count(*) FILTER (WHERE EXISTS (
SELECT 1 FROM fulfillment_tasks ft
WHERE ft.order_item_id = oi.id AND ft.executor_key = 'affiliate_dash'
)) AS affiliate_tasks,
count(*) FILTER (WHERE EXISTS (
SELECT 1 FROM fulfillment_tasks ft
WHERE ft.order_item_id = oi.id AND ft.executor_key = 'manual_dispatch'
)) AS manual_tasks
FROM order_items oi
WHERE oi.fulfillment_status = 'assigned'
GROUP BY 1;
```
### 规则
```sql
SELECT match_key_type, channel, enabled, count(*)
FROM sku_channel_rules
GROUP BY 1,2,3
ORDER BY 1,2,3;
```
旧 `sku_code` 规则应只剩无法定位内部 SKU 的少量记录。
### 内部 SKU 渠道表
```sql
SELECT channel, count(*)
FROM internal_sku_channels
WHERE enabled
GROUP BY 1;
```
## 5. 数据清理(确认后执行)
```bash
# 先 dry-run
npx tsx scripts/merge-duplicate-products.ts
npx tsx scripts/cleanup-legacy-sku-code-rules.ts
npx tsx scripts/reclassify-post-cutover.ts
# 确认后 apply
npx tsx scripts/merge-duplicate-products.ts --apply
npx tsx scripts/cleanup-legacy-sku-code-rules.ts --apply
npx tsx scripts/reclassify-post-cutover.ts --apply
```
顺序:
1. 合并重复商品;
2. 清理旧 sku_code 规则;
3. 重判断代后订单;
4. 跑一次完整管道;
5. 核对成本/销售事实。
## 6. 回滚
- 代码:回退到部署前镜像/commit;
- 数据库:
- 116–119 均为新增表/列/数据,不删旧表,可保留;
- 若必须回滚,恢复到部署前备份;
- 迁移 118/119 不改旧字段语义,回滚代码后旧逻辑仍可运行。