Files
order_site/docs/项目全流程与数据库设计梳理-2026-10-23.md
T

52 KiB
Raw Blame History

order_site 全流程与数据库设计梳理(2026-10-23)

状态:梳理稿(待评审,尚未登记到 docs/README.md) 落笔日期:2026-10-23 代码基线:工作区当前 HEAD;数据库迁移 001–103(共 103 个文件,本地库 schema_migrations 记录 103 条,已全量应用) 核对方式:源码走查 + 本地 PostgreSQL 实读表结构 / 约束 / 索引 + 本地库状态与数据量统计 说明:本地库与生产同量级(orders 约 37.5 万行),本文引用的数据量均为本地库实测,不与生产承诺一致,仅用于判断结构合理性。


一、先看结论(10 条)

  1. 系统早已不是「快手自动发货工具」,而是三来源订单履约 + 打手众包接单 + 寄卖仓库 + 电子凭证核销 + 客服备用金台账的一体化平台。
  2. 订单来源只有三个:kuaishou-91(91 回调 + 快手 send-code 两条通道,同一个来源)、pdd、douyin-mashida。代码层的来源键定义在 apps/backend/src/services/product-match/types.ts。
  3. 核心不变式:一个订单 = 骨架(orders + order_items)+ 买家凭证(快手电子凭证 / 抖音券)+ 一个执行载体。载体只有三种:affiliate_dash 自动发货、work_orders 工单、manual 人工(当前无显式表,靠「无任务且无工单」判定)。
  4. 履约任务按「订单项 × 数量」拆单元:fulfillment_tasks 的 UNIQUE(order_item_id, unit_index) 是整套领取链接体系的基座。
  5. 领取链接 = claimLinkBase + '/' + claim_tokens.token,一任务一 token(claim_tokens.task_id 唯一);CLAIM_TOKEN_TTL_HOURS=0 表示永久有效。
  6. 打手侧是一条独立业务线:工单状态机 pending_material → unassigned → open → in_progress → pending_acceptance → accepted,另有 problem、cancelled;资金走「钱包 + 流水」模型,不是会计总账。
  7. 寄卖是第三个业务面:提报 → 审核入库(INV…)→ 规格匹配(系统/区服/系列/钻级/璀璨)→ 履约(内部现货 or 指派打手)→ 结算与撤销押金策略。
  8. 备用金台账(迁移 100)已落地:fund_accounts(主管池 / 客服账户)+ fund_entries(只追加流水,余额只能由流水变动)+ manual_expenses + fund_reconciliations。
  9. 数据库现状:60 张业务表 + schema_migrations,113 个外键、21 个索引的 work_orders 是全库索引最多的表;配置不入表结构,统一存 app_config_entries(本地 18 个配置键)。
  10. 主要架构性风险仍是 2026-10-17 报告列的那几处:资金门禁只在前端、Webhook 先落库后处理、/collect /redeem 无第二因子、抢单/指派锁序相反、单实例依赖进程内存。

二、系统地图

2.1 运行拓扑

flowchart LR
  U1["外部平台<br/>91卡券 / 快手 / 拼多多 / 抖音码速达"] --> C
  U2["买家<br/>领取页 / H5 / 兑换页"] --> C
  U3["打手<br/>worker.khhao.com"] --> C
  U4["后台运营 / 客服 / 财务"] --> C
  C["Caddy :80/443<br/>反代 + 静态缓存 + 压缩"] --> F["frontend<br/>React 19 + Vite(一套产物三个入口)"]
  C --> B["backend<br/>Express 5 + TS<br/>routes → services → repositories → pg"]
  B --> P[("PostgreSQL 16")]
  B --> O["对象存储<br/>生产 OSS / 本地 MinIO"]
  B --> X["外部 API<br/>affiliate-dash / 快手 OpenAPI / 抖音码速达 / PDD"]
  B --> M["进程内存<br/>限流桶 / 调度器 / SSE / 列表缓存"]

单实例假设写在代码里:限流、定时任务、SSE 订阅、列表缓存全部在进程内存(middleware/rate-limit.ts、services/realtime/),扩容前必须先外置。

2.2 后端入口与路由挂载

入口 apps/backend/src/app.ts:

挂载点 说明
/health、/health/live、/health/ready 健康检查;未 ready 时其余请求统一 503
/api/v1/open/douyin-mashida 必须在全局 body parser 之前挂载,签名基于原始 JSON 字符串
/api/v1/open/affiliate-dash 发货平台异步通知(验签 + 时间戳容差)
/api/v1/open/91 91 卡券异步下单 / 查询(验签)
/api/v1/open/kuaishou-industry 快手行业电子凭证 send-code / destroy / query / consume
/api/v1/redeem 抖音券兑换页接口
/api/v1/claim 买家领取页(token 与 H5 两种入口)
/api/v1/collect 待补全订单资料收集
/api/v1/pdd/extension 拼多多浏览器扩展上报 Cookie / 公钥
/api/v1/files 文件对象读取
/api/v1/worker 打手端(公开鉴权路由 + 会话路由)
/api/dcauth 设备认证(dcauth 协议)
/api/v1/admin 后台 API(auth 路由放行,其余全部 requireAdminSession)
/s 短链跳转

2.3 分层与模块职责

