diff --git a/docs/README.md b/docs/README.md index 4697f4da..e428eb97 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,7 +28,7 @@ ### 3. 履约方式与成本模型必须分开 -- `fulfillment_mode` 回答“谁执行”;`cost_model` 回答“成本怎么算” +- `fulfillment_channel`(+ `fulfillment_status`)回答“谁执行”;`cost_model` 回答“成本怎么算” - 自动发货:`affiliate_purchase` - 接单平台:`work_order_reward` - 人工:`manual_actual`(逐单实际金额) @@ -80,7 +80,7 @@ | 后台用户中文显示名 | **已实施** | `admin_users.display_name`,备用金姓名触发器同步;审计、登录记录、任务处理人优先显示中文名 | | 商品主数据 + 渠道 SKU 映射 | **已实施 P0/P1** | 2,017 个标准商品、4,897 条映射:`tpl:` 710、`man:` 1,195、`aff:` 49、`wo:` 63;订单项已按履约证据/人工内部 SKU 归一 | | 成本中心 | **已实施 P0** | 迁移 `103`;710 个规则级版本 + 2 个全局占位版本;工单成本快照 151,939 条,其中 actual 129,430、estimated 22,509 | -| 销售事实 / 订单毛利 | **已实施 P0** | 迁移 `103`;375,447 项、收入 ¥3,143.61 万、成本 ¥572.95 万、毛利 ¥2,570.66 万;`sales_daily_facts.fulfillment_mode` | +| 销售事实 / 订单毛利 | **已实施 P0** | 迁移 `103`;375,447 项、收入 ¥3,143.61 万、成本 ¥572.95 万、毛利 ¥2,570.66 万;`sales_daily_facts.channel` | | 人工成本录入(确认履约) | **已实施 P0(2026-10-26 单事务 + 防重复)** | 确认即扣积分/备用金并写 `actual` 快照;钱与成本同事务、订单项级唯一;无登记单/审批/报销 | | 人工 SKU 配置自动匹配 | **已实施(2026-10-26)** | 保存配置立即回填 + 定时 rematch + 确认时按 SKU 兜底匹配 | | 人工缺成本监控 | **已实施(2026-10-26)** | `manual_cost_missing`:首次人工配置之后的订单项未确认即入队,确认后自动收口 | @@ -97,7 +97,7 @@ ## 三、统一实施顺序 ```text -0. 统一履约事实(fulfillment_mode + 成本模型正交) 已完成 P0(迁移 103) +0. 统一履约事实(渠道 + 成本模型正交) 已完成(迁移 112–114) 1. 商品主数据 + 渠道 SKU 映射 已完成 P0(仅自动发货/接单平台) 2. 成本中心 + 销售事实 已完成 P0 2.5 人工成本链路收敛(确认履约 → 扣积分/备用金 → 写快照) 已完成 P0(迁移 104) @@ -158,7 +158,7 @@ | 文件 | 用途 | 接入前必须处理 | | --- | --- | --- | | [drafts/2026-10-17-载体判定与人工块量化.sql](drafts/2026-10-17-载体判定与人工块量化.sql) | 载体分布/人工块量化查询、`carrier_type` 判定与回填 | **已过时**:`carrier_type` 已从商品/成本/快照/异常表删除;只读查询里的口径结论已吸收进总纲 §二 | -| [drafts/2026-10-17-成本中心与销售事实-DDL草案.sql](drafts/2026-10-17-成本中心与销售事实-DDL草案.sql) | `product_catalog`、`channel_product_mappings`、`cost_items/versions/snapshots/exceptions`、`sales_daily_facts` | 已落地为 `103`–`105`;草案里的 `carrier_type` 已被 `cost_model` / `fulfillment_mode` 取代,仅作设计追溯 | +| [drafts/2026-10-17-成本中心与销售事实-DDL草案.sql](drafts/2026-10-17-成本中心与销售事实-DDL草案.sql) | `product_catalog`、`channel_product_mappings`、`cost_items/versions/snapshots/exceptions`、`sales_daily_facts` | 已落地为 `103`–`105`;草案里的 `carrier_type` 已被 `cost_model` / 渠道三列取代,仅作设计追溯 | | [drafts/2026-10-31-112-履约渠道拆分-迁移草案.sql](drafts/2026-10-31-112-履约渠道拆分-迁移草案.sql) | 渠道/状态/来源拆列、`sku_channel_rules`、异常两级化、水位表、卡券成本项 | 已落地为 `apps/backend/src/db/migrations/112_fulfillment_channel_split.sql`;草案保留作设计追溯 | | [drafts/2026-10-31-同步链路重构-旧函数对照.md](drafts/2026-10-31-同步链路重构-旧函数对照.md) | 16 个旧同步/回填入口 → 6 个新管道作业的对照与验收口径 | 已落地:三条管道服务 + 六个定时任务 + 后台「履约渠道」页 | | [archive/2026-10-23/2026-10-17-人工支出登记-DDL草案.sql](archive/2026-10-23/2026-10-17-人工支出登记-DDL草案.sql) | `manual_fulfillment_cases`、`manual_expense_records`、`manual_expense_events` | **已作废**:`manual_expense_records` / `manual_expense_events` 已随迁移 `104` 归并下线,人工成本改走「人工录单 → 扣备用金 → 写快照」;文件已移入 archive | diff --git a/docs/dev合并main评估-2026-10-27.md b/docs/dev合并main评估-2026-10-27.md index 1af5b22c..e55627f9 100644 --- a/docs/dev合并main评估-2026-10-27.md +++ b/docs/dev合并main评估-2026-10-27.md @@ -1,5 +1,9 @@ # dev 合并 main 评估(2026-10-27) + +> **历史文档(2026-10-31 前口径)**:文中 `fulfillment_mode` / `legacy_platform` 已随迁移 112–114 删除; +> 现行口径为 `order_items.fulfillment_channel + fulfillment_status`,销售事实维度为 `sales_daily_facts.channel`。 +> 本文保留用于追溯当时的决策,不再作为实现依据。 > 评估对象:`main`(2fb7aae1)与 `dev`(0de0fd7b + 当前未提交的财务 P0/P1 改动)。 > 生产基线:生产库当前 `schema_migrations=102`;验证用 dump 恢复到 `101` 后再补 `102`。 > 结论:**代码可以合并;生产发布还需要执行一组一次性数据初始化/回填,不能只合并就上线。** diff --git a/docs/成本中心API契约.md b/docs/成本中心API契约.md index d2840c50..b4384079 100644 --- a/docs/成本中心API契约.md +++ b/docs/成本中心API契约.md @@ -1,8 +1,8 @@ # 成本中心 API 契约 > 状态:**现行**。后端服务与前端页面都必须严格按此实现,不得各自发挥。 -> 最后校准:2026-10-25 -> 代码基线:`dev`(合并 dev1023 后)+ 迁移 `103`–`111`(已合并到 `dev`,**未推送、未部署生产**) +> 最后校准:2026-10-31(履约渠道模型,迁移 112–114) +> 代码基线:`main` > 详细业务口径见 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md);数据模型见 [成本中心与履约模型.md](成本中心与履约模型.md)。 ## 一、字段替换总表 @@ -14,7 +14,7 @@ | `cost_snapshots.carrier_type` | (删除) | DB | | `cost_exceptions.carrier_type` | (删除) | DB | | `product_catalog.carrier_type` | (删除,改由 `canonical_sku` 前缀推导) | DB / 商品接口 | -| `sales_daily_facts.carrier_type` | `sales_daily_facts.fulfillment_mode` | DB / 销售事实接口 | +| `sales_daily_facts.carrier_type` | `sales_daily_facts.channel` | DB / 销售事实接口 | | `work_product_rules.legacy_cost_source` | (删除) | DB | | `manual_expense_records` / `manual_expense_events` | (删除,人工成本只走确认履约) | DB | @@ -28,6 +28,7 @@ | --- | --- | --- | | `affiliate_purchase` | 自动发货上游采购成本 | `cost_type=auto_purchase` | | `work_order_reward` | 接单平台打手报酬 | `cost_type=work_order_reward` | +| `third_party_voucher_cost` | 第三方卡券采购成本 | 新增 | | `manual_actual` | 人工客服实际支出 | `cost_type=manual_service` | | `platform_fee` | 平台服务费 | `cost_type=platform_fee` | | `payment_fee` | 支付通道费 | `cost_type=payment_fee` | @@ -39,9 +40,12 @@ `per_item` / `per_order` / `percent_of_revenue` (删除 `per_hour`、`fixed_daily`——人工成本逐单按实际金额,不按工时) -### 2.3 `fulfillment_mode`(履约方式,唯一真源) +### 2.3 `channel`(履约渠道,唯一真源) -`work_order` / `affiliate_dash` / `legacy_platform` / `manual` +`work_order` / `affiliate_dash` / `third_party_voucher` / `manual`, +另有两个派生值:`pending`(待分类)、`archived`(断代归档)、`unclassified`(事实桶)。 + +DB 真源是 `order_items.fulfillment_channel` + `fulfillment_status`;接口字段名统一为 `channel`。 ## 三、成本项接口 @@ -116,11 +120,14 @@ AND ( | 接口 | 说明 | | --- | --- | -| `GET /api/v1/admin/cost-center/mappings/diagnostics` | 标准商品数 / 映射数 / 有映射商品数 / 未归一订单项 / 未归一 SKU 组;按 `fulfillment_mode` 拆分聚合 | -| `GET /api/v1/admin/cost-center/mappings/unmapped` | 未归一队列,按「来源 + 店铺 + 平台 SKU + 履约方式」聚合,支持 `keyword` / `provider` / `fulfillmentMode` 与分页 | +| `GET /api/v1/admin/cost-center/mappings/diagnostics` | 标准商品数 / 映射数 / 有映射商品数 / 未归一订单项 / 未归一 SKU 组;按 `channel` 拆分聚合 | +| `GET /api/v1/admin/cost-center/mappings/unmapped` | 未归一队列,按「来源 + 店铺 + 平台 SKU + 履约方式」聚合,支持 `keyword` / `provider` / `channel` 与分页 | | `POST /api/v1/admin/cost-center/mappings/backfill` | 回填订单项映射;返回 `resolvedExceptionCount`(已归一的 `product_mapping_missing` 异常自动收口) | -### 4.2 人工商品归一(P1) +### 4.2 人工商品归一(已删除) + +> 2026-10-31 起商品归一只走 `product_normalize` 管道(模板/人工配置/`aff:`/`tpv:` 直达), +> 不再提供手工归一接口。以下为历史记录。 `POST /api/v1/admin/cost-center/manual-products/normalize`,`finance.config`。 @@ -177,15 +184,21 @@ AND ( ## 六、成本异常接口 -路径不变。请求参数 `carrierType` **删除**;响应 `carrierType` **删除**,改为 `fulfillmentMode`。 -若前端要按履约方式筛选异常,用参数 `fulfillmentMode`: +请求参数 `carrierType` / `fulfillmentMode` **删除**;筛选统一用 `channel`: + +```jsonc +// 请求 +{ "status": "open", "subjectType": "order_item", "channel": "manual" } +``` + +旧参数示例(已不生效): ```jsonc // 请求 { "status": "open", "subjectType": "order_item", "fulfillmentMode": "manual" } ``` -后端实现:join `order_items oi ON oi.id = ce.order_item_id` 并过滤 `oi.fulfillment_mode`。 +后端实现:join `order_items oi ON oi.id = ce.order_item_id` 并过滤 `oi.fulfillment_channel`。 响应项: @@ -196,7 +209,7 @@ AND ( "subjectId": 316571, "orderItemId": 316571, "workOrderId": null, - "fulfillmentMode": "manual", + "channel": "manual", "reason": "cost_missing", "status": "open", "detail": {}, @@ -211,11 +224,11 @@ AND ( ## 七、销售事实接口 -`GET /api/v1/admin/sales-profit` 的分组维度字段名从 `carrierType` 改为 `fulfillmentMode`: +`GET /api/v1/admin/sales-profit` 的分组维度字段名为 `channel`,响应分组为 `byChannel`: ```jsonc { - "fulfillmentMode": "manual", + "channel": "manual", "itemCount": 102955, "grossRevenueFen": 1219202470, "costAmountFen": 32000, @@ -224,7 +237,7 @@ AND ( } ``` -筛选参数同样从 `carrierType` 改为 `fulfillmentMode`,取值四种履约方式。 +筛选参数用 `channel`,取值 `work_order` / `affiliate_dash` / `third_party_voucher` / `manual` / `unclassified`。 ## 八、成本中心概览接口 diff --git a/docs/成本中心与履约模型.md b/docs/成本中心与履约模型.md index fc5be834..f628cc5f 100644 --- a/docs/成本中心与履约模型.md +++ b/docs/成本中心与履约模型.md @@ -1,11 +1,11 @@ # 成本中心与履约模型 -> 状态:**现行**。商品主数据、成本中心、成本快照、销售事实、履约事实均已落地。 -> 最后校准:2026-10-27 -> 代码基线:`dev`(合并 dev1023 + 人工成本完整性修复,迁移基线收敛为 `103`–`105`) -> 数据基线:生产 dump `order_site-20261022-092601.dump`(2026-07-10 ~ 2026-10-22);本地只跑了近 30 天增量,历史数字仅作基线。 +> 状态:**现行**。2026-10-31 起履约事实改为「渠道 + 状态」两列,旧 `fulfillment_channel` 已删除。 +> 最后校准:2026-10-31(迁移 112–114) +> 代码基线:`main` +> 数据基线:2026-10-29 生产 dump;历史数字仅作迁移参照。 > 上线初始化/验收见 [财务上线初始化与验收-2026-10-26.md](财务上线初始化与验收-2026-10-26.md)。 -> **业务口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准**;本文只负责数据模型、迁移与运维。 +> 业务口径见 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md)(10-31 前的历史口径);本文负责数据模型、迁移与运维。 > 接口字段见 [成本中心API契约.md](成本中心API契约.md)。 --- @@ -13,23 +13,24 @@ ## 〇、模型总览 ```text -orders ──┬── order_items ──── fulfillment_mode (唯一履约事实) +orders ──┬── order_items ──── fulfillment_channel + fulfillment_status (唯一履约事实) │ ├── canonical_product_id ── product_catalog │ ├── normalized_quantity / normalized_sku_name / normalized_sku_series │ └── revenue_amount_fen / revenue_basis │ ├── cost_snapshots ──── cost_versions ──── cost_items (cost_model) - ├── cost_exceptions (order_item_id → order_items.fulfillment_mode) - ├── sales_daily_facts (fulfillment_mode) + ├── cost_exceptions (order_item_id → order_items 渠道) + ├── sales_daily_facts (channel) └── manual_expenses (客服备用金支出) ── subject_id ── cost_snapshots ``` -四条不变式: +五条不变式: -1. **履约方式只有一处存**:`order_items.fulfillment_mode`。快照、异常、销售事实要展示履约方式时 join `order_items`。 -2. **商品来源只由 `canonical_sku` 前缀表达**,商品表不存载体列。 -3. **成本模型只表达"怎么算"**:`cost_items.cost_model` 与履约方式正交,不复用同一字段。 -4. **成本金额只在一处改**:`cost_versions`;业务表只保存快照,历史成本永不回写。 +1. **履约渠道只有一处存**:`order_items.fulfillment_channel` + `fulfillment_status`。快照、异常、销售事实只 join `order_items`,不再冗余渠道。 +2. **渠道判定必须留痕**:`fulfillment_source` 记录规则/证据/人工指定;指定后 `fulfillment_locked=true`,自动判定不再覆盖。 +3. **商品来源只由 `canonical_sku` 前缀表达**(`aff:` / `tpl:` / `wo:` / `tpv:` / `man:`),商品表不存载体列。 +4. **成本模型只表达"怎么算"**:`cost_items.cost_model` 与渠道正交,不复用同一字段。 +5. **成本金额只在一处改**:`cost_versions`;业务表只保存快照,历史成本永不回写。 --- @@ -58,7 +59,7 @@ orders ──┬── order_items ──── fulfillment_mode (唯一履约 | `cost_snapshots.carrier_type` | (删除) | DB | | `cost_exceptions.carrier_type` | (删除) | DB | | `product_catalog.carrier_type` | (删除,改由 `canonical_sku` 前缀推导) | DB / 商品接口 | -| `sales_daily_facts.carrier_type` | `sales_daily_facts.fulfillment_mode` | DB / 销售事实接口 | +| `sales_daily_facts.carrier_type` | `sales_daily_facts.channel` | DB / 销售事实接口 | | `work_product_rules.legacy_cost_source` | (删除) | DB | | `cost_versions.cost_item_carrier_type` | `cost_versions.cost_item_cost_model` | 成本版本接口 | | `pricing_mode: per_hour / fixed_daily` | (删除,只剩三种) | 成本项接口 | @@ -253,7 +254,7 @@ resolved_at, superseded_at, created_at - 同一主体 + 同一成本项只允许一条"当前有效"快照(`uq_cost_snapshots_active_subject_item`); - 预估快照代表支付时点的成本,**不因后续改价重写**;只有实际成本允许替代当前有效行; - 匹配不到成本时写 `cost_missing` 异常,**不能默认 0**; -- 履约方式不存快照里,需要时 join `order_items.fulfillment_mode`; +- 履约方式不存快照里,需要时 join `order_items.fulfillment_channel`; - 人工单的快照主体是 `subject_type='manual_case'` + `subject_id=order_item_id`;资金流水 id 只写 `metadata_json`,**没有独立的登记单表**; - 负数 `subject_id` 表示历史孤儿快照(迁移 `103` 约定),迁移 `104` 保留不动,不参与新链路; - 一个订单项最多一条有效人工快照(`uq_cost_snapshots_manual_order_item`)。 @@ -270,7 +271,7 @@ detail_json created_at, resolved_at, resolved_by ``` -`open` 数量必须在后台暴露。按履约方式筛选时用 `EXISTS`/`IN` 半连接 `order_items.fulfillment_mode`(本地 248,878 条,`IN` 半连接 + `idx_order_items_fulfillment_id` 索引约 170ms)。 +`open` 数量必须在后台暴露。按履约方式筛选时用 `EXISTS`/`IN` 半连接 `order_items.fulfillment_channel`(本地 248,878 条,`IN` 半连接 + `idx_order_items_fulfillment_id` 索引约 170ms)。 --- @@ -299,7 +300,7 @@ created_at, resolved_at, resolved_by stat_date -- Asia/Shanghai,按 paid_at 归属 provider, shop_id canonical_product_id -- 允许为空:未归一桶 -fulfillment_mode -- work_order / affiliate_dash / legacy_platform / manual +channel -- work_order / affiliate_dash / third_party_voucher / manual / unclassified order_count, item_count, quantity gross_revenue_fen, refund_amount_fen, net_revenue_fen cost_amount_fen, cost_missing_count, cost_stage @@ -309,7 +310,9 @@ computed_at 口径: +- 只统计 `pay_status IN ('paid','refunded')` 的订单; - 收入 = `order_items.revenue_amount_fen`,不直接拿整单金额; +- 退款 = `order_refunds` 中 `confirmed` 的金额,净收入 = 收入 − 退款; - 成本来自 `cost_snapshots` 当前有效行;没有快照的订单项计入 `cost_missing_count`; - 未归一商品允许 `canonical_product_id` 为空,单独成"未归一"行,保证总收入对平; - `cost_stage`:`missing`(全部无快照)/ `partial`(部分无)/ `estimated`(全部预估)/ `actual`(全部实际); @@ -329,73 +332,41 @@ computed_at ## 六、履约事实 -### 6.1 `order_items.fulfillment_mode` +### 6.1 `order_items` 渠道三列 ```text -fulfillment_mode -- work_order / affiliate_dash / legacy_platform / manual;允许 NULL -fulfillment_decided_at -- 最后一次判定时间 -fulfillment_reason -- 判定来源:order_sync / reconcile / backfill +fulfillment_channel -- work_order / affiliate_dash / third_party_voucher / manual;pending 时为空 +fulfillment_status -- pending 待分类 / assigned 已确定 / archived 断代归档 +fulfillment_source -- rule / evidence / operator / manual_confirm / cutover_archive ... +fulfillment_locked -- 人工指定后为 true,自动判定跳过 +fulfillment_decided_at / fulfillment_reason ``` -判定顺序固定:**工单 → 自动发货任务 → 历史平台任务 → 人工**。当前库中三种载体没有重叠,顺序不影响结果;后续出现重叠必须告警,不能静默覆盖。 +判定顺序固定:**规则命中 → 载体证据(工单/自动发货任务)→ 无证据则 pending**。 +渠道值不再有 `legacy_platform`:断代归档后统一是 `archived`。 -决策服务在 `services/fulfillment/fulfillment-mode-service.ts`: +决策服务在 `services/fulfillment/channel-assign-service.ts`: -- `classifyFulfillmentMode()` —— 纯函数,供单测与文档对照; -- `decideOrderItemsFulfillmentModes()` —— 订单同步后批量落库; -- `reconcilePendingFulfillmentModes()` —— 24 小时兜底归集; -- `getFulfillmentModeCounts()` —— 计数核对。 +- `resolveChannelDecision()` —— 纯函数,规则与证据的优先级; +- `assignPendingOrderItemChannels()` —— 按水位游标批量判定; +- `assignChannelBySku()` —— 运营按 SKU 指定,写 `operator` 规则并锁定。 -### 6.2 回填 +### 6.2 断代与归档 -回填是**一次性脚本**,不塞进迁移:31 万行 `UPDATE` 放进迁移会拖慢部署,也无法在中途失败后按批重试。 +断代不再依赖独立回填脚本,执行 `finance:cutover-v2` 时: -```bash -npm run fulfillment:backfill -``` - -只填 `NULL`,可重复执行。判定 SQL: - -```sql -WITH decided AS ( - SELECT - oi.id, - CASE - WHEN EXISTS (SELECT 1 FROM work_orders wo WHERE wo.order_item_id = oi.id) - THEN 'work_order' - WHEN EXISTS ( - SELECT 1 FROM fulfillment_tasks ft - WHERE ft.order_item_id = oi.id AND ft.executor_key = 'affiliate_dash' - ) THEN 'affiliate_dash' - WHEN EXISTS ( - SELECT 1 FROM fulfillment_tasks ft - WHERE ft.order_item_id = oi.id - AND ft.executor_key IN ('kuaishou_ct_assisted', 'kuaishou_feifei') - ) THEN 'legacy_platform' - ELSE 'manual' - END AS fulfillment_mode - FROM order_items oi -) -UPDATE order_items oi -SET fulfillment_mode = d.fulfillment_mode, - fulfillment_decided_at = now(), - fulfillment_reason = 'backfill_2026_10_23' -FROM decided d -WHERE oi.id = d.id - AND oi.fulfillment_mode IS NULL; -``` - -> 上述计数是 2026-10-23 历史回填的结果,仅作迁移正确性参照;当前本地生产副本只跑了近 30 天增量,**不要用这些数字判断上线是否完成**。 -> 本地验收(2026-10-23 重写后重跑):`work_order` 118,053 / `manual` 102,955 / `legacy_platform` 53,901 / `affiliate_dash` 41,676,四项相加 = 316,585,`NULL = 0`。 +1. 把断代日之前的订单项批量置为 `fulfillment_status='archived'`、渠道清空、`source='cutover_archive'`; +2. 清空成本快照、异常、销售事实、总账等派生数据; +3. 写入 `finance_cutovers` 断代记录,防止重复执行。 ### 6.3 决策边界(写进代码注释与测试) -1. `fulfillment_mode` 允许短暂为 `NULL`;订单项创建时通常还没有工单/任务,不能强制非空。 -2. 上线后新订单项超过 **24 小时**仍为 `NULL`,进入异常队列;不是要求在创建瞬间就有值。 -3. 模式可以因业务改派而变化,但每次变化必须写原因与操作人,禁止静默 `UPDATE`。 -4. **商品来源不能反推履约方式。** 当前 11,349 个人工项指向 `tpl:*` 商品,就是典型反例。 +1. 新订单项默认 `pending`;由 `channel_assign` 管道在分钟级判定,不要求下单瞬间有渠道。 +2. 规则优先级:运营指定(10)> 自动发货(60)> 人工配置(70)> 默认;冲突会记 `routing_conflict` 异常。 +3. 被运营指定的订单项 `fulfillment_locked=true`,自动判定与回填都不得覆盖。 +4. **商品来源不能反推渠道**:`man:` 商品也可能由自动发货履约,判定只认规则与证据。 5. `voucher_state` 只做展示与核销流程,不参与任何成本/履约判断。 -6. 三种载体同时命中的订单项必须先告警人工复核;当前本地为 0。 +6. 多渠道证据同时命中必须先告警人工复核,不能静默取其一。 --- @@ -462,8 +433,8 @@ npm run revenue:backfill -- --batch-size=2000 --max-batches=100 | 索引 | 用途 | | --- | --- | -| `idx_order_items_fulfillment_mode (fulfillment_mode, order_id)` | 按履约方式筛订单项 | -| `idx_order_items_fulfillment_id (fulfillment_mode, id)` | 异常按履约方式半连接 | +| `idx_order_items_fulfillment_channel (fulfillment_channel, order_id)` | 按渠道筛订单项 | +| `idx_order_items_fulfillment_id (fulfillment_status, id)` | 管道按状态扫增量 | | `idx_order_items_canonical_product` | 按标准商品查订单项 | | `idx_order_items_normalized_sku_lookup` | 按归一名称/系列匹配映射 | | `idx_channel_product_mappings_match_lookup` | 映射查找 | diff --git a/docs/财务上线初始化与验收-2026-10-26.md b/docs/财务上线初始化与验收-2026-10-26.md index e1a2bbe8..25190dd8 100644 --- a/docs/财务上线初始化与验收-2026-10-26.md +++ b/docs/财务上线初始化与验收-2026-10-26.md @@ -103,7 +103,7 @@ SELECT COUNT(*) FILTER (WHERE oi.fulfillment_config_id IS NULL) AS unconfigured_items, COUNT(*) AS manual_items FROM order_items oi -WHERE oi.fulfillment_mode = 'manual' +WHERE oi.fulfillment_status = 'assigned' AND oi.fulfillment_channel = 'manual' AND oi.created_at >= (SELECT MIN(created_at) FROM manual_fulfillment_configs); -- 需要补配置的 SKU(按订单量排序) @@ -113,7 +113,7 @@ SELECT MAX(oi.sku_name) AS sku_name, COUNT(*) AS items FROM order_items oi -WHERE oi.fulfillment_mode = 'manual' +WHERE oi.fulfillment_status = 'assigned' AND oi.fulfillment_channel = 'manual' AND oi.fulfillment_config_id IS NULL AND oi.created_at >= (SELECT MIN(created_at) FROM manual_fulfillment_configs) GROUP BY 1 @@ -138,7 +138,7 @@ ORDER BY items DESC; -- 上线后人工项:已配置但未确认(客服待办) SELECT COUNT(*) AS pending_confirmed FROM order_items oi -WHERE oi.fulfillment_mode = 'manual' +WHERE oi.fulfillment_status = 'assigned' AND oi.fulfillment_channel = 'manual' AND oi.fulfillment_config_id IS NOT NULL AND oi.created_at >= :launch_at AND NOT EXISTS ( @@ -199,18 +199,18 @@ SELECT COUNT(*) FROM order_items WHERE revenue_basis = 'unknown'; -- 退款上 ```sql -- 1. 履约方式覆盖:四项相加 = order_items 总数,NULL = 0 -SELECT COALESCE(fulfillment_mode, '(null)') AS mode, COUNT(*) +SELECT COALESCE(channel, '(null)') AS channel, COUNT(*) FROM order_items GROUP BY 1 ORDER BY 2 DESC; -- 2. 上线后人工项必须有履约配置(允许配置跟进期) SELECT COUNT(*) FROM order_items -WHERE fulfillment_mode = 'manual' +WHERE channel = 'manual' AND created_at >= :launch_at AND fulfillment_config_id IS NULL; -- 3. 上线后人工项必须有成本快照(关账门槛) SELECT COUNT(*) FROM order_items oi -WHERE oi.fulfillment_mode = 'manual' +WHERE oi.fulfillment_status = 'assigned' AND oi.fulfillment_channel = 'manual' AND oi.created_at >= :launch_at AND NOT EXISTS ( SELECT 1 FROM cost_snapshots cs diff --git a/docs/财务与履约口径总纲-2026-10-23.md b/docs/财务与履约口径总纲-2026-10-23.md index b92ae249..efd324c5 100644 --- a/docs/财务与履约口径总纲-2026-10-23.md +++ b/docs/财务与履约口径总纲-2026-10-23.md @@ -1,6 +1,10 @@ # 财务与履约口径总纲(2026-10-23 重写版) -> 状态:**现行唯一口径**。本文是财务、成本、履约分类的总入口;其他文档与本文件冲突时,以本文为准。 + +> **历史文档(2026-10-31 前口径)**:文中 `fulfillment_mode` / `legacy_platform` 已随迁移 112–114 删除; +> 现行口径为 `order_items.fulfillment_channel + fulfillment_status`,销售事实维度为 `sales_daily_facts.channel`。 +> 本文保留用于追溯当时的决策,不再作为实现依据。 +> 状态:**已归档**。现行口径见《成本中心与履约模型》(2026-10-31 校准)与《成本中心API契约》。 > 最后校准:2026-10-26 > 代码基线:`dev`(合并 dev1023 + 人工成本完整性修复,迁移到 `111`) > 数据基线:§二/§七 的数字来自 2026-10-22 生产 dump 的**历史快照**,只用于理解历史缺口,**不再作为上线验收依据**; diff --git a/docs/财务系统深度分析-2026-10-27.md b/docs/财务系统深度分析-2026-10-27.md index d83cca6d..2c8da133 100644 --- a/docs/财务系统深度分析-2026-10-27.md +++ b/docs/财务系统深度分析-2026-10-27.md @@ -1,5 +1,9 @@ # order_site 财务系统深度分析(2026-10-27) + +> **历史文档(2026-10-31 前口径)**:文中 `fulfillment_mode` / `legacy_platform` 已随迁移 112–114 删除; +> 现行口径为 `order_items.fulfillment_channel + fulfillment_status`,销售事实维度为 `sales_daily_facts.channel`。 +> 本文保留用于追溯当时的决策,不再作为实现依据。 > 分析对象:订单履约、成本中心、客服备用金/积分、销售事实、影子总账、期间关账与平台对账。 > 代码基线:`dev`,HEAD `0de0fd7b`(迁移收敛到 `101–111`)。 > 文档基线:`docs/README.md` 标注的现行口径(2026-10-26)。 diff --git a/docs/财务系统集成方案.md b/docs/财务系统集成方案.md index dd7e8d1e..7aac1445 100644 --- a/docs/财务系统集成方案.md +++ b/docs/财务系统集成方案.md @@ -20,7 +20,7 @@ | 商品主数据 / 渠道 SKU 映射 / 成本中心 | **已实施 P0(`dev` 分支)**,迁移 `103/104`;标准商品 745 个,只覆盖自动发货 38 + 接单平台 707 | | `sales_daily_facts` / 订单级毛利 | **已实施 P0(`dev` 分支)**,迁移 `105`;94 天收入 ¥2,650.87 万、成本 ¥950.27 万 | | 人工成本录入(确认履约 → 扣积分/备用金 → 写快照) | **已实施 P0(`dev` 分支)**,迁移 `104/107`;2026-10-24 人工履约页下线、入口收敛到订单页;**无登记单、无审批、无报销** | -| 履约方式统一 `fulfillment_mode` | **已实施 P0(`dev` 分支)**;迁移 `105`,316,585 项已回填;订单页、成本快照、销售事实统一读该字段 | +| 履约渠道统一 `fulfillment_channel + fulfillment_status` | **已实施(2026-10-31,迁移 112–114)**;旧 `fulfillment_mode` 已删除,订单/成本/事实统一读渠道 | | `finance_accounts` / `finance_journal_entries` / `finance_journal_lines` | **已实施 P0(迁移 108)** | | `finance_periods` / `finance_reconciliations` / `finance_reconciliation_items` | **已实施 P0(迁移 108)** | | `finance_business_events` / `finance_settlements` | 未实施(当前用条目唯一键幂等) | @@ -93,7 +93,7 @@ 因此建议顺序是: ```text -统一履约事实(fulfillment_mode + 成本模型) +统一履约事实(渠道 + 成本模型) -> 商品主数据 + 渠道SKU映射 -> 成本中心(cost_items / cost_versions / cost_snapshots) -> 销售事实(sales_daily_facts) diff --git a/scripts/verify-finance-launch.sql b/scripts/verify-finance-launch.sql index 70c15269..310593ee 100644 --- a/scripts/verify-finance-launch.sql +++ b/scripts/verify-finance-launch.sql @@ -254,7 +254,7 @@ FROM ( SELECT count(*) AS v FROM sales_daily_facts WHERE stat_date >= current_date - 6 - AND fulfillment_mode <> 'manual' + AND channel <> 'manual' AND cost_missing_count > 0 ) s;