34 KiB
成本中心与三块履约模型
状态:现行(P0 已实施)。商品主数据、渠道映射、成本中心、成本快照运行层、销售事实与公司人工支出登记(含订单页逐单实际金额「人工录单」)已落地;轻量人工 case 与影子总账仍为后续方案。 最后校准:2026-10-23 代码基线:
dev @ a7f60414 + 财务 108–113数据基线:2026-10-17 生产库只读聚合(default_transaction_read_only=on,仅 SELECT,无写入) 实施顺序以 README.md §三 为准;本文负责“商品、成本、销售事实”的数据模型,不重复总账与页面设计。 关联:
〇、当前进度与边界
| 能力 | 状态 |
|---|---|
客服备用金支出(manual_expenses) |
已实施;只解决客服备用金余额与用途,不等于公司人工支出登记 |
| 商品主数据 / 渠道 SKU 映射 | 已实施 P0;迁移 103 + 匹配键修正 106,后台「商品与成本」页可批量初始化、维护与回填历史订单项 |
cost_items / cost_versions / cost_snapshots / cost_exceptions |
已实施 P0;迁移 104 + 修正 105,107 增加商品载体,108 增加业务时间与预估/实际替代 |
| 销售事实 / 订单毛利 | 已实施 P0;迁移 109,sales_daily_facts + 后台「销售毛利」页,未归一商品单独成行 |
公司人工支出登记(manual_expense_records) |
已实施 P0;迁移 111,支持登记/审批/驳回/报销;轻量人工 case 尚未实施 |
| 影子总账 | 未实施;必须在本文件的成本与销售事实之后 |
0.1 本期已实现边界
已实现:
product_catalog、channel_product_mappings,以及order_items.canonical_product_id/normalized_quantity/revenue_amount_fen/revenue_basis;- 按
provider + shop_id + platform_sku_code建映射,空shop_id为来源级映射、具体店铺优先;provider必须与orders.provider一致(91 为91kaquan,不是匹配层的kuaishou-91,服务层会归一别名); - 映射回填、SKU 文本数量回填与订单项收入固化(一单一品归整单,多品按等额拆分并标
equal_split); cost_items/cost_versions/cost_snapshots/cost_exceptions;- 成本版本优先级
rule > product > shop > provider > global,按paid_at/accepted_at等业务时间解析; - 订单项成本预览、预估快照、工单验收实际快照、缺失成本异常;
sync-cost-finance脚本与定时任务:批量回填快照并重算销售事实;sales_daily_facts与后台「销售毛利」页,未归一商品单独成行,成本缺失显式告警;- 后台「商品与成本」页、
finance.view/finance.config权限与审计。
本期不做:
- 订单进单时自动写
canonical_product_id/normalized_quantity(目前用后台回填); - 上游
cost_amount实时自动同步为cost_versions(目前用价格导入脚本); - 人工 case 在业务事务内自动生成成本快照;
- 退款按日冲减与退款金额字段;
- 影子总账。
一、先纠正一个口径:三块不是“订单来源”,是“执行载体”
同一个来源的订单,可能走自动发货、接单平台或人工。正确的拆分维度是订单项最终由谁履约:
| 块 | 执行载体 | 成本特征 | 监管重点 |
|---|---|---|---|
| ① 自动发货 | affiliate_dash 履约任务 |
固定成本,按皮肤/SKU,很少变化 | 上游成本、店铺/日期/皮肤销量 |
| ② 接单平台 | work_orders + 拼单份额 |
变动成本,按模板/单价/固定价/拼单 | 发单价格、打手提现、售后追缴、毛利 |
| ③ 人工客服 | 目前无载体 | 逐单实际金额(不按工时、不按固定单量) | 谁处理、何时完成、证据、成本、异常 |
硬不变式:
一个订单项 = 订单骨架 + 买家凭证 + 一个执行载体
执行载体 ∈ { affiliate_dash, work_order, manual }
当前代码对抖音有 snapshot.classification 守卫,但 91/PDD 仍然主要靠 isConfigured=false 隐式表达“人工”。这会导致人工单在系统里没有显式状态,也没有处理记录。
二、生产数据证据(2026-10-17 只读)
2.1 最近 30 天无载体订单
“无载体”定义:已支付订单项,既没有 fulfillment_tasks,也没有 work_orders。
| 来源 | 无载体订单数 | 无载体金额 | 说明 |
|---|---|---|---|
| 91 | 42,391 | 约 ¥624 万 | 几乎全部 snapshot.isConfigured=false |
| PDD | 2,258 | 约 ¥39 万 | 几乎全部 isConfigured=false |
| 抖音 | 8 | 约 ¥595 | 仅 8 单未命中,抖音分流已基本收口 |
补充:
- 2026-09 无载体订单项约 37,708;2026-08 约 46,322。这不是历史遗留,是持续发生。
manual_dispatch任务全库只有 6 条,全部为manual_review。- 这些无载体订单的
claim_status全部为active,没有完成信号。 - 已验收工单对应的订单,
orders.order_status仍然是paid:订单主状态不能代表履约完成。
结论:人工块不是少量兜底,而是每天上千单、金额很大的主业务块,但基本没有进入系统任务与状态机。
2.2 接单成本占比
最近 30 天已验收工单:
| 来源 | 工单数 | 打手报酬 | 订单金额口径 | 报酬/订单金额 |
|---|---|---|---|---|
| 91 | 60,902 | ¥235.9 万 | ¥274.4 万 | 86.0% |
| PDD | 1,289 | ¥20.3 万 | ¥25.4 万 | 80.0% |
| 抖音 | 478 | ¥2.14 万 | ¥2.21 万 | 96.5% |
注意:91 的 orders.total_amount 来自 maxAmount(91 给的可接受金额/结算口径),PDD 来自 order_amount,抖音来自 pay_amount。三者不是同一财务口径,不能直接相加当收入。
2.3 SKU 口径问题
最近 30 天,同一个商品名会对应多个 sku_code:
- 「指挥官密钥1个」:27 个
sku_code - 「1个精致荣耀专属礼盒」:10 个
- 「苏尔南钻石100颗」:35 个
因此:
- 不能直接按
sku_code做“什么皮肤卖了多少”; - 必须先建
product_catalog+channel_product_mappings,把平台 SKU 归一到canonical_product_id; - 数量也不能只读
order_items.quantity,要从 SKU 文本解析(如「指挥官密钥5个」)。
2.4 当前订单结构
最近 30 天,三个来源的订单基本都是 1 个订单 1 个订单项。这让第一版销售统计可以做简化,但模型必须按“订单项”设计,不能把“一单一品”写死。
三、统一商品维度:先解决“什么皮肤”
3.1 商品主数据
product_catalog
id
canonical_sku -- 内部标准编码,统计/成本/报表唯一口径
product_name -- 展示名
category -- 皮肤/礼包/密钥/改名卡/其他
game -- 和平精英等
carrier_type -- 默认履约载体:affiliate_dash / work_order / manual / shared
status
attributes_json
carrier_type(迁移 107_product_catalog_carrier.sql)是后台「商品与成本中心」的分区维度:自动发货 / 接单平台 / 人工 / 共用。回填口径(2026-10-22 修正):只把真正走 affiliate-dash 执行器的商品算作 affiliate_dash——以 fulfillment_tasks.executor_key='affiliate_dash' 的履约证据为准,按 context_json.skuCode 建标准商品(aff:<skuCode>,生产快照 41,676 条任务 / 28 个 SKU);接单模板初始化出来的 tpl:* 商品全部是 work_order(662 个)。渠道映射只代表平台 SKU 能归一到标准商品,不代表自动发货。自动发货是单一上游渠道,订单项直接取最新 fulfillment_tasks.context_json.skuCode 归一为 aff:<skuCode>,不写 channel_product_mappings;页面「回填自动发货 SKU」会回填 order_items.canonical_product_id 并自动重算受影响日期的销售事实。人工/共用由后台按需手工调整;新增自动发货 SKU 可在页面点「同步自动发货商品」补建。
上游自动发货商品价格用 apps/backend/scripts/sync-affiliate-dash-prices.ts 批量导入(价格清单见 apps/backend/scripts/data/affiliate-dash-prices.json):按 aff:<skuCode> 补建/更新商品,并给指定成本项生成 scope=product 的按件版本;金额取上游 cost_amount(分),价格变化时先停用旧版本再插入新版本,历史快照不变。首次导入历史基线用 --effective-from=2026-07-10,脚本会把「金额相同但生效时间更晚」的版本回填到该日期。示例:npx tsx scripts/sync-affiliate-dash-prices.ts --cost-item-id=5 --effective-from=2026-07-10。
接单平台与人工客服各有一个系统成本项(迁移 110_system_cost_items.sql):
sys_work_order_reward:662 个接单模板各生成一条规则级成本版本(634 条固定发单金额 + 28 条按件发单单价),全局 0 元版本只作「按工单实际结算」的占位。已验收工单按work_orders.reward_amount写actual;未验收但已确定报酬的工单先按同一金额写estimated(metadatasource=work_order_reward_committed),验收后升级为actual;取消工单不计成本。本地全量已回填 95,870 条actual+ 21,217 条estimated,其中 111,831 条快照已关联到对应模板成本版本。sys_manual_service:人工支出登记通过后按登记金额写actual快照;成本版本先为draft占位,不参与预估,但允许人工支出使用实际金额入账。财务填写正式按单单价后可将版本改为active。
3.2 渠道 SKU 映射
channel_product_mappings
id
provider -- 落库值:91kaquan / pdd / douyin_mashida
shop_id -- 空串 = 来源级;具体店铺优先
platform_sku_code -- 平台原始 sku_code / productNo / code
platform_sku_name
match_type -- sku_code / sku_code_name / sku_code_series / sku_name_series
platform_sku_name_key -- 归一后的精确名称或数量系列键
canonical_product_id
quantity_multiplier -- 数量已在归一数量中体现,系列 SKU 固定为 1
confidence -- exact / rule / manual
source -- manual / import / rule
enabled
匹配优先级:
店铺 + SKU 编码
> 来源 + SKU 编码
> 店铺 + SKU 编码 + 精确名称
> 来源 + SKU 编码 + 精确名称
> 店铺 + SKU 编码 + 名称系列
> 来源 + SKU 编码 + 名称系列
> 店铺 + 名称系列
> 来源 + 名称系列
匹配不到的商品进入 product_mapping_missing 异常队列,不能直接拿原始 sku_code 当报表维度。
3.2.1 接单模板批量初始化(2026-10-18 修正)
662 个接单模板不允许逐个手工建商品。后台「从接单模板初始化」按以下规则批量生成:
- 每个启用模板生成 1 个标准商品,内部编码
tpl:<rule_key>;暂时没有历史工单的模板也先建商品。 - 91 与快手 send-code 的历史工单直接命中模板,以
work_orders.product_rule_id作为映射的第一手证据,不再只看原始sku_code。 - 同一
provider + shop_id + sku_code只命中一个模板时,生成sku_code精确映射。 - 同一编码被多个模板复用时,继续用“编码 + 归一名称系列”和“编码 + 精确名称”消歧;只有唯一归属才生成映射。
- 同一编码 + 同一名称在历史上仍命中多个模板时,不选最新、不按次数猜;跳过并列入冲突清单,前端在初始化预览中展示候选模板与涉及订单项数。
- 同一来源 + 店铺内名称系列唯一时,额外生成
sku_name_series兜底,供后续出现新编码时使用;它永远排在编码类映射之后。 - 数量系列(1个/2个/5个/10个,秘钥/密钥等等价写法)归为同一个标准商品;数量写入
order_items.normalized_quantity,不能把“5个”再写成quantity_multiplier=5,否则会重复乘算。 - 重跑初始化时只清理
source='import'且备注为“接单模板 … 自动初始化”的映射,并重置由这些映射带出的订单项canonical_product_id;人工映射、成本版本、快照和真实订单/工单不删除。
本地生产快照验证结果(2026-10-18):
- 启用模板 662 个,自动生成/复用标准商品 662 个;
- 历史工单证据 112,451 个订单项;
- 分层映射 4,311 条,其中编码精确 2,302、编码 + 精确名称 325、编码 + 名称系列 160、名称系列兜底 1,524;
- 可唯一归一 86,533 个有证据订单项;另有 25,918 个订单项属于 32 组真实冲突,保持未映射;
- 全库实际归一 103,143 个订单项(包含可由名称系列兜底命中的无工单订单项)。
3.3 店铺维度
shop_id 是稳定键;shop_name 会变。报表以 shop_id 为准,展示名取最新或按时间快照。
四、统一成本中心
4.1 当前成本散落点
| 成本 | 当前来源 | 问题 |
|---|---|---|
| 自动发货采购成本 | affiliate-dash 商品接口 cost_amount |
已落库:订单项按 aff:<skuCode> 归一,写 estimated 快照并在销售事实/成本版本页展示(2026-10-22) |
| 工单报酬 | work_product_rules.reward_amount / unit_price_fen / sharing_unit_reward |
价格与匹配规则混在一张表,改价无版本 |
| 工单实际成本 | work_orders.reward_amount、share_reward |
实际值有了,但没有统一成本项/版本 |
| 人工成本 | 订单界面「人工录单」→ manual_expense_records + sys_manual_service 快照 |
已落地逐单实际金额;不按小时、不按固定单量/固定单价;未录入的订单项留在成本缺失队列 |
| 平台费/支付费/售后损失 | 分散或未建模 | 无法算真实毛利 |
4.2 成本类型
| 类型 | 说明 | 典型单位 |
|---|---|---|
auto_purchase |
自动发货上游采购成本 | 按件 |
work_order_reward |
接单平台打手报酬 | 按件/按单/按份 |
manual_service |
人工客服成本 | 逐单实际金额(不按工时) |
platform_fee |
平台服务费/佣金 | 按单/按比例 |
payment_fee |
支付通道费 | 按单/按比例 |
after_sales_loss |
售后损失/赔付 | 按单 |
shipping_other |
其他履约费用 | 按单 |
4.3 成本表
cost_items
id
cost_key
cost_type
name
unit -- per_item / per_order / per_hour / percent
currency
status
cost_versions
id
cost_item_id
amount_fen -- 固定金额;percent 类型用 rate_bps
rate_bps
effective_from
effective_to
scope_type -- global / provider / shop / product / rule
provider
shop_id
canonical_product_id
work_product_rule_id
source -- manual / upstream_sync / import
status -- draft / active / retired
change_reason
created_by
approved_by
4.4 成本快照
cost_snapshots
id
order_id
order_item_id
work_order_id
manual_case_id
cost_item_id
cost_version_id
cost_type
quantity
unit_amount_fen
total_amount_fen
snapshot_stage -- estimated / actual / adjustment
resolved_at
metadata_json
规则:
- 改价只新增
cost_versions,不覆盖历史; - 订单/工单/人工单创建或完成时写
cost_snapshot; - 匹配不到成本时写
cost_missing,不能默认 0; - 历史成本永远从快照读,不从当前成本表倒推。
4.5 “只改一个地方”的过渡方案
目标状态:
- 金额只在
cost_versions改; work_product_rules只保留匹配条件 + 计价方式 +cost_item_id;channel_product_mappings引用自动发货cost_item_id;- 人工成本引用
manual_service成本项; - 业务表只保存
cost_snapshot_id。
过渡:
- 给
work_product_rules增加可空cost_item_id; - 有
cost_item_id走成本中心; - 没有则回退旧
reward_amount/unit_price_fen,标记legacy_cost_source; - 659 条规则逐步迁移;
- 全部迁移后旧字段变只读/删除。
这样不会一次性打断发单,又能逐步做到“成本改动只在一个地方”。
五、销售事实与店铺日报
5.1 销售事实表
sales_daily_facts
stat_date -- Asia/Shanghai,按 paid_at 归属
provider
shop_id
canonical_product_id -- 允许为空:未归一桶
carrier_type -- affiliate_dash / work_order / manual / shared / unknown
order_count
item_count
quantity -- 归一化件数(SKU 文本解析后)
gross_revenue_fen
refund_amount_fen
net_revenue_fen
cost_amount_fen
cost_missing_count
cost_stage -- missing / partial / estimated / actual
gross_profit_fen
5.2 三个必须统一的字段
- 商品:
canonical_product_id,不是原始sku_code;为空表示未归一,报表单独成行,不能丢。 - 数量:
normalized_quantity,不是简单的order_items.quantity。 - 收入:
revenue_amount_fen+revenue_basis,不是直接orders.total_amount。一单一品归整单;多品当前按等额拆分,余数给最后一项。
收入口径(2026-10-22 已确认):
| 来源 | 当前字段 | 口径 |
|---|---|---|
| 91 | orders.total_amount |
用户实付,已与快手 paymentFen 核验一致 |
| PDD | orders.total_amount |
用户实付 |
| 抖音 | orders.total_amount |
用户实付 |
5.3 退款日期
现在只有 pay_status=refunded 和 updated_at,不足以做按日净销售。sales_daily_facts 已预留 refund_amount_fen,但退款逻辑仍未接入;后续补 refunded_at / refund_amount_fen 或退款事件表。
5.4 统计实现
当前是增量重算表,不是物化视图:
orders + order_items + channel_product_mappings + cost_snapshots
-> sales_daily_facts(按日期范围 DELETE + INSERT)
后台入口:/admin/sales-profit。回填脚本:npm run finance:sync -- --days=14 --mode=all;定时任务 cost-finance-sync 每 5 分钟处理最近 7 天。成本缺失会写入 cost_missing_count,毛利在补齐成本前只作参考。
数据校验:本地 2026-07-10 ~ 2026-10-14 共 316,597 个已支付订单,其中 12 单没有订单项(¥2,480.60),无法归集到商品/成本,销售事实会排除并在页面单独提示;其余 316,585 个订单项收入与订单总额完全对平。
全量回填后(2026-07-10 ~ 2026-10-10):接单平台 95,870 条 actual(¥3,773,401.32)+ 21,217 条 estimated(¥754,912.45);自动发货 41,676 条 estimated(¥4,974,059.00)。自动发货与接单平台收入归一率均为 100%,未归一收入只剩人工块 145,507 项 / ¥15,253,660.86。接单平台仍有 966 个订单项没有成本快照:601 个已取消工单 + 365 个零报酬未验收工单,保留在成本异常队列等业务确认,不自动记 0。人工客服仍因缺少商品与正式单价显示成本缺失。
六、自动发货块
订单项 -> 最新 fulfillment_task.context_json.skuCode
-> product_catalog.canonical_sku = 'aff:<skuCode>'
-> 上游商品成本 cost_amount -> cost_snapshot
-> 履约完成 -> sales_daily_facts
要点:
- 不建渠道映射:自动发货只有 affiliate-dash 一个上游渠道,订单项直接从履约任务 SKU 归一;
channel_product_mappings只服务接单平台。 - 回填入口:后台「自动发货 SKU」→「回填自动发货 SKU」,接口
POST /api/v1/admin/cost-center/affiliate-products/backfill。按order_item_id取最新任务 SKU,只更新有变化的订单项,完成后自动重算受影响日期的销售事实;返回updatedCount / skippedCount / remainingCount。 - 列表的「履约任务 / 最近履约」实时取自
fulfillment_tasks,不读会被价格导入覆盖的product_catalog.attributes_json。 - 上游商品接口已有
price_amount/cost_amount,价格由sync-affiliate-dash-prices.ts落到成本中心。 price_amount是售价/积分,cost_amount是成本,不能混用。- 成本变化生成新版本;变动超阈值告警/审批。
- 店铺/日期/皮肤统计必须走
canonical_product_id,不能按原始sku_code。 - 统计件数要解析 SKU 文本中的数量。
实测(本地 2026-10-22):41,676 个自动发货订单项全部回填,未归一收入 ¥5,089,712.26 → ¥0.00;采购成本 ¥4,974,059.00(毛利率 2.27%)。整体未归一收入占比 79.67% → 57.54%,剩余 100% 来自人工块(145,507 项 / ¥15,253,660.86),需要业务先确定人工 SKU/商品口径。
七、接单平台块
订单项 -> 命中接单模板 -> work_order
-> 创建/发布/抢单/验收 -> 工单报酬 -> cost_snapshot
-> 押金冻结/释放/没收 -> 影子总账(负债,不直接进成本)
-> 提现/售后/欠款 -> 影子总账
要点:
- 工单报酬是主成本;押金是负债,不是成本。
- 模板改价必须生成
cost_versions,不能直接改历史工单。 - 工单实际结算金额写入
cost_snapshot(actual);未验收但已确定报酬的工单先写actual以外的estimated,验收后升级。 - 商品归一入口:后台「接单平台」→「按工单回填商品」,接口
POST /api/v1/admin/cost-center/work-order-products/backfill。优先按工单真实使用的work_orders.product_rule_id指向tpl:<rule_key>;仍无法归一的 341 个长尾订单项,再按provider + shop_id + sku_code + normalized_sku_name自动补 45 个wo:标准商品和 115 条sku_code_name渠道映射。两次回填后接单平台归一率 100%。 - 成本版本入口:后台「接单平台」→「成本版本」→「同步模板发单金额」,接口
POST /api/v1/admin/cost-center/work-order-template-costs/sync。662 个模板各生成一条规则级版本(634 固定金额 + 28 按件单价),页面同时显示该模板的「用户实付均价」,用于对比用户付款与发单金额;全局 0 元版本只作实际结算占位。 - 成本异常:966 个订单项没有成本快照(601 个已取消工单 + 365 个零报酬未验收工单),保留在异常队列;取消/零报酬不等于 0 成本,不自动写 0。
- 删除模板:历史成本版本会脱钩并停用,
cost_snapshots继续引用原版本,保证历史毛利可追溯;不会因为外键把模板卡死。 - 影子总账负责收入/成本/押金/提现/售后的复式记录;成本中心负责“金额是多少、用了哪个版本”。
本地实测(2026-10-22):接单平台 118,053 个订单项全部归一;未归一收入 ¥775,641.19(14.54%)→ ¥0.00。成本 95,870 条 actual(¥3,773,401.32)+ 21,217 条 estimated(¥754,912.45),合计 ¥4,528,313.77;销售收入 ¥5,335,734.45,毛利率 15.13%。成本版本从 39 条增至 701 条(38 自动发货商品 + 662 模板规则 + 1 全局占位)。
生产数据提示:91/PDD/抖音已验收工单的报酬/订单金额分别约 86%/80%/96.5%。在收入口径未确认前,这个比例只能作为量级参考,不能直接当毛利率。
八、人工客服块(当前最需要补的)
口径确认(2026-10-17):业务明确人工块当前不以 SLA 监管为目标,第一优先级是 人工支出可追溯 ——「主要是把流程搞清楚:哪个人处理了哪些东西、付了多少钱。现在人工完全监管不到, 很多地方需要人工付款,也不知道付款项目、金额、订单号」。 因此本章顺序调整为:① 人工支出登记 → ② 处理人可见(轻量 case) → ③ SLA 与监管指标(延后)。 DDL 见 drafts/2026-10-17-人工支出登记-DDL草案.sql。
2026-10-22 进度:客服备用金支出已实施(
fund.manual_expenses),解决“客服手里有多少钱、这笔钱花到哪”; 公司口径的人工支出登记(manual_expense_records)也已实施(迁移111),并在订单界面提供「人工录单」逐单录入实际成本; 后者通过fund_expense_id关联前者,不重复做客服余额系统。轻量人工 case 仍未实施。
8.1 问题本质
不是“客服效率低”,而是人工单没有进入系统状态机,且人工付款完全没有台账:
- 没有 case;
- 没有处理人;
- 没有开始/完成时间;
- 没有交付证据;
- 没有人工成本;
- 订单完成状态无法回写;
- 付款项目、金额、关联订单号、付款人、凭证全部缺失 —— 这是当前最直接的损失点。
8.2 载体判定
建议在订单项上增加显式字段:
carrier_type: affiliate_dash / work_order / manual
carrier_status: pending / active / completed / failed / cancelled
carrier_decided_at
carrier_reason
判定:
- 命中自动发货 ->
affiliate_dash - 命中接单模板 ->
work_order - 都没有 ->
manual - 宽限期后仍无载体 -> 自动创建人工 case
8.3 人工 case 模型
落地版见 drafts/2026-10-17-人工支出登记-DDL草案.sql:已去掉
due_at/优先级等 SLA 字段,先只保留「谁在处理哪个订单项 + 结果 + 证据 + 支出登记」。下表为完整形态(阶段③)。
manual_fulfillment_cases
id
case_no
order_id
order_item_id
provider
shop_id
canonical_product_id
quantity
priority
status
assignee_admin_user_id
assigned_at
due_at
started_at
completed_at
outcome -- delivered / refunded / failed / cancelled
delivery_reference
evidence_json
cost_snapshot_id
notes
manual_case_events
id
case_id
event_type
actor_type
actor_id
payload_json
created_at
状态:
pending_assign -> assigned -> processing -> waiting_customer
-> pending_review -> completed / failed / cancelled
8.4 监管规则(阶段③,延后)
- 每个无载体订单必须有 case;
- 完成必须填 outcome + 交付凭证/参考号;
- 失败必须填原因并决定重试/退款/升级;
- 高价值订单主管复核;
- 超时自动升级;
- 所有状态变更写
manual_case_events,不可改; - 客服不能私下改价/退款,必须走审批。
8.5 人工成本(已落地:逐单、按实际金额)
业务口径(2026-10-22 确认):人工成本不能按小时,也不能按固定单量/固定单价——每一单的实际付款都不一样。 必须先关联订单号,再逐单录入这一单实际付出去的钱。 资金来源(2026-10-23 确认):人工支出实际上都走客服备用金,登记时必须选择备用金账户;客服角色只能用自己的账户(默认自动带入),管理员/财务可代录到指定客服账户。
- 入口在订单界面:订单列表对人工单显示「人工录单」,订单详情始终有「人工录单」;弹窗锁定订单号、选择备用金账户,订单项多于 1 个时由人工选择。
- 接口
POST /api/v1/admin/finance/manual-order-costs:同一事务内扣减备用金余额、写fund_entries与manual_expenses,再写manual_expense_records(成本项固定sys_manual_service,fund_expense_id必填),金额即本单实际支出;余额不足直接拒绝。 - 有审核权限(
admin/finance/finance.review)且选择立即生效时,当场审批并写actual快照;否则留在submitted,由财务在「人工支出」页审核。 - 快照以
subject_type='manual_case'、subject_id=expense.id写入并携带order_item_id;销售事实按cost_snapshots.order_item_id汇总,同一订单项的多笔支出(渠道费 + 加急费 + 补差价)累加。 - 工时只作为后续可选的分析字段,不参与成本计算;不设「按单固定单价」。
- 未录入成本的订单项保持
cost_missing,进入成本异常队列,不默认记 0。
8.6 监管指标(阶段③,延后)
- 待处理量、平均等待、最长挂起;
- 客服处理量、平均处理时长、完成率;
- 退款率、投诉率、返工率;
- 单均人工成本;
- 无凭证完成率;
- 同一买家/客服异常重复;
- 高价值订单抽样复核。
8.7 与现有任务系统的关系
现有 manual_dispatch 任务只有 6 条,且缺少 assignee/SLA/证据/成本字段。建议:
- 新人工 case 作为主模型;
- 如果存在对应
manual_dispatch任务,完成时桥接现有完成接口; - 不把所有人工作业强行塞进
fulfillment_tasks。
九、三块统一数据流
flowchart LR
O["订单项"] --> C["载体判定"]
C -->|命中自动| A["affiliate_dash 任务"]
C -->|命中模板| W["work_order"]
C -->|都没有| M["manual_case"]
A --> CS["cost_snapshots"]
W --> CS
M --> CS
CS --> F["sales_daily_facts / 毛利"]
A --> L["影子总账"]
W --> L
M --> L
P["product_catalog + channel_product_mappings"] --> F
V["cost_items + cost_versions"] --> CS
十、分期落地
P0:商品与销售事实(先能做报表)——已实施
product_catalog(迁移103/107)channel_product_mappings(迁移103/106)sales_daily_facts(迁移109,日期范围重算表)- 数量归一、收入固化;退款日期字段仍待补
P1:成本中心影子期——已实施运行层
cost_items/cost_versions/cost_snapshots(迁移104/105/108)- 自动发货接固定成本与价格导入
- 接单平台:系统成本项
sys_work_order_reward在工单验收后按reward_amount直接写实际快照 - 人工客服:系统成本项
sys_manual_service+ 人工支出登记通过后写实际快照;正式单价版本待财务填写 - 缺失成本进异常队列
P2:公司人工支出登记 + 轻量处理记录——支出登记已完成,轻量 case 未实施
- 载体判定(
carrier_type) - 无载体自动建轻量 case(谁在处理哪个订单项)
- 公司人工支出登记:付款项目 / 金额 / 订单号 / 付款人 / 收款人 / 付款方式 / 凭证 / 审批
- 通过
fund_expense_id关联客服备用金支出,避免重复记账 - 人工支出进成本快照与影子总账(借:履约成本-人工,贷:其他应付-垫付人)
- 延后(阶段③):SLA、超时升级、主管复核阈值、监管指标
P3:影子总账
- 订单收入、工单成本、押金、提现、寄卖、售后
- 日对账、差异清单
- 连续对平后转正
十一、业务口径确认结果(2026-10-17)
11.1 已确认
| # | 问题 | 结论 | 依据 |
|---|---|---|---|
| 1 | 91 的 orders.total_amount 是收入还是成本上限? |
收入:total_amount 就是用户付款金额,与快手发码配合,计入收入 |
业务确认;数据核验:91 已支付 312,569 笔中 235,271 笔含快手 paymentFen,其中 235,268 笔与 total_amount 完全一致(100.00%),仅 3 笔差 ¥1.80~2.00 |
| 2 | 打手报酬是成本还是代收代付? | 支出/成本:工单结算后打手会提现,我方实际付款给他们 → 按总额法(收入记用户实付全额,报酬记履约成本) | 业务确认 |
| 3 | 人工块的目标是什么? | 先做流程可见与支出可追溯(谁处理了哪些、付了多少钱、什么项目、关联订单号、凭证),SLA/监管指标延后 | 业务确认 |
| 4 | 退款/仲裁怎么冲减? | 暂未接入,先预留:字段、反向冲正关系与事件类型先留好,本轮不实现逻辑 | 业务确认 |
| 5 | 收入确认时点? | 2026-10-22 确认:先按 paid_at 归属销售日;成本在快照生成后回填到同一销售日,后续再评估验收/售后期口径 |
业务确认 |
| 6 | 未归一商品怎么进报表? | 2026-10-22 确认:允许 canonical_product_id 为空,单独成“未归一”行,保证总收入与订单总额对平 |
业务确认 |
| 7 | 人工成本怎么归集? | 逐单、按实际金额:关联订单号录入本单实际付款,不按小时、不按固定单量/固定单价;同一订单项多笔可累加 | 业务确认(2026-10-22) |
11.2 仍待确认
- 自动发货的
cost_amount是否稳定、是否含平台积分/汇率? - 平台服务费/支付通道费由谁承担、结算周期与账单口径?
- 寄卖货款的采购成本确认时点?
十二、分析基线与克隆现状(2026-10-17 更新)
生产只读聚合(SET default_transaction_read_only = on)已完成,无写入、无改表、无改数据。
现有基线:本机已存在可反复使用的生产快照克隆(本地 docker order_site-postgres-1,orders 316,597 行,数据截至 2026-10-14),
因此后续 SQL 迭代一律打本地克隆,不必再连生产;需要更新时再取新快照。
本机备份目录已收敛:backups/ 及子目录 700、两份 dump 600,并删除了一个残留临时 dump(137 MB)。
如需长期保留留档,建议只保留一份最新 dump,并确认磁盘加密。
十三、一句话结论
自动发货解决“固定成本 + 店铺/日期/皮肤销量”,接单平台解决“变动成本 + 影子总账”,人工客服解决“支出可追溯(谁付的、什么项目、多少钱、哪个订单)+ 处理人可见 + 人工成本”;三者共用一套商品主数据、成本中心和销售事实表,成本改价只发生在 cost_versions 一个地方。