层 目录 职责
路由 src/routes/** 参数解析、限流、鉴权、调用 service
领域 src/domain/** 状态机与纯策略(任务、工单、寄卖、押金、验收模式)
服务 src/services/** 订单、履约、工单平台、平台对接、财务、寄卖、调度
仓储 src/repositories/** 全部 SQL(含 worker-platform/* 大仓储)
迁移 src/db/migrations 001 基线 + 102 个增量
前端 apps/frontend/src/pages/** admin 后台 / worker 打手端 / claim 领取页 / redeem 兑换页 / collect 补资料

三、端到端主流程

3.1 总览(一条单的完整生命周期)

flowchart TD
  A["来源进单<br/>91 / PDD / 抖音"] --> B["upsertOrderFromSource<br/>advisory lock 去重 + 状态合并"]
  B --> C["order_items 落库<br/>resolveOrderItemForFulfillment 判定载体"]
  C --> D{"isConfigured?"}
  D -->|否| E["待补全<br/>91 返回 orderStatus=10"]
  C -->|是| F["syncDeliveryTasksForOrder<br/>按 order_item × quantity 建任务单元"]
  F --> G{"executor_key"}
  G -->|affiliate_dash| H["自动发货<br/>建 affiliate-dash 单 + 领取链接"]
  G -->|manual_dispatch| I["人工履约<br/>manual_review 待办"]
  C --> J["syncWorkerOrdersForSourceOrder<br/>product-match 匹配接单模板"]
  J -->|命中且 auto_create| K["work_orders<br/>待补素材 / 待发布"]
  K --> L["打手大厅抢单 / 后台指派"]
  L --> M["代练履约 → 提交验收"]
  M --> N["验收 → 结算报酬 / 释放押金 / 等级进度"]
  H --> O["买家领取页 claimUrl<br/>填 UID → 绑定 → 提交发货"]
  O --> P["affiliate-dash delivered 回调"]
  P --> Q["任务 completed / redeemed<br/>快手电子凭证核销闭环"]
  N --> R["钱包流水 + 售后 / 欠款"]
  Q --> S["后台报表 / 备用金台账 / 审计"]
  R --> S

3.2 三个来源的进单通道

sourceKey(业务口径) 实际 provider / platform 入口 数据形态 备注
kuaishou-91 91kaquan / kuaishou POST /api/v1/open/91/orders/create 一单一条,productNo 解析出商品名与店铺 ID 91 是订单骨架权威源
kuaishou-91 91kaquan / kuaishou POST /api/v1/open/kuaishou-industry/send-code 快手电子凭证补全/兜底 与 91 共享店铺键空间,不是新来源
pdd pdd / pdd 定时任务 pdd_order_sync(20s)拉取 两段式 pdd_raw_orders → orders 浏览器扩展上报 Cookie
douyin-mashida douyin_mashida / doudian POST /api/v1/open/douyin-mashida/message 消息推送,先落 webhook_events 去重 抖店订单 + DYC 券

本地库实测分布:91 368,355 单;PDD 5,563 单;抖音 1,541 单。

3.2.1 91 卡券(下单 + 查询)

sequenceDiagram
  participant K as 91卡券
  participant API as backend /open/91
  participant S as order-service
  participant DB as PostgreSQL
  K->>API: POST /orders/create(验签)
  API->>API: buildOpen91SourceEvent<br/>provider=91kaquan, platform=kuaishou<br/>totalAmount=maxAmount(分)
  API->>S: upsertOrderFromSource(event)
  S->>DB: pg_advisory_xact_lock(hashtextextended('provider|platform|orderNo'))
  S->>DB: upsert orders + replace order_items
  S->>S: 载体判定 → 建任务 / 建工单
  API-->>K: orderStatus=10(未生成 claimUrl)或成功
  K->>API: POST /orders/query
  API-->>K: orderStatus=20 + cards(claimUrl 加密卡密)<br/>或 10(处理中)/ 30(失败)

要点:

  • 订单幂等键是 UNIQUE(provider, platform, shop_id, platform_order_id),事务内先取 advisory lock,避免并发重复建单。
  • 状态合并用优先级表:created(0) < paid(1) < closed(2) < refunded(3),pay_status 同理;pending_config 这类历史状态优先级最低,会被新事件覆盖。
  • 91 查询的三种状态:10 处理中/未就绪,20 已发货(cards 为加密后的 claimUrl),30 失败(pending_config、manual_failed 或任务失败)。
  • 历史遗留:库中仍有 25 条 order_status='pending_config' 的 91 老单(最近一条 2026-08-11),当前代码不再写入该状态,只作为查询口径识别。

3.2.2 快手行业电子凭证(send-code 四条接口 + 两条出站回调)

  • 券号与 91 订单解耦:voucher_code = 'KSV' + Crockford Base32(10 字节随机),UNIQUE(voucher_code),冲突重试 5 次。
  • 幂等键是 UNIQUE(oid, unit_index):重复 send-code 返回同一批 eticket.id;unit_index = 1..num。
  • 未找到 91 任务时券先落库(task_id 为空),后续由 voucher-binding-service 绑定,支持「快手先发码、91 后建单」。
  • 出站回调用 send_callback_status/attempt_count/last_error/response_json/sent_at 记录,失败可重推。
  • 核销流水号幂等:consume_serial_num = CONSUME-{voucher_code};状态只有 UNUSED / CONSUMED / DESTROYED。
  • 本地库实测:CONSUMED 376,179、DESTROYED 26,815、UNUSED 106 —— 说明核销链路是真实主路径。

3.2.3 拼多多(两段式)

pdd_order_sync 定时任务(默认 20s / pageSize 10)
  → pdd-client 拉订单(Cookie + anti-content)
  → upsert pdd_raw_orders(UNIQUE(order_sn))
  → processPendingPddRawOrders 逐条消费
       → pdd-order-mapper 归一化
       → upsertOrderFromSource
       → sync_status: pending → synced / ignored / failed
  → 后台可对 failed 的原始单做 replay

设计意图:原始报文与业务订单分离,失败可重放,不会因为映射问题丢单。断路器与 Cookie 过期识别在 pdd-sync-circuit-breaker.ts / isPddCookieExpiredError。

3.2.4 抖音码速达(推送 → 建单 → 发券 → 领取)

sequenceDiagram
  participant D as 抖音码速达
  participant N as notify-service
  participant O as order-service
  participant C as certificate-service
  D->>N: POST /message?topic&pub_time&sign(原始 body)
  N->>N: 验签(data 原始字符串)
  N->>N: eventId = f(topic, pub_time, data)
  N->>N: insertWebhookEventOnce(先落库去重)
  alt topic = trade_TradePaid
    N->>D: 拉订单详情(网关)
    N->>O: upsertOrderFromSource
    N->>C: 生成 / 同步 DYC 券(redeem_certificates)
    N->>D: 推送给买家的领取链接
  else trade_TradeCanceled / refund_RefundSuccess
    N->>C: 券失效 + 订单状态收敛
  end
  • 抖音 sku_order_list[].code 是内部 SKU,product_name 是引流标题;sku_code 恒存内部 SKU,标题只进 snapshot.productTitle,永不参与匹配。
  • 券表 redeem_certificates 自带状态机 issued / synced / verified / invalid 与同步状态 pending / success / failed,核销走 coupons.verifyV3。
  • 已知缺陷:webhook_events 先插入后处理,处理失败时平台重推会被判 duplicate 丢弃(详见风险清单)。

3.3 载体判定(本系统最关键的一步)

flowchart TD
  A["order_item"] --> B{"来源"}
  B -->|91 / 快手| C["productNo → affiliate-dash sku 映射<br/>常量表 + 后台 skuMapping 覆盖"]
  B -->|抖音| D["内部 SKU → affiliate-dash sku 映射"]
  C --> E["resolveFulfillmentRoute"]
  D --> E
  E --> F{"路由顺序"}
  F --> G["1. 履约路由总开关<br/>关闭 = 停止自动履约"]
  F --> H["2. 指定匹配(显式映射)"]
  F --> I["3. 商品名规则 exact / contains"]
  F --> J["4. 内部 SKU 规则"]
  F --> K["5. 全局执行器优先级"]
  F --> L["6. unmatchedExecutorKey(生产为空)"]
  G --> M["snapshot.fulfillmentRoute<br/>selectedExecutorKey / reason / candidates / skipped"]
  H --> M
  I --> M
  J --> M
  K --> M
  L --> M
  M --> N{"selectedExecutorKey"}
  N -->|affiliate_dash| O["isConfigured=true<br/>建自动履约任务"]
  N -->|manual_dispatch| P["isConfigured=true<br/>建人工任务"]
  N -->|空| Q["isConfigured=false<br/>待补全 / 交给发单层判定"]

判定结果全部写进 order_items.item_snapshot_json:

快照键 内容
fulfillmentRoute selectedExecutorKey、reason、candidates、skipped、matchedRule
classification(抖音) channel: affiliate_dash / unmatched、canonicalSku
affiliateDash 命中的 sku、productNo、skuName、matchMode
isConfigured 是否已配置可执行载体
productTitle(抖音) 引流长标题(只读展示)
kuaishouSendCode 快手 send-code 补全的卖家/SKU 上下文

载体互斥守护:sync-worker-orders-service.isAffiliateDashCarrierSnapshot() 会跳过 classification.channel === 'affiliate_dash' 的订单项,避免自动发货与工单双通道同时消费一个订单项。

3.4 履约任务与领取链接

  1. syncDeliveryTasksForOrder 对每个订单项按 quantity 补建 unit_index = 1..n,重复执行只补缺口。
  2. 执行器来自 snapshot.fulfillmentRoute:
    • affiliate_dash → 绑定 fulfillment_profiles.profile_key='affiliate_dash',requires_claim=true,初始状态 paid;
    • manual_dispatch → profile_key='manual_review',初始状态 manual_review;
    • 未支付 → pending_payment。
  3. 已支付订单立即 preparePaidFulfillmentTask(执行器注册表:affiliate_dash / manual / legacy-retired 只读桩)。
  4. 领取 token:createTaskClaimToken 写 claim_tokens(task_id 唯一、token 唯一、max_use_count=1),claimUrl = claimBaseUrl + '/' + token。
  5. 抖音走独立基址 claimLinkBase(后台配置),刻意不回退 orders.claimBaseUrl。
  6. orders.claim_status(迁移 099)= active / paused / closed,用于暂停或关闭整单兑换,是后台处置链接失效问题的新开关。

3.5 affiliate-dash 自动履约

建任务(executor=affiliate_dash,requires_claim=true)
  → preparePaidFulfillmentTask:调 affiliate-dash 建单,拿到 orderNo / bindUrl / qrUrl
  → 生成 claim token 与 claimUrl(91 查询即可返回 20)
  → 建单成功时即核销快手电子凭证(consumeStatus=success;失败只告警)
  → 买家在领取页:查 delivery → 填 UID → bind → 轮询 bind-result → submit
  → affiliate-dash 回调 order.shipping.updated(delivered)
  → 任务 completed / redeemed;回调只做状态收敛与兜底补核销

任务上下文固定形状 context_json.affiliateDash(AffiliateDashFlow),包含 sku / orderNo / clientOrderNo / orderStatus / canShip / bindUrl / qrUrl / bound / submitStatus / consumeStatus / lastSyncedAt。

3.6 买家侧三个页面

入口 路由 用途
主站领取页 /#/claim/:token 快手渠道主路径,按 executorKey 渲染不同步骤
H5 领取页 /h5/:orderNo(BrowserRouter 分支) 抖音等按订单号承接,/api/v1/claim/h5/...
兑换页 /redeem 抖音 DYC 券兑换/核销
补资料页 /collect 待补全订单资料收集(仅凭订单号,无第二因子)

领取页快照与轮询逻辑在 pages/claim/claim-snapshot.ts、claim-poll.ts;affiliate-dash 的具体步骤在 ClaimAffiliateDashSteps.tsx(查 delivery → UID → bind → 轮询 → submit)。

3.7 打手众包:从匹配到结算

3.7.1 接单模板匹配(product-match 引擎)

匹配引擎是纯函数(services/product-match/engine.ts),来源差异只在适配器:

概念 说明
条目 entry 一条接单模板(work_product_rules)或其映射(work_product_rule_mappings)编译而来
作用域 sourceKeys、shopKeys、qtyMin/qtyMax:配了就必须命中
条件组 all(全部命中)、any(至少一条)、bonus(命中加分、不中不淘汰)
匹配模式 exact、contains、series(剥离「N个」后比系列,兼容秘钥/密钥)、series-exact
权重 skuId/relSkuId 900 > skuText/skuNick 800 > series 600 > skuCode 500 > itemId 300 > itemTitle 200(contains 降档,文本另有「具体度」加成上限 80)
结果 唯一最高分 → matched;并列最高 → ambiguous(不猜);无候选 → unmatched

每次匹配都会写 work_product_match_logs(含 candidates_json、raw_payload_json),后台有「匹配测试 / 匹配日志 / 日志清理」三个入口。

3.7.2 建单规则

for each order_item:
  if snapshot.classification.channel == 'affiliate_dash' → skip(载体已被自动发货占用)
  if 该订单项已有工单 → skip(already_exists,不纠正旧单)
  match = 匹配引擎
  无命中/歧义 → skip + 日志
  命中但 auto_create=false 且 autoOnly → skip
  platform_order_id 已挂在**其他**订单下 → skip(防撞号)
  建单:
    status = 素材必填项齐全 ? unassigned : pending_material
    reward = 单价 × SKU 数量(unit_price_fen)或固定 reward_amount
    sharing(拼单)按模板 sharing_total_quantity / unit_reward 计算
    acceptance_mode = normal / friend_gift(好友赠送)
    timeout_minutes + timeout_policy(reopen / cancel_release / cancel_deduct)
    material = 来源摘要 +(抖音)已有券快照
pending_material → 生成后台「待补素材」通知

3.7.3 工单生命周期

stateDiagram-v2
  [*] --> pending_material
  pending_material --> unassigned: 素材补全
  pending_material --> cancelled: 取消
  unassigned --> open: 发布到大厅
  unassigned --> in_progress: 后台指派
  open --> in_progress: 打手抢单 / 拼单参与
  in_progress --> pending_acceptance: 打手提交验收
  pending_acceptance --> accepted: 验收通过(结算)
  pending_acceptance --> problem: 发起问题单
  problem --> open: 退回大厅
  problem --> cancelled: 取消
  in_progress --> open: 超时按 reopen 策略
  in_progress --> cancelled: 超时按 cancel 策略
  open --> cancelled: 取消
  accepted --> [*]
  cancelled --> [*]
  • 抢单:单事务内 ensureWallet → 校验活跃单上限 → 校验可用余额 ≥ 押金 → 条件更新 WHERE status='open' AND assigned_worker_id IS NULL → 冻结押金(deposit_freeze流水)→ 写work_order_events`。
  • 指派:先 SELECT … FOR UPDATE 锁工单,再锁钱包(与抢单顺序相反,见风险 R4)。
  • 拼单:work_order_shares 一份一行,UNIQUE(work_order_id, worker_id);份额状态 joined → submitted → accepted / cancelled;母单可长期 open,最后一个份额验收后母单转 accepted。
  • 好友赠送:acceptance_mode='friend_gift',gift_phase:material_required → material_submitted → friend_countdown → gift_ready,冷却结束才能提交赠送凭证;确认加好友后不再退押金(防套利)。
  • 超时:定时任务 work-order-timeout(默认 60s,scanLimit 50)扫描 deadline_at,按 timeout_policy(reopen 退回大厅 / cancel_release 取消并退押金 / cancel_deduct 取消并扣押金)处置;已确认加好友的赠送单即使配 cancel_release 也会转为扣款。超时只写 timeout_* 事件并释放寄卖现货,不直接改等级计数(等级处罚来自退单审核通过次数,见 3.7.6)。
  • 验收与结算(acceptWorkOrderAndSettle):锁工单与份额 → 报酬入账(可延迟解冻)→ 释放/延迟解冻押金(worker_deposit_unfreezes 队列)→ 抵扣售后欠款 → 升级等级 → 写 worker_wallet_ledgers。
  • 售后:worker_after_sales_cases(问题类型、投诉、验收快照、处置动作、退款)+ worker_after_sales_debts(欠款余额)+ worker_after_sales_case_events(时间线)。

3.7.4 打手资金模型

表 作用
worker_wallets 可用余额、冻结押金、累计入账、累计结算、待解冻、提现冻结
worker_wallet_ledgers 只追加流水,13 种类型(见下)
worker_finance_requests 充值 / 提现申请与审核
worker_withdrawal_accounts 提现账户(微信 / 支付宝二维码)
worker_deposit_unfreezes 延迟解冻队列(押金、报酬两类)

实测流水类型分布(本地库):reward_settlement 108,623、sharing_reward 34,295、deposit_freeze 8,283、deposit_release 6,705、withdraw_paid 6,381、withdraw_freeze 5,833、deposit_deduction 920、deposit_pending_unfreeze / deposit_unfreeze 各 621、manual_credit 398、withdraw_release 121、after_sales_balance_deduction 20、after_sales_debt_offset 9。

3.7.5 等级、权限与风控

  • worker_levels.permission_json 承载:免押额度、最大同时接单量、大厅可见延迟、自动验收(VIP)。
  • worker_level_logs 记录升降级;worker_users.is_level_locked 可锁定等级。
  • worker_sessions 会话落库 + session_version,设备数与设备类型可限制。
  • worker_risk_events 记录刷单/异常行为与处置(ban / unban / release),middleware/worker-risk-control.ts 做抢单前置限流与拦截。
  • 提现黑名单:worker_users.is_withdrawal_blacklisted + 原因 + 时间。

抢单/拼单/好友赠送/验收/结算的逐行细节(含押金矩阵、资金等式、退单处罚规则)见 打手工单深挖-抢单拼单好友赠送结算-2026-10-23.md。

3.8 寄卖仓库

flowchart TD
  A["卖家提报 consignment_submissions<br/>游戏/系统/区服/系列/钻级/璀璨/要价/凭证"] --> B{"客服审核"}
  B -->|驳回| C["rejected(可改后重提)"]
  B -->|通过| D["入库 consignment_inventories<br/>inventory_no = INV…<br/>status=in_stock"]
  D --> E{"订单匹配"}
  E -->|规格匹配| F["占用 consumed<br/>bound_order_id / bound_work_order_id"]
  E -->|不匹配| D
  F --> G{"履约方式"}
  G -->|internal_stock| H["内部现货交付<br/>客服操作"]
  G -->|worker_deposit| I["指派打手代练<br/>冻结打手押金"]
  H --> J["验收 → 释放发布押金 + 结货款"]
  I --> J
  F -.->|撤销| K["revoke:打手押金 + 卖家押金<br/>auto / refund / forfeit / keep_frozen"]

匹配口径(consignment-matching-service.ts):

  • 系统 / 区服 / 系列必须一致(区服「互通」、系统「通用」视为通配);
  • 钻级与璀璨低配不充高配:号主 2 钻不能配 1 钻库存;
  • 三态比对:match / mismatch / unknown,unknown(任一侧没值)不算精确匹配,只能进部分匹配候选;
  • 人工指定时用 classifyConsignmentMatch 返回 conflicts,把「差在哪」显式告诉客服;
  • 建议价从接单模板反推(work_product_rules 商品名 → 规格解析 → 最接近档位兜底,命中多条取最高价)。

卖家视角的单一阶段由 deriveConsignmentItemStage 从「提报 + 库存 + 工单」三套状态推导:pending_review → in_stock → matched_waiting_friend → gift_cooldown → gift_ready → gift_submitted → fulfilling → pending_acceptance → completed,终态 rejected / archived / cancelled。

押金策略(domain/consignment-deposit-policy.ts):

场景 auto 行为
打手履约押金撤销 全额退还;要扣必须显式 forfeit
卖家发布押金撤销 作废(下架)→ 退还;回库(继续挂卖)→ 保持冻结
显式覆盖 必须填原因(isDepositHandlingOverridden 校验)

3.9 客服备用金台账(迁移 100)

公司 → 主管池(fund_accounts.account_type='supervisor')
主管 → 客服账户('agent',responsible_supervisor_id 指向主管)
客服 → 支出明细(manual_expenses:平台充值 / 采购 / 赔付 / 运费 / 其他)

四条硬约束(写在建表注释里):

  1. 余额只能由 fund_entries 变动,禁止手工改;
  2. 发放 / 缴回必须同时写主管与客服两条方向相反的流水,共用 transfer_no;
  3. 支出必须同时写客服负数流水与 manual_expenses 明细(UNIQUE(fund_entry_id));
  4. 已记账流水不修改不删除,差异走 adjustment。

对账:fund_reconciliations(pending / confirmed / discrepancy),DB 级校验 difference_fen = actual_balance_fen - system_balance_fen。

权限点:fund.view / issue / expense / reconcile / adjust / export,按角色(admin / finance / operator / support)分别在迁移里授权。

3.10 后台治理

能力 实现
登录 用户名密码 + 短信二次(可选) + 设备认证(admin_user_devices / admin_device_challenges)
会话 admin_sessions 落库 + session_version,可远程踢设备
RBAC 4 个内置角色(admin / finance / operator / support)+ 29 个权限点 + 72 条角色授权
审计 admin_audit_logs(actor / action / target / payload),实测 55 万行
通知 admin_notifications(类型 / 优先级 / 去重键 / 提醒次数 / 可见性 / 声音)
实时 realtime_events + SSE,后台列表与工单详情增量刷新
定时任务 后台可视化管理(启用、间隔、立即执行、运行状态)

3.11 定时任务

id 类型 默认间隔 作用
work-order-timeout work_order_timeout 60s(scanLimit 50) 工单超时处置 + 打手超时计数
deposit-unfreeze deposit_unfreeze 60s(scanLimit 100) 押金/报酬延迟解冻出队
pdd_order_sync pdd_order_sync 20s(pageSize 10) 拼多多订单拉取
data-retention data_retention 24h 清理原始报文、匹配日志、任务事件、webhook、审计;回填 source_order_amount_fen

调度器在进程内(services/scheduler/scheduler-service.ts),同一任务运行中会跳过本轮;配置存 app_config_entries.scheduled_jobs。


四、状态机一页汇总

4.1 订单 orders

字段 取值 备注
order_status created / paid / closed / refunded(另有历史值 pending_config、manual_failed) 合并时按优先级只升不降
pay_status unpaid / failed / paid / refunded 与 paid_at 联动
claim_status active / paused / closed 兑换有效性开关(迁移 099)

注意:order_status 不能代表履约完成。本地库中 375,432 条 paid 订单里,绝大多数并没有「订单级完成」状态,履约进度只能看 fulfillment_tasks.task_status 或 work_orders.status。

4.2 履约任务 fulfillment_tasks

pending_payment → paid → link_generated → claimed → pending_binding_prepare → waiting_binding → role_confirmed → redeeming → dispatched_pending_return → completed / redeemed,旁支 retry_pending、manual_review、failed、expired、closed。

状态转移约束定义在 domain/task-status.ts(TASK_TRANSITIONS),终态 completed / redeemed / expired / closed。

4.3 接单工单 work_orders / 份额 work_order_shares

  • 工单:pending_material / unassigned / open / in_progress / pending_acceptance / problem / accepted / cancelled(终态 accepted、cancelled)。
  • 份额:joined / submitted / accepted / cancelled。
  • 好友赠送额外有 gift_phase:material_required / material_submitted / friend_countdown / gift_ready。

4.4 寄卖

对象 取值
consignment_submissions.status pending_review / approved / rejected
consignment_inventories.status in_stock / consumed / archived
consignment_inventories.allocation_status 待安排 / 已安排(中文枚举,历史遗留)
source_type manual / worker_portal / recycle
卖家可感知阶段 pending_review / in_stock / matched_waiting_friend / gift_cooldown / gift_ready / gift_submitted / fulfilling / pending_acceptance / completed / rejected / archived / cancelled

4.5 凭证与券

对象 取值
kuaishou_industry_vouchers.status UNUSED / CONSUMED / DESTROYED
kuaishou_industry_vouchers.send_callback_status success / failed / pending(回调快手发码结果)
redeem_certificates.status issued / synced / verified / invalid
redeem_certificates.sync_status pending / success / failed
kuaishou_cloud_task_states.ticket_status 快手云端取号/绑号状态(配合 dispatch_status、consume_status)

4.6 资金与售后

对象 取值
worker_finance_requests.status pending / approved / rejected / cancelled
worker_finance_requests.request_type recharge / withdraw
worker_deposit_unfreezes.status 待解冻 / 已释放(released_at 落地)
worker_after_sales_cases.status 待响应 / 已响应 / 已处置 / 已退款(配合 resolution_action)
worker_after_sales_debts.status 未结清 / 已结清
fund_entries.entry_type opening / funding / issue / expense / return / adjustment
fund_reconciliations.status pending / confirmed / discrepancy

五、数据库设计

5.1 总览(本地库实读)

指标 数值
业务表 59 张(+ schema_migrations)
迁移文件 103 个(001 基线 + 002–103 增量),全部已应用
外键 91
唯一约束 29
CHECK 约束 28
索引 268(含主键与唯一索引)
主键 绝大多数是 BIGSERIAL;钱包、工单备注、工单已读等关系表用复合主键
时间类型 统一 TIMESTAMPTZ(不存本地时间)
金额类型 打手侧 INTEGER(分);orders.total_amount 也是 INTEGER;资金台账、PDD 与工单来源金额用 BIGINT(分);没有 NUMERIC,也没有浮点金额
JSON 变化快的扩展信息一律 JSONB(快照、上下文、凭证原文),不建宽表

设计风格非常一致:关系表 + *_json 扩展 + 只追加流水。代价是 JSONB 里的字段无法被 DB 约束保护(如 context_json.affiliateDash 全靠 TS 类型)。

5.2 分域表清单

A. 订单与履约主链路(12)

表 行数(估) 说明
orders 375,459 订单骨架;UNIQUE(provider, platform, shop_id, platform_order_id)
order_items 375,263 订单项;item_snapshot_json 是载体判定的全部证据
fulfillment_tasks 107,111 履约单元;UNIQUE(order_item_id, unit_index)、UNIQUE(task_no)
claim_tokens 107,083 领取 token;UNIQUE(task_id)、UNIQUE(token)
task_events 1,310,742 任务事件流水(最大表)
task_runtime_contexts 45,976 运行时上下文(登录态、角色、截图、产物)
fulfillment_profiles 4 履约档案(affiliate_dash / manual_review / legacy 桩)
fulfillment_profile_requirements 少量 档案的凭证要求
pdd_raw_orders 5,068 PDD 原始单,UNIQUE(order_sn),sync_status 四态
webhook_events 110,233 外部回调去重表(provider + event_id)
short_links 95 短链(目标 URL 哈希 + 访问计数)
file_assets 289,260 对象存储资产(缩略图/中图/URL/场景/上传者)

B. 凭证(3)

表 行数(估) 说明
kuaishou_industry_vouchers 403,299 快手电子凭证;UNIQUE(voucher_code)、UNIQUE(oid, unit_index)
kuaishou_cloud_task_states 46,894 快手云端取号/绑号/核销状态(一任务一行)
redeem_certificates 1,541 抖音 DYC 券,含平台券号、平台状态、同步错误

C. 接单平台(25)

表 行数(估) 说明
worker_users 1,905 打手账号(30 列:等级、邀请、改名卡、冻结、黑名单、寄卖权限、等级锁)
worker_levels / worker_level_logs 6 / 133 等级定义(permission_json)与升降级流水
worker_sessions 32,727 会话(设备、IP、UA、过期、吊销)
worker_sms_codes 5,206 短信验证码(哈希、失败次数)
worker_wallets 1,905 钱包(可用、冻结押金、待解冻、提现冻结、累计)
worker_wallet_ledgers 172,830 钱包流水(13 种 ledger_type)
worker_finance_requests 6,769 充值/提现申请(含冻结金额与审核人)
worker_withdrawal_accounts 776 提现账户(微信/支付宝二维码 JSON)
worker_deposit_unfreezes 621 延迟解冻队列(fund_type 区分押金/报酬)
work_categories 30 工单分类(支持父级与图标)
work_orders 156,043 工单主表(44 列,全库最宽)
work_order_events 1,258,811 工单事件流水
work_order_shares 41,383 拼单份额
worker_work_order_notes 13,604 打手备注(一工单一打手一行)
worker_work_order_views 147,356 已读/素材已读
worker_order_cancel_requests 953 打手取消申请与审核
worker_order_feedbacks 501 工单反馈/申诉
worker_after_sales_cases 39 售后问题单
worker_after_sales_debts 4 售后欠款
worker_after_sales_case_events 93 售后时间线
worker_risk_events 209 风控事件与处置
work_product_rules 710 接单模板(29 列:匹配、奖励、押金、拼单、时限、验收模式)
work_product_rule_mappings 707 模板映射(卖家/SKU/标题,sku_match_mode)
work_product_match_logs 10,000 匹配诊断日志(保留量受清理任务控制)

D. 寄卖(2)

表 行数(估) 说明
consignment_submissions 877 提报单(submission_no 唯一、要价、押金、审核结果指向库存)
consignment_inventories 784 库存(INV… 唯一、38 列:规格、凭证、绑定订单/工单、卖家、指派打手)

E. 后台与权限(11)

admin_users(40)、admin_sessions(3,077)、admin_login_logs(4,936)、admin_roles(4)、admin_permissions(29)、admin_role_permissions(72)、admin_audit_logs(556,360)、admin_notifications(166,883)、admin_sms_codes、admin_user_devices、admin_device_challenges。

F. 配置与实时(2)

app_config_entries(18 行,全部平台/业务配置)、realtime_events(600,滚动保留)。

G. 资金台账(4)

fund_accounts、fund_entries、manual_expenses、fund_reconciliations(本地均为空表,属于新落地模块)。

5.3 核心表字段速查

orders

列 类型 说明
id bigserial PK
provider / platform text NOT NULL 例如 91kaquan / kuaishou
shop_id / shop_name text 店铺键空间(参与唯一键)
platform_order_id text NOT NULL 平台单号
order_status / pay_status text 见 4.1
buyer_id / buyer_name / receiver_contact text 买家信息
total_amount integer 用户付款金额(分),三家口径已统一为「实付」;91 = maxAmount
currency text 默认 CNY
raw_payload_json jsonb 原始报文(保留期由 data-retention 控制)
paid_at timestamptz 支付/成团时间,建单时用作 created_at 优先值
claim_status / claim_status_reason / claim_status_updated_at text / text / timestamptz 兑换有效性开关

索引(12 个)覆盖:唯一键、(provider, platform, order_status, created_at)、平台单号、店铺筛选、claim 状态等(迁移 070/074/086/099)。

order_items

id、order_id(FK CASCADE)、sku_code、sku_name、quantity、spec_json、item_snapshot_json。

没有金额字段,也没有 canonical_product_id:多商品订单无法直接分摊收入(缺口 C5)。

fulfillment_tasks

列 说明
order_id / order_item_id / unit_index 归属与单元序号;UNIQUE(order_item_id, unit_index)
task_no DT… 唯一编号
provider / platform / shop_id / shop_name / platform_order_id 冗余来源字段(便于按来源查询,避免 join)
profile_id / executor_key 履约档案与执行器
task_status / delivery_status / result_code / result_message 主状态机 + 交付状态
claim_token / claim_expires_at 历史冗余列;实际 token 在 claim_tokens(查询时 LEFT JOIN 出 primary_claim_token)
automation_mode / requires_claim / user_action_status 自动/人工、是否需要用户领取
attempt_count / last_error 重试
context_json 执行器上下文(affiliateDash、kuaishouIndustryVoucher 等)
claimed_at / role_confirmed_at / redeemed_at 关键时间点

work_orders(44 列,按用途分组)

组 列
标识 id、work_order_no(唯一)、order_id、order_item_id、task_id(历史遗留,已注释废弃)、platform_order_id、product_rule_id、category_id
商品 product_name
状态 status、acceptance_mode、gift_phase、admin_note、problem_note
金额 reward_amount、required_deposit_amount、deposit_threshold_amount、source_order_amount_fen、sharing_unit_reward
归属 assigned_worker_id、last_assigned_worker_id
拼单 sharing_enabled、sharing_total_quantity、sharing_unit_reward
时限 deadline_at、timeout_minutes、timeout_policy
好友赠送 friend_added_at、gift_available_at、gift_cooldown_hours、gift_cooldown_minutes
内容 material_json、requirement_json、acceptance_json、draft_acceptance_json、booster_material_json、worker_note
大厅 hall_queued_at、pinned_at、published_at
时间 assigned_at、submitted_at、accepted_at、created_at、updated_at

21 个索引,覆盖:状态+时间、分类+状态、店铺筛选、大厅队列、统计口径、关键词查询等(迁移 031/047/051/073/083/085/087/088/090/103)。

worker_wallets / worker_wallet_ledgers

worker_wallets:worker_id PK/FK、available_amount、frozen_deposit_amount、pending_unfreeze_amount、frozen_withdraw_amount、total_credited_amount、total_settled_amount。

worker_wallet_ledgers:worker_id、ledger_type、amount(有符号)、balance_after、frozen_after、related_work_order_id、audit_status、note、payload_json。

余额是可变字段,没有「由流水重算」的校验;这是财务化的第一道缺口(见第七节)。

consignment_inventories(38 列)

inventory_no、game_name、category_id、item_name、system、server_zone、series、diamond_level、brilliance_count、cost_price_fen、seller_*(联系人/游戏 ID/收款/手机)、credentials_json(游戏账号密码)、screenshots_json、status、allocation_status、bound_order_id、bound_work_order_id、bound_platform_order_id、shop_name、source_type、source_submission_id、worker_id、assigned_worker_id、deposit_amount_fen、inbound_at、consumed_at。

fund_*

  • fund_accounts:account_no 唯一、account_type(supervisor/agent)、owner_admin_user_id、responsible_supervisor_id(自引用)、balance_fen >= 0、status。
  • fund_entries:entry_no 唯一、account_id、entry_type(6 种)、amount_fen <> 0(有符号)、balance_after_fen、transfer_no、counterparty_account_id、reconciliation_id、evidence_json。
  • manual_expenses:expense_no 唯一、account_id、amount_fen > 0、category(5 种)、payee_type(4 种)、order_id/order_item_id、fund_entry_id 唯一(一支出对一流水)。
  • fund_reconciliations:period_start/end、system_balance_fen、actual_balance_fen、difference_fen(CHECK 等于差值)、status、confirmed_by。

5.4 关系图(核心链路)

erDiagram
  orders ||--o{ order_items : "1:N"
  orders ||--o{ fulfillment_tasks : "1:N"
  order_items ||--o{ fulfillment_tasks : "1:N (unit_index)"
  fulfillment_tasks ||--|| claim_tokens : "1:1 (task_id unique)"
  fulfillment_tasks ||--o{ task_events : "1:N"
  fulfillment_tasks ||--o| task_runtime_contexts : "1:0..1"
  fulfillment_tasks ||--o| kuaishou_cloud_task_states : "1:0..1"
  orders ||--o{ kuaishou_industry_vouchers : "1:N"
  fulfillment_tasks |o--o{ kuaishou_industry_vouchers : "task_id 可空"
  orders ||--o{ redeem_certificates : "local_order_id"
  order_items ||--o| work_orders : "1:0..1"
  work_orders ||--o{ work_order_shares : "1:N"
  work_orders ||--o{ work_order_events : "1:N"
  worker_users ||--|| worker_wallets : "1:1"
  worker_users ||--o{ worker_wallet_ledgers : "1:N"
  worker_users ||--o{ worker_sessions : "1:N"
  worker_users ||--o{ worker_finance_requests : "1:N"
  work_orders ||--o{ worker_deposit_unfreezes : "1:N"
  work_orders ||--o{ worker_after_sales_cases : "1:N"
  worker_after_sales_cases ||--o{ worker_after_sales_debts : "1:N"
  consignment_submissions ||--o| consignment_inventories : "审核产出"
  consignment_inventories |o--o{ work_orders : "bound_work_order_id"
  admin_users ||--o{ fund_accounts : "owner"
  fund_accounts ||--o{ fund_entries : "1:N"
  fund_entries ||--|| manual_expenses : "1:1 (fund_entry_id)"
  fund_accounts ||--o{ fund_reconciliations : "1:N"
  pdd_raw_orders |o--|| orders : "synced_order_id"

5.5 约束与索引策略(值得记住的几条)

  1. 唯一键即幂等键:orders(provider, platform, shop_id, platform_order_id)、fulfillment_tasks(order_item_id, unit_index)、kuaishou_industry_vouchers(oid, unit_index)、pdd_raw_orders(order_sn)、work_order_shares(work_order_id, worker_id)、fund_entries(entry_no)、manual_expenses(fund_entry_id)。
  2. DDL 层不枚举业务状态:orders / fulfillment_tasks / work_orders 的状态列没有 CHECK,状态机只存在于 TS(domain/*.ts)。好处是迁移成本低,坏处是脏状态只能靠代码兜底(pending_config 就是遗留证据)。寄卖与资金表则加了 CHECK,口径不完全统一。
  3. 索引高度针对后台查询:迁移 070/071/073/074/082/084–090 全是后台列表/统计/模糊搜索的覆盖索引,其中 073 用 pg_trgm GIN 支撑工单关键词(游戏 ID)搜索。
  4. JSONB 不做 GIN:除搜索字段外,snapshot/context 的查询都走「先取行再在应用层判断」,这也是解析逻辑必须集中在少数几个 service 的原因。
  5. 软删/归档:业务表基本不物理删除(ON DELETE CASCADE 只用于任务→事件、订单→订单项等从属关系),保留靠 status 与归档字段。

5.6 配置存储(app_config_entries)

配置不在文件里、也不在环境变量里(环境变量只管连接串与密钥),全部走一张 key-value 表,后台保存后即时生效(部分需要重启或重载调度器):

key 内容
affiliate_dash 自动发货平台配置(baseUrl、商户号、密钥、skuMapping)
kuaishou_industry_source 快手电子凭证接入(appId、secret、token、店铺)
douyin_mashida 抖音码速达(uid/secret/baseUrl/claimLinkBase/handledTopics)
pdd_source 拼多多(店铺、Cookie、同步开关)
fulfillment_routing 履约路由(总开关、商品名/SKU 规则、全局优先级、unmatchedExecutorKey)
scheduled_jobs 定时任务配置与间隔
notification 通用通知(Bark / WPush)
worker_finance 打手财务策略(押金、解冻天数、提现规则)
worker_product_match 匹配引擎开关(legacyFallbackEnabled)
worker_hall 大厅可见性、容量、分类上限
worker_announcement 打手端公告
worker_platform_notifications 打手端通知模板
worker_risk_control 风控阈值与处罚
worker_mock_shops 开发/联调假店铺
历史键 cloudtentacles_*、kuaishou_feifei(已下线通道,仅保留数据)

5.7 迁移演进(001–103)

区段 主题
001 基线:后台、配置、履约档案、订单/订单项/任务/领取 token、任务事件、快手券、短链
002–003 登录日志、电子凭证类型
004–012 接单平台落地:等级、打手、钱包、流水、分类、工单、工单事件、文件、商品规则、财务申请、拼单、超时、解冻、短信
013–028 回调去重、权限修补、永久 claim token、工单备注/置顶、提现账户、通知
029–040 会话、实时事件、搜索索引、匹配上下文/目录/SKU 模式、匹配日志、后台会话
041–058 排行榜、自动激活、取消申请、反馈、来源规则、冻结原因、性能护栏、大厅队列、售后、改名卡、赠送验收、解冻配置、素材已读
059–076 RBAC、提现冻结、管理备注、取消措辞、赠送冷却、个人免押、数据保留、筛选索引、财务角色、搜索重建、分类父子、提现黑名单
077–082 寄卖(库存、打手押金、解除收集链接、分配状态、白名单)
081 PDD 原始单两段式
083–093 工单来源金额、多组覆盖索引、风控事件、遗留列注释、autovacuum
094–098 抖音券 redeem_certificates、平台券号、任务 claim 提交(后废弃)、等级锁
099–103 订单兑换有效性、备用金台账、后台显示名、短信/设备认证、店铺筛选索引

约定(README):不改历史迁移;结构变更一律新增;空库按序执行;后端启动自动 up。

5.8 实测状态分布(本地库,用于验证对业务的理解)

维度 分布
订单 paid/paid/active 375,432;pending_config 25;manual_failed 1;closed 1
履约任务 redeemed+delivered 49,527;completed+delivered 45,658;closed+cancelled 5,158;closed+closed 3,238;waiting_binding+pending 956
工单 accepted 131,786;pending_material 21,509;cancelled 1,075;unassigned 760;in_progress 612;pending_acceptance 174;open 101;problem 26
快手券 CONSUMED 376,179;DESTROYED 26,815;UNUSED 106
抖音券 verified 1,337;invalid 123;synced 81
财务申请 提现 approve 6,381;充值 reject 151;提现 reject 129;充值 approve 91

两个值得注意的信号:

  1. pending_material 占工单 13.8%(21,509 条),说明接单素材缺口是常态,补素材是运营主要工作量之一。
  2. unassigned 只有 760,而 open 只有 101 —— 大量工单没有经过大厅,直接被后台指派或同步生成后长期停在待补素材。

六、外部集成与安全边界

链路 验签 幂等/去重 备注
91 卡券 签名 + 时间戳容差 orders 唯一键 + advisory lock 卡密用 buildOpen91Cards 加密返回
快手电子凭证 签名(crypto.ts) UNIQUE(oid, unit_index) 4 个入站接口 + 2 个出站回调
affiliate-dash 通知 验签 + 时间戳 webhook_events 失败重推会被判 duplicate(风险 R2)
抖音码速达 原始 body 验签 webhook_events(先落库) 同上
PDD Cookie / anti-content pdd_raw_orders(order_sn) + 断路器 无回调,纯拉取

其他安全面:

  • 后台/打手口令都是 scrypt + 盐,会话落库 + session_version。
  • 日志统一脱敏(敏感 key、URL 参数、JSON 字段),有单测。
  • 生产强制:session secret 长度、显式 CORS 白名单、存储凭据完整。
  • 明显缺口:/collect/*、/redeem/* 只凭订单号;/files/object 无鉴权;Caddy 无 CSP/HSTS/X-Frame-Options;后台 token 存 localStorage。

七、风险与设计缺口(按优先级)

P0 · 资金与一致性

# 问题 位置 影响
R1 发单金额高于用户付款只有前端提醒,后端无统一门禁 frontend/pages/admin/panels/work-order-payment.ts;admin-work-order-management-service.ts 直接调 API 可发亏损单
R2 寄卖审核可由请求体改 workerId 并按改后归属结算货款 consignment-admin-service.ts、consignment-supplier-repo.ts 钱结给错误的人
R3 寄卖审核详情全量返回游戏账号密码,只需 consignment.view consignment-admin-service.ts 权限粒度过粗
R4 抢单先锁钱包、指派先锁工单(锁序相反) work-order-grab-repo.ts 并发下 40P01 死锁
R5 自动工单硬编码零押金,模板押金字段不生效 sync-worker-orders-service.ts 自动发单无押金约束
R6 发布工单时凭证核销失败不回滚,指派却拦截 admin-work-order-management-service.ts / ...assignment-service.ts 同一规则两套行为

P1 · 可靠性

# 问题
R7 Webhook 先落库后处理,失败后平台重推被永久跳过
R8 抖音回调同步串行调上游,单次超时 20s,与 R7 叠加不可恢复
R9 /collect/*、/redeem/* 无第二因子
R10 /collect/upload 可匿名上传任意文件到 OSS
R11 /files/object 无鉴权;claim token 默认永久
R12 无 CSP/HSTS;后台 token 在 localStorage
R13 先到先匹配,后到的快手补全数据不纠正既有工单
R14 单实例耦合(限流/调度/SSE/缓存都在进程内)

P2 · 可维护性

巨型文件(WorkOrdersPanel.tsx 3,686 行、AdminTaskDetailPage.tsx 1,925 行、work-order-query-repo.ts 1,409 行、consignment-repo.ts 1,410 行、work-order-settlement-repo.ts 1,181 行);前端无组件测试/E2E;无 CI;迁移启动自动执行且无 down。

数据模型层面的四个缺口

# 缺口 后果
D1 没有「人工块」载体:无自动任务且无工单的已支付订单项只靠「无任务且无工单」推断 人工履约不可监管、不可计量
D2 无商品主数据 product_catalog / channel_product_mappings 同一商品名对应 27–35 个 sku_code,销量统计不可靠
D3 无成本版本与快照 cost_items/cost_versions/cost_snapshots 改价不可追溯,历史毛利不可重算;自动发货 cost_amount 只映射不落库
D4 order_items 无金额字段 多商品订单无法分摊收入

这几条与 docs/成本中心与三块履约模型.md、docs/drafts/* 的草案一致,草案尚未进迁移。


八、我看到的「三个业务面」边界图

flowchart LR
  subgraph 订单面
    O["orders / order_items"]
    T["fulfillment_tasks / claim_tokens"]
    V["vouchers / redeem_certificates"]
  end
  subgraph 工单面
    W["work_orders / work_order_shares"]
    WW["worker_users / wallets / ledgers"]
    P["work_product_rules / mappings"]
  end
  subgraph 寄卖面
    CS["consignment_submissions"]
    CI["consignment_inventories"]
  end
  subgraph 财务面
    FA["fund_accounts / fund_entries"]
    ME["manual_expenses"]
  end
  O --> T
  O --> V
  O --> W
  P --> W
  W --> WW
  CI --> O
  CS --> CI
  CI --> W
  ME --> FA
  W -. 影子总账未落地 .-> FA

三个面共享的只有两件事:订单项(order_items.id 是所有链路的锚点)与打手钱包(工单与寄卖都用它)。


九、需要你确认的问题

见对话中的提问(不在文档内固化,确认后我再补「口径确认」章节)。