feat(cost-center): 成本中心与商品映射开发草案 (含文档与订单页调整 WIP)

This commit is contained in:
yml2213
2026-10-20 10:01:52 +08:00
parent 7542d547bf
commit 111bc76357
32 changed files with 7088 additions and 156 deletions
@@ -0,0 +1,190 @@
# 载体判定与成本中心落地:复核与草案(2026-10-17 第二轮)
> 状态:**历史(2026-10-17 第二轮复核,已归档)**;基线:`main @ ccd5ed2a`;代码未改动。
> 说明:本文的现行结论已吸收进 [成本中心与三块履约模型.md](../../成本中心与三块履约模型.md) 与 [财务系统集成方案.md](../../财务系统集成方案.md);本文件只用于追溯 2026-10-17 的数据量化与 DDL 验证过程,不再作为实现依据。
> 数据来源:**本地只读克隆**(生产快照,`orders` 316,597 行,数据截至 2026-10-14 14:52),全程只读 SELECT,未连生产、未写入任何业务库。
> 金额单位:**分**(`orders.total_amount` / `work_orders.reward_amount` / `source_order_amount_fen` 均为分)。
> 上游文档:[成本中心与三块履约模型.md](../../成本中心与三块履约模型.md)、[项目全量分析报告-2026-10-17.md](../../项目全量分析报告-2026-10-17.md)。
本轮任务是把上一轮「只分析」的结论**复核到底、量化到数、落成可执行草案**。三件事:① 提交并推送待提交文档;② 收敛本机生产备份暴露面;③ 用只读 SQL 量化载体分布与人工块,并产出载体判定 + 成本中心两份 SQL 草案。
---
## 一、本轮执行结果
| # | 事项 | 结果 |
| --- | --- | --- |
| 1 | 提交并推送文档 | `ccd5ed2a docs: 补充三块执行载体与通用成本中心方案`,已推送 `6850bc64..ccd5ed2a main -> main`;pre-push 两端全量 check 通过 |
| 2 | 备份目录加固 | `backups/` 与子目录 `chmod 700`,两份 dump `chmod 600`;删除残留临时 dump(见 §六) |
| 3 | 数据量化 | 只读克隆上完成 8 组聚合(见 §二、§三) |
| 4 | SQL 草案 | 载体判定 + 成本中心/销售事实两份,**已在沙箱库零错误应用验证**(见 §五) |
---
## 二、独立复核:上一轮结论逐条对照
| 上一轮结论 | 本轮复核方式 | 结果 |
| --- | --- | --- |
| 后端 740 测试(737 pass / 3 skipped) | 本地实跑 `npm test` | ✅ `tests 740 / pass 737 / fail 0 / skipped 3` |
| 前端 143 测试全绿 | 本地实跑 | ✅ `143 pass / 0 fail` |
| `typecheck` 通过 | 实跑 `tsc --noEmit` | ✅ 通过 |
| 99 个迁移 / 53 张表 | 数文件 + 数 `CREATE TABLE` + 查库 | ✅ 99 迁移、53 条 `CREATE TABLE`;本地库 55 张 = 53 业务表 + `schema_migrations` + `pg_stat_statements`×2 |
| 后端非测试源码 371 文件 | `find` 实测 | ✅ 371(另有测试 109) |
| 无 CI、实际单分支 | 查 `.github` / `.gitlab-ci.yml`;`git log main..dev` | ✅ 均不存在;`main..dev` 为 0 |
| 巨文件行数(3686/2015/1499/1478) | `wc -l` | ✅ 完全一致 |
**风险项抽查(读源码确认,非复述)**:发单金额后端只校验 `> 0`(`admin-work-order-management-service.ts:228`)✅;寄卖审核 `payload.workerId` 覆盖归属(`consignment-admin-service.ts:606`)✅;抢单先锁钱包、指派先锁工单(`work-order-grab-repo.ts:25` vs `:156`)✅ 锁序反转成立;自动工单押金硬编码 0(`sync-worker-orders-service.ts:245`)✅;Webhook 先落库去重后处理(`douyin-mashida/notify-service.ts:95` → `:112`)✅;`/collect/upload` 不校验订单存在、`/redeem` 仅凭订单号、`/files/object` 无鉴权、`CLAIM_TOKEN_TTL_HOURS=0` ✅;Caddy 无 CSP/HSTS ✅。
---
## 三、载体分布(近 30 天,只读克隆实测)
判定口径:**已支付订单项**为单位,「人工块」= 既无 `fulfillment_tasks` 也无 `work_orders`。
| 来源 | 已支付项 | 自动发货 `affiliate_dash` | 接单工单 | 人工块 | 双载体冲突 | 人工块金额 | 人工占比 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| `kuaishou-91` | 112,520 | 30,900 | 42,907 | **38,451** | 0 | ¥4,853,281 | 34.2% |
| `pdd` | 3,714 | 0 | 1,648 | **2,066** | 0 | ¥361,364 | 55.6% |
| `douyin-mashida` | 4 | 2 | 0 | 2 | 0 | ¥63 | 50.0% |
| **合计** | **116,238** | **30,902** | **44,555** | **40,519** | **0** | **¥5,214,708** | 34.9% |
补充说明(都已核对):
- 91 的 `112,520` 减去上述三类后还差 **262 项**,经查是历史执行器 `kuaishou_ct_assisted`(= `KUAISHOU_CLOUD`,已下线,活到 2026-09-15)的存量任务。**这是载体建模必须单列 `legacy` 的原因**,否则这 262 项会被错算成人工。
- **双载体冲突 = 0 条**,说明文档「一个订单项只有一个执行载体」的不变式在当前数据上成立。
- 人工块 `order_status` 全部 `paid`、`pay_status` 全部 `paid`、`claim_status` 全部 `active` —— **零完成信号**,与上一轮结论一致。
### 人工块的形状(监管收益判断)
| 来源 | 涉及店铺 | 单均 | p50 | p90 | ≥¥100 | ≥¥500 |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| 91 | **8** | ¥126.22 | ¥53 | ¥288 | 13,229 | 2,708 |
| PDD | 4 | ¥174.91 | ¥141 | ¥306 | 1,353 | 150 |
Top 店铺(人工块):向日葵游戏服务店 14,903 项 / ¥183.9 万;西瓜游戏交易店 7,768 项 / ¥118.0 万;小鱼儿游戏服务店 8,190 项 / ¥97.4 万;佳佳游戏交易店 7,460 项 / ¥84.5 万;PDD 向日葵网络游戏专营店 1,885 项 / ¥32.8 万。
结论:**人工块店铺高度集中(91 只有 8 个店铺)**,意味着「先上线人工 case 队列」的监管收益集中在少数店铺上,容易先抓重点、快速看到效果。
### 月度趋势(确认不是历史遗留)
| 月份 | 已支付项 | 人工块 |
| --- | ---: | ---: |
| 2026-08 | 142,969 | 46,337 |
| 2026-09 | 103,010 | 39,148 |
| 2026-10(截至 14 日) | 55,647 | 17,469 |
### 人工成本区间(单均人工成本 X 元的月度量级)
| X | ¥3 | ¥5 | ¥8 | ¥15 |
| --- | ---: | ---: | ---: | ---: |
| 月人工成本 | ¥121,557 | ¥202,595 | ¥324,152 | ¥607,785 |
这是「人工履约投入」的量级估算,不含商品/货源成本(那部分属于 `cost_versions`)。
---
## 四、两个口径硬问题(比上一轮描述更严重)
### 1. 数量:`quantity` 不可用,低估约 7.3 倍
| 指标 | 实测值(全量已支付 316,585 项) |
| --- | ---: |
| `quantity = 1` | 316,578(99.998%) |
| `quantity > 1` | **7** |
| 名称里带「N个」但 `quantity = 1` | **104,710** |
| `sum(quantity)` | 316,593 |
| 按 SKU 名称解析的数量合计 | **2,316,222** |
用 `quantity` 统计「卖了多少件」会低估约 **7.3 倍**。必须在订单项固化 `normalized_quantity`(草案已加列)。
### 2. SKU 多码一名:比上一轮更严重
| 商品名 | 不同 `sku_code` 数 | 项数 |
| --- | ---: | ---: |
| 苏尔南钻石100颗 | **47** | 3,319 |
| 99999 | 44 | 540 |
| 指挥官密钥1个 | **42** | 26,748 |
| 指挥官密钥5个 | 41 | 3,608 |
| 指挥官密钥10个 | 37 | 3,059 |
| 苏尔南钻石50颗 | 35 | 379 |
(上一轮报的是近 30 天窗口的 27/35/10;本轮是全量已支付口径,两者不矛盾,但都指向同一个结论:**不做 `channel_product_mappings` 就无法统计销量**。)
---
## 五、对上一轮结论的 4 处修正
| # | 上一轮说法 | 本轮修正 |
| --- | --- | --- |
| 1 | C5「订单项无金额字段」是当下风险 | **是潜在缺口**:实测每笔已支付订单恰好 1 个订单项(316,585 单 = 316,585 项),订单级金额暂时等价;但 schema 允许多项,应在做销售事实前补齐 |
| 2 | 三载体互斥(设计不变式) | **数据上已验证**:近 30 天双载体 = 0 条 |
| 3 | 自动发货执行器只剩 `affiliate_dash` | 还有 **262 项历史 `kuaishou_ct_assisted` 任务**(已下线,2026-09-15 停);载体建模需单列 `legacy`,且这类任务仍在领取页给出「已下线」提示 |
| 4 | 手动履约 `manual_dispatch` 任务极少 | 补充:`affiliate_dash` 侧仍在途 552 项(`link_generated` 300 + `waiting_binding` 252),属运营待跟进而非人工块 |
---
## 六、两份 SQL 草案
放在 `docs/drafts/`,**未接入** `apps/backend/src/db/migrations`(接入时再拆分编号并先跑 `npm run check:sql`,避免本地栈重启时自动迁移)。
| 文件 | 内容 |
| --- | --- |
| [载体判定与人工块量化.sql](../../drafts/2026-10-17-载体判定与人工块量化.sql) | §1 八组只读量化查询(含实测值注释);§2 判定规则与优先级(含 `legacy` 与冲突处理);§3 加列 + 回填 + 索引 + 回滚;§4 守恒校验与异常队列查询;§5 人工成本区间 |
| [成本中心与销售事实-DDL草案.sql](../../drafts/2026-10-17-成本中心与销售事实-DDL草案.sql) | `product_catalog`、`channel_product_mappings`、`cost_items`、`cost_versions`、`cost_snapshots`、`cost_exceptions`、`sales_daily_facts`;订单项口径补齐(`normalized_quantity`/`revenue_amount_fen`/`revenue_basis`/`refunded_at`);`work_product_rules.cost_item_id` 过渡列;§5 对平校验;§6 已确认口径与剩余待确认 |
| [人工支出登记-DDL草案.sql](../../drafts/2026-10-17-人工支出登记-DDL草案.sql) | `manual_fulfillment_cases`(轻量处理记录)+ `manual_expense_records`(付款项目/金额/订单号/付款人/收款人/凭证/审批)+ `manual_expense_events` 审计;§3 校验查询;§4 与影子总账的分录约定 |
**验证方式**:在本地副本上 `pg_dump --schema-only` 建了一个一次性沙箱库(55 张表),按依赖顺序(载体判定 → 成本中心 → 人工支出登记)用 `psql -v ON_ERROR_STOP=1` 应用**三份草案 —— 零错误**:生成 10 张新表(共 65 张)、`order_items` 由 9 列增至 19 列、27 个 `CHECK` 约束、169 个索引;校验查询与回填语句均可执行。验证后沙箱库已 `DROP`。
关键设计约束(三份草案共同遵循):
1. **改价只发生在 `cost_versions`**,历史成本靠 `cost_snapshots` 固化,永不回写;
2. **匹配不到成本不默认 0**,写入 `cost_exceptions`,报表必须暴露 `open` 数量;
3. `carrier_type`/`carrier_status` 是**派生列**,事实来源仍是任务/工单存在性,必须有重算作业与守恒校验;
4. 报表维度统一 `canonical_product_id + shop_id + stat_date`,禁止直接用 `sku_code` 或 `orders.total_amount`。
---
## 七、本机卫生记录(第 2 项执行细节)
| 项 | 处理前 | 处理后 |
| --- | --- | --- |
| `backups/`、`backups/postgres/`、`backups/douyin-platform-clear-20261014/` | `755` | **`700`** |
| `order_site_latest.dump`(559 MB)、`order_site_remote.dump`(105 MB) | `644` | **`600`** |
| `.order_site_readonly.dump.nKbeRp8kUA`(137,330,688 B) | `600`,残留 | **已删除**,`backups/` 由 765 MB 降至 634 MB |
删除前已确认:① 解析后绝对路径为 `…/order_site/backups/postgres/.order_site_readonly.dump.nKbeRp8kUA`;② 是**完整可读**的 PostgreSQL 自定义 dump(`pg_restore --list` 795 条目 / 53 个 `TABLE DATA`),sha256 `1a666af11ae9df2058bc2a5afa2196baefb9c125daf0cdecf18dfd20c59cc075`;③ 全仓库无任何脚本引用该文件或其余两份 dump 名;④ 无进程占用;⑤ 与同分钟产出的 `order_site_latest.dump` 属同一批快照,且本地克隆数据(截至 10-14)比这两份 10-10 快照更新。
**遗留建议**:本机仍存两份全量生产 dump(含买家信息与平台凭据)。建议确认磁盘已加密;若后续以本地克隆为唯一分析基线,可只保留一份最新 dump。
**分析基线建议**:本地 docker 栈(`order_site-postgres-1`)已含 10-14 生产快照(`orders` 316,597),后续所有 SQL 迭代直接打本地克隆即可,无需再连生产。
---
## 八、业务口径确认(2026-10-17)
业务已就四个阻塞问题给出结论;其中两条已用克隆数据独立核验。
| # | 问题 | 结论 | 数据核验 |
| --- | --- | --- | --- |
| 1 | 91 的 `total_amount` 是收入还是成本上限? | **收入**:它就是**用户付款金额**,与快手发码配合 | ✅ 91 已支付 312,569 笔,其中 235,271 笔含快手 `kuaishouSendCode.paymentFen`;**235,268 笔与 `total_amount` 完全一致(100.00%)**,仅 3 笔差 ¥1.80 / ¥1.85 / ¥2.00(2026-09-05 / 09-18 / 10-04) |
| 2 | 打手报酬是成本还是代收代付? | **支出/成本**:工单结算 → 打手可提现 → 提现打款,属我方真实支出 → **总额法** | — |
| 3 | 人工块的目标? | **先把流程搞清楚:谁处理了哪些、付了多少钱**。现状「很多地方需要人工付款,但不知道付款项目、金额、订单号」→ 新增**人工支出登记**草案;SLA/监管指标延后 | — |
| 4 | 退款/仲裁? | **暂未接入,先预留**:字段 + `reversal_of_id` + 事件类型,本轮不实现逻辑 | — |
影响:
- 收入口径统一为 `revenue_amount_fen = orders.total_amount`(`revenue_basis = 'user_payment'`),草案中原本枚举的三家字段名已简化;
- 成本侧确认「打手报酬 = 支出」,`cost_items.cost_type = 'work_order_reward'` 计入履约成本,不做净额;
- 人工块方案重心从「case + SLA」改为「**人工支出登记**(付款项目 / 金额 / 订单号 / 付款人 / 收款人 / 凭证 / 审批)」;
- 退款相关列与冲正关系保留为预留,不写逻辑。
---
## 九、下一步
1. **业务仍待拍板 1 个关键口径**:收入**确认时点**(付款 / 发货 / 验收 / 售后期结束)—— 它决定利润表与预收账款;金额口径已定。
2. **工程可先做两件不改业务代码的事**:
- 建 `channel_product_mappings` 初始映射,把 47 个 `sku_code` 归到 1 个标准商品;
- 用现有 `resolveSkuNameQuantity` 解析并回填 `normalized_quantity`。
3. **紧接着做人工支出登记**(业务当前最痛):`manual_fulfillment_cases` + `manual_expense_records`,先解决「谁付的、什么项目、多少钱、哪个订单」。
4. 之后按「商品主数据 → 成本中心影子期 → 人工支出 → 影子总账 → 对账转正」推进。
+8 -2
View File
@@ -14,9 +14,15 @@
| `近期性能与稳定性专项优化分析报告.md` | 2026-10-01 / 10-02 的生产快照与专项复盘,保留作性能证据,不代表当前容量 |
| `寄卖系统迁移079上库前检查.sql` | 迁移 079 的一次性上库前检查脚本,生产已执行;保留作审计追溯 |
## 2026-10-17
| 文档 | 归档原因 |
| --- | --- |
| `2026-10-17/载体判定与成本中心落地-复核与草案.md` | 2026-10-17 第二轮复核与数据量化;现行结论已吸收进 `成本中心与三块履约模型.md` 与 `财务系统集成方案.md`,仅保留作数据与 DDL 验证追溯 |
## 使用建议
- 需要当前架构与风险:看 `docs/项目全量分析报告-2026-10-17.md`。
- 需要财务系统设计:看 `docs/财务系统集成方案.md`。
- 需要当前架构与风险:看 `docs/项目全量分析报告-2026-10-17.md`(参考)和 `docs/README.md`(状态索引)。
- 需要财务系统设计:看 `docs/财务系统集成方案.md`;实施顺序以 `docs/README.md` §三 为准。
- 需要某个平台协议:看对应的厂商文档目录(`91卡券/`、`行业电子凭证/`、`发货平台-2/`、`抖音-码速达/接口API/`、`抖音-码速达/消息推送/`)。
- 历史文档中的代码行号、测试数量、迁移数量不要直接引用。