28 KiB
抖音码速达 · 按 SKU 分类改造总设计(对齐 kuaishou-91 三层)
状态:抖音侧已实施(2026-10-14);P2 / P3 后续项未实施。最后校准:2026-10-18(
main @ 7542d547;仅基线更新,业务逻辑未变)。 依据:全链路代码走查 + 生产库只读实测(模板 662 条 /kuaishou-91订单 32.9 万 / 抖音 11 笔) 前置:10-三层分流设计.md、11-接单模板待补清单.md 前提:抖音侧实现可自由重构(无历史兼容包袱);91 回调与快手 send-code 是同一个kuaishou-91来源的两条数据通道,不拆成两个来源
一、目标与结论摘要
目标:抖音与 kuaishou-91 用同一套「按 SKU 分类」逻辑,产出三种结果:
| 层 | 触发条件 | 产物 |
|---|---|---|
| ① 皮肤/道具自动发货 | SKU 命中 affiliate_dash 映射表 | affiliate_dash 履约任务 + 领取链接 |
| ② 接单平台发单 | SKU 命中接单模板(work_product_rules) |
工单(打手抢单),不建履约任务 |
| ③ 人工 | 以上都不命中 | 不建任务、不建工单,进「待人工」队列 |
⚠️ 这三层不是三条独立业务线,而是同一个单子选择哪种执行载体(见 §2.1「一个单子的构成」):一个单子 = 订单骨架 + 买家侧凭证 + 一个执行载体。
结论(需要改的不是「加一层」,而是三件事):
- 分类键不稳定:现在 SKU 命中自动发货后,
order_items.sku_code会被覆写成英文 sku,导致「按 SKU 分类」失去稳定依据 → 必须确立规范化的 canonical SKU 并保证不被覆写; - 抖音绕过了分类框架:抖音分支写死
preferredExecutorKey,既不认履约路由规则,也未命中时强制建manual_dispatch人工任务(91 是「不建任务」); - 人工层没有载体:91 的「待配置队列」链路实际已成死代码(见 §3.4),人工单只是「裸单」;抖音连裸单标记都没有 → 需要一个显式的「待人工」状态/队列。
1.1 已实施(2026-10-14,抖音侧)
| # | 改动 | 位置 |
|---|---|---|
| 1 | 抖音 sku_code 恒为内部 SKU(不再被英文 sku 覆写);英文 sku 落 snapshot.affiliateDash.sku;引流标题落 snapshot.productTitle |
apps/backend/src/services/fulfillment/product-resolution-service.ts(resolveOrderItemForFulfillment) |
| 2 | 抖音分级判定重写:命中映射 → affiliate_dash;未命中 → 不设 preferred、且不兜底 manual_dispatch → 无执行载体 |
同上(resolveDouyinMashidaItemCandidate) |
| 3 | 映射来源改为「权威常量表 + 后台 skuMapping 增量」(新增商品不必发版) |
同上(matchDouyinMashidaAffiliateDashSku) |
| 4 | 写 snapshot.classification = { channel, canonicalSku, matchMode } |
同上 |
| 5 | 载体唯一守卫:分类为 affiliate_dash 的订单项不再建工单 |
apps/backend/src/services/worker-platform/sync-worker-orders-service.ts |
| 6 | 券 cert_link 按基址形态决定(/h5 一律按订单号拼;其它形态有 token 用 token、否则回退 certLink)——不再依赖是否命中自动发货 |
apps/backend/src/services/redeem/certificate-service.ts |
| 7 | 抖音券写入工单 material_json.douyinMashidaVoucher(对齐快手把凭证挂工单):① 发券后回填;② 建工单时券已存在则直接带上(覆盖「补模板后重新匹配」) |
同上 + apps/backend/src/services/worker-platform/sync-worker-orders-service.ts |
验证(2026-10-14 当时):npm run typecheck 通过;后端全量测试 717 例 / 714 通过 / 0 失败 / 3 跳过;本次新增 16 例(分类判定、sku_code 不覆写、券链接 5 例、券回填 4 例、载体守卫 3 例、91 回归)。2026-10-17 复核为 740 项(737 pass / 3 skipped)。
1.2 业务口径(2026-10-14 确认)与后续
| 项 | 决定 | 落法 |
|---|---|---|
| A 「待人工」队列 / 后台筛选 | 测试阶段不做,人工直接处理 | 未命中单 = 无任务无工单,维持现状 |
| B 券的发放 | 任何订单都要发券,与是否命中无关 | 发券逻辑不动(所有抖音单都发券 + syncV2);cert_link 也解耦命中判据(见 §1.1 第 6 项) |
| C 券 → 工单 | 写入工单 material_json.douyinMashidaVoucher |
发券后回填 + 建单时带入,两条路径都覆盖 |
仍需运营/产品:抖音内部 SKU 命名规范 + 按 SKU 补接单模板(见 11-接单模板待补清单.md)。
二、现状全链路梳理
2.1 kuaishou-91 全链路
flowchart TD
A["91 下单 API(验签+参数校验)"] --> B["buildOpen91SourceEvent<br/>provider=91kaquan / platform=kuaishou<br/>skuCode=中文商品名(productNo 去 ----店铺 后缀,后续会被覆写)"]
B --> C["upsertOrderFromSource"]
C --> D["resolveOrderItemForFulfillment<br/>分类"]
D -->|"命中 AFFILIATE_DASH_SKU_MAPPING"| E["isConfigured=true → 建 affiliate_dash 任务<br/>requires_claim → claim token → 领取页"]
D -->|"未命中(rules 空 + unmatched 空串)"| F["isConfigured=false → 不建任何任务"]
C --> G["syncWorkerOrdersForSourceOrder(autoOnly)<br/>按 work_product_rules 匹配"]
G -->|命中且 auto_create| H["建工单 → 打手抢单"]
G -->|未命中| I["不建工单"]
F --> J["有买家侧凭证但无执行载体<br/>(无工单无任务),order_status=paid"]
| 环节 | 事实 | 位置 |
|---|---|---|
| 进单 | createOpen91Order:验签 → 号码校验 → buildOpen91SourceEvent |
apps/backend/src/services/open-91/order-create-service.ts:26、apps/backend/src/services/platforms/ninetyone/order-service.ts:28 |
| SKU 口径 | 分类键 = 商品名(productNo 去 ----<shopId> 后缀);sku_code 有三种形态、不可作分类键(见 §3.3) |
ninetyone/order-service.ts:62、parseOpen91ProductNo:91 |
| 分类 | 规则表为空、unmatchedExecutorKey=''、优先级 [affiliate_dash] → 只有映射命中才有 executor |
app_config_entries.fulfillment_routing(生产库实测) |
| 任务 | 仅 configuredOrderItems(即命中映射的项)建任务;affiliate_dash 走 requires_claim=true |
services/order/order-service.ts:233-236、services/order/delivery-task-service.ts:55 |
| 发单 | 无条件对全部订单项跑接单模板匹配(autoOnly:true) |
services/order/order-service.ts:211、:241 |
| 电子凭证门禁 | 用 oid=platform_order_id 查快手电子凭证;未确认 → 暂停建任务,但工单照建 |
services/fulfillment/order-fulfillment-readiness-service.ts:17 |
| 领取 | 仅 ① 层有 claim token → 领取页(输 UID → 绑定 → 发货) | services/claim/claim-service.ts:152 |
| 后台 | 91 平台配置页有「待配置」列表 + 重试/标记失败 | services/platforms/ninetyone/order-service.ts:199(retryOpen91Order)、:222(failOpen91Order) |
生产库实测(2026-10-14):
| 指标 | 数量 |
|---|---|
| 91 订单 | 329,260 |
| └ 有快手电子凭证 | 329,219(≈100%) |
└ affiliate_dash 任务 |
46,103(另 kuaishou_ct_assisted 46,894 / kuaishou_feifei 7,007) |
| └ 接单平台工单 | 126,537 |
| └ 工单 ∩ 任务 | 0(严格互斥) |
| └ 既无任务也无工单 | 102,674(8 月 46,329 / 9 月 37,713 / 10 月 18,631;近 10 天约 1,000/天) |
后者的 order_status |
102,648 = paid,25 = pending_config,1 = manual_failed |
后者的快照 isConfigured |
近 10 天 10,208 项全部 false |
一个单子的构成(关键认知)
91 回调与快手 send-code 是同一个 kuaishou-91 来源的两条数据通道,工单不是独立的第三条线——sync-work-orders-from-send-code.ts:5-6 注释即权威口径:「91 回调是订单骨架权威源,快手 send-code 是数据补全源与兜底源」。两者共享同一店铺键空间与同一套匹配上下文,不是两个来源。
| 组成 | 说明 |
|---|---|
orders + order_items |
订单骨架(91 回调权威 + 快手 send-code 补全/兜底) |
kuaishou_industry_vouchers |
买家侧快手电子凭证(按 oid 关联;≈100% 订单都有) |
| 执行载体(二选一,实测交集 0) | 工单(打手交付) 或 fulfillment_tasks(affiliate_dash 自动发货) |
| 凭证归属 | 工单自带凭证:work_orders.material_json.kuaishouIndustryVoucher[](voucherCode / token / status / 有效期) |
→ 因此那 102,674 笔的准确说法是:有买家侧凭证,但既没派打手、也没走自动发货的裸单(需要显式人工队列,见 §4.3),而不是「另一条业务线」。
2.2 抖音 全链路(现状)
flowchart TD
A["trade_TradePaid 推送"] --> B["验签 + webhook_events 幂等"]
B --> C["order.orderDetail 拉详情"]
C --> D["buildDouyinMashidaSourceEvent<br/>skuCode=内部SKU(code) / skuName=引流长标题"]
D --> E["upsertOrderFromSource"]
E --> F["resolveOrderItemForFulfillment 抖音分支"]
F -->|"命中常量映射"| G["affiliate_dash 任务<br/>(skuCode 被覆写为英文 sku)"]
F -->|"未命中"| H["**强制** manual_dispatch 任务<br/>manual_review(无人工队列)"]
E --> I["syncWorkerOrdersForSourceOrder(autoOnly)"]
I --> J["实测 11 笔全 unmatched → 0 工单"]
E --> K["每个订单项都发 DYC 券 + coupons.syncV2"]
G --> L["付款后私信领取链接(仅①层)"]
H --> M["券 cert_link 兜底指 /h5"]
| 环节 | 事实 | 位置 |
|---|---|---|
| 进单 | 推送 → orderDetail 补齐 → upsertOrderFromSource |
services/platforms/douyin-mashida/notify-service.ts:112-229 |
| SKU 口径 | skuCode = 内部 SKU(code/out_sku_id/product_id),skuName = 引流长标题 |
douyin-mashida/order-mapper.ts:151-165 |
| 分类 | 抖音分支写死 preferredExecutorKey:命中映射→affiliate_dash,否则→manual_dispatch;候选只有这两个 |
services/fulfillment/product-resolution-service.ts:255-317 |
| 映射来源 | 只查代码常量表 AFFILIATE_DASH_SKU_MAPPING(不走后台增量、无英文透传) |
同上 :323-329 |
sku_code 污染 |
选中 affiliate_dash 后 skuCode 被替换为英文 sku,skuName 被替换为中文内部 SKU |
同上 :89-102 |
| 发单 | 与其他 provider 同样调用,但被 platform 过滤挡住 | work-product-match-mappers.ts:346-372(已于 2026-10-14 清空 platform,解除) |
| 券码 | 所有订单项都发券 + syncV2;cert_link 优先 claim 链接、否则 certLink(含 /h5 捷径) |
services/redeem/certificate-service.ts:61、:137-180 |
| 核销 | 领取完成 best-effort verifyV3;/redeem 支持手动输订单号核销 |
services/claim/kuaishou-cloud-claim-service.ts:529、services/redeem/redeem-service.ts:53 |
生产库实测(11 笔):命中映射 5 笔(3 link_generated / 2 redeemed,均为 lucky_coin_x2、suit_romantic_destiny)+ 未命中 6 笔(manual_dispatch / manual_review);接单模板匹配 11 笔全 unmatched、0 工单。
2.3 逐项差异对照
| 维度 | kuaishou-91 |
抖音(现状) | 改造方向 |
|---|---|---|---|
| 分类键 | 中文商品名(productNo) |
内部 SKU(code) |
统一为规范化 canonical SKU |
sku_code 落库 |
命中映射后被覆写成英文 | 同左(未命中时才是内部 SKU) | 不再覆写,英文 sku 只落 snapshot |
| 分类框架 | 走 resolveFulfillmentRoute(规则+优先级+unmatched) |
写死 preferred,绕过框架 | 复用同一框架 |
| 未命中兜底 | 不建任务(裸单) | 强制建 manual_dispatch 任务 |
显式「待人工」,不建任务 |
| 接单平台 | 命中 126,537 工单 | 0(已清空 platform,待新单验证) | 按 SKU 对齐商品名/编码 |
| 人工队列 | 有 UI 但链路已成死代码 | 无 | 新增显式状态 + 队列 + 重试 |
| 券码 | 同一个单子的买家侧快手电子凭证(挂在任务/工单上,≈100% 订单都有) | 自有 redeem_certificates + syncV2(等价于抖音版买家侧凭证) |
按分类决定是否发券 / 链接形态 |
| 领取链接 | 命中映射的才有 | 同左(人工单已修不再误发) | 保持 |
| 电子凭证门禁 | 有(发码回调未确认先暂停) | 无(抖音无此概念) | 不适用 |
三、生产库关键事实(改造的前提)
3.1 接单模板(662 条)
| 维度 | 实测 |
|---|---|
provider |
全部为空(通配) |
platform |
原 354 条 kuaishou,已于 2026-10-14 清空(现 662 条全通配) |
shop_id |
全部为空 |
sku_code |
仅 6 条有值(全是原 kuaishou 批次) |
match_json |
仅 4 条有值 |
| 商品绑定 | 662 条(1:1),但 seller_ids_json 有非空 CHECK → 抖音无 sellerId → 这条路径对抖音永远不可用 |
→ 抖音的模板匹配只能走「规则自身的 sku_code / product_name」(work-product-match-mappers.ts:303-343)。
3.2 履约路由配置(生产库)
{ "rules": [], "enabled": true,
"executors": { "affiliate_dash": {"enabled": true}, "manual_dispatch": {"enabled": true} },
"unmatchedExecutorKey": "", "defaultExecutorPriority": ["affiliate_dash"] }
→ 91 未命中时选不出执行器(isConfigured=false)→ 不建任务。抖音若照抄此配置语义,就天然等于「未命中不建任务」。
3.3 落库口径(决定匹配键)
91(生产库实测,329,248 个订单项) —— sku_code 有三种形态,不可作为分类键:
| 商品类型 | sku_code |
sku_name |
|---|---|---|
| 命中映射(①层) | 英文 sku(suit_romantic_destiny,如 330 笔) |
中文商品名(套装-浪漫天命) |
| 接口直接进单(走工单) | 91 productNo(中文商品名) |
中文商品名 |
| 被快手 send-code 补全后 | 平台数字 ID(189873094508762,占 244,322/329,248 = 74%) |
中文商品名(1个精致荣耀专属礼盒) |
抖音:
| 商品类型 | sku_code |
sku_name |
|---|---|---|
| 命中映射(①层) | 英文 sku(lucky_coin_x2) |
中文内部 SKU(幸运币2个) |
| 未命中(②③层) | 内部 SKU(套装-牛仔情诗) |
引流长标题(和平精英皮肤小店入口…) |
→ 结论:
- 91 的分类键只能是「商品名」(
sku_name/productNo)——这正是 662 条接单模板全部用product_name匹配的原因; - 抖音的分类键是「内部 SKU」(未命中映射时落在
sku_code上); - 所以 canonical SKU 必须按来源分别取,不能一律取
sku_code;抖音「命中后被覆写」的问题必须修掉。
3.4 91 的「待配置队列」是死代码(重要)
- 类型里有
UpsertOrderIgnoredResult(order-service.ts:63-69),但运行时从不返回ignored: true(全仓库仅出现在该类型定义处); createOpen91Order的if (result.ignored) → upsertOpen91PendingOrder()(services/open-91/order-create-service.ts:63)因此永不触发;- 生产库仅 25 笔
pending_config(全部 2026-08-08~08-11,早期版本产物); - 结论:91 未命中的订单现在既不建任务、也不进队列,只是
paid状态的裸单(102,674 笔)。
→ 抖音改造时不要复用这套死链路,应新建显式的人工状态与队列。
四、目标模型:按 SKU 的三层分类
4.1 统一分类键(canonical SKU)
canonicalSku(按来源分别取,禁止一律取 sku_code)
91 :商品名 —— sku_name / productNo(如「1个精英荣耀专属礼盒」「套装-浪漫天命」)
抖音 :内部 SKU —— sku_order_list[].code(如「幸运币2个」「套装-牛仔情诗」)
归一化:NFKC → 去所有空白 → 小写(复用 normalizeInternalSkuMatchKey)
落库约定(核心改动):
| 字段 | 约定 |
|---|---|
order_items.sku_code |
仅抖音:恒等于 canonicalSku(不再被执行器覆写);91 保持现状(sku_code 三种形态见 §3.3,其分类键取 sku_name) |
order_items.sku_name |
可读商品名(抖音取内部 SKU;引流标题另存 snapshot.productTitle) |
order_items.item_snapshot_json.classification |
新增:{ channel, canonicalSku, matchedRuleId, matchReason, decidedAt } |
item_snapshot_json.affiliateDash.sku |
英文 sku 只落这里(+ resolvedSkuCode) |
4.2 判定顺序(抖音与 91 共用)
输入:orderItem(canonicalSku, 商品名)
① 皮肤/道具自动发货
canonicalSku 命中 AFFILIATE_DASH_SKU_MAPPING(常量表 + 后台 skuMapping 增量)
→ executor=affiliate_dash;requires_claim;建 claim token;付款后发领取链接
→ 明确标记:不再参与接单平台发单(防重复交付)
② 接单平台发单
canonicalSku / 商品名 命中 work_product_rules(enabled + auto_create)
→ 建工单;**不建履约任务**;不发领取链接
(是否发券/券链接形态见 §4.4)
③ 人工
都不命中
→ isConfigured=false(不建任务);也不建工单
→ 标记 classification.channel='manual',进「待人工」队列(§4.3)
与 91 的对应关系:①②③ 完全同构;差别只在 ③ 由「裸单」升级为「显式待人工」。
4.3 人工层(③)的载体
| 设计点 | 方案 |
|---|---|
| 订单状态 | 不污染 order_status(保持真实 paid),用 order_items.snapshot.classification.channel='manual' 表达 |
| 队列判据 | 「无履约任务 且 无工单」= 待人工(可直接 SQL 筛,见 §7.3) |
| 后台入口 | 订单列表/抖音配置页新增筛选「待人工」+ 单笔「重新匹配」按钮(复用 POST /api/v1/admin/worker-platform/orders/reprocess-matches) |
| 重试语义 | 运营补好映射/模板后,重跑分类即可建任务或工单 |
| 通知 | 复用 notifyOpen91PendingConfig 类似机制给运营/群机器人告警(可选) |
若产品希望与 91 的
pending_config字面对齐,也可用order_status='pending_config',但会污染真实支付状态,不推荐。
4.4 券码与领取页按分类分流(需产品决策)
| 层 | 是否发券给平台 | cert_link |
领取页 |
|---|---|---|---|
| ① 自动发货 | 是(现状) | claim token 链接(现状) | 输 UID → 绑定 → 发货 |
| ② 接单平台 | 决策项 | 建议:不发券或指「人工/客服」页 | 不做领取交互 |
| ③ 人工 | 决策项 | 同上 | 现状已显示「人工处理中,请等待客服」 |
现状问题:②③ 也会发券且 cert_link 走 /h5(certificate-service.ts:151 的 /h5 捷径),买家点进去看到「人工处理中」,属误导。
五、改造方案(抖音侧,可自由重构)
5.1 数据层:canonical SKU 不再被覆写
resolveOrderItemForFulfillment(product-resolution-service.ts:69-137):skuCode不再随执行器替换;英文 sku 只写snapshot.affiliateDash.sku与snapshot.resolvedSkuCode。- 只对抖音生效(91 现状不动,避免影响 32.9 万历史与线上逻辑):在函数内按
isDouyinMashidaOrder(provider, platform)分支处理。 - 影响面需同步检查:
planner.buildFulfillmentTaskContext(用skuCode/skuName)、工单productName(用sku_name)、redeem-service.buildSummary、后台展示。
5.2 分类判定:抖音接入统一框架
改写 resolveDouyinMashidaItemCandidate(product-resolution-service.ts:255-317):
// 伪代码
const canonicalSku = 内部SKU
const dashSku = matchAffiliateDashSku(mergeAffiliateDashSkuMapping(config.skuMapping), canonicalSku)
if (dashSku) {
→ candidates=[{affiliate_dash, available:true}], preferred=affiliate_dash
} else {
→ candidates=[{affiliate_dash, available:false}],**不设 preferred**
// 让 resolveFulfillmentRoute 走规则/优先级/unmatched(当前配置下 unmatched='' → 不选执行器)
}
// 分类结果写 snapshot.classification
要点:
- 不再写死
manual_dispatch→ 未命中=不建任务(与 91 一致); - 顺带修好「后台履约路由规则对抖音无效」的问题(规则含
internal_sku维度); - 映射查找改为「常量表 + 后台增量」(对齐 91,新增商品不必发版)。
5.3 接单平台层对齐
| 事项 | 做法 |
|---|---|
| platform 限制 | 已执行:清空 354 条 platform=kuaishou(2026-10-14),抖音现在可命中 |
| 匹配键 | 运营按 sku_code = 内部 SKU + match_type=exact 建模板(见 11-接单模板待补清单.md) |
| provider 限制 | 已全为空(通配),无需改 |
| 代码侧 | 建议在 matchesRuleProviderPlatform 增加注释/白名单说明「抖音依赖 platform 留空」,避免后人又填回去 |
| 防重复交付(必做) | 命中映射的订单项 → 跳过建工单(sync-worker-orders-service.ts 循环内加守卫,判据:matchAffiliateDashSku(sku_code) 或已存在 executor_key='affiliate_dash' 的任务) |
5.4 人工层落地
order_items.snapshot.classification.channel='manual'(判定时写入);- 后台订单列表加筛选项「待人工(无任务无工单)」;
- 单笔/批量「重新匹配」入口(复用现有
reprocess-matches); - 可选:运营告警(同
notifyOpen91PendingConfig)。
5.5 券码与领取页
- ① 层:保持现状(有 token 才发领取链接、完成时自动核销);
- ②③ 层:按 §4.4 的产品决策实施(建议不发券 or 链接指人工页),并去掉
certificate-service.ts:151的/h5捷径(它是历史遗留,会让人工单拿到领取页链接)。
5.6 可观测
- 分类结果落
snapshot.classification; - 日志统一前缀
[douyin-mashida/classify],含canonicalSku / channel / matchedRuleId / reason; - 后台「匹配诊断」页已能看工单侧未命中原因,补充抖音分类结果展示。
5.7 存量数据
- 不回溯历史
sku_code(已落库的英文 sku 保留); - 存量 11 笔抖音单(6 笔人工)人工处理;
- 放行后只对新单生效。
六、改动清单(文件级)
| # | 文件 | 改动 | 风险 |
|---|---|---|---|
| 1 | services/fulfillment/product-resolution-service.ts:69-137 |
抖音:skuCode 不再被覆写(canonical SKU 恒为内部 SKU) |
中(需同步检查展示/任务/工单) |
| 2 | services/fulfillment/product-resolution-service.ts:255-317 |
抖音分类改写:不写死 preferred;未命中不选执行器;写 snapshot.classification |
低 |
| 3 | services/fulfillment/product-resolution-service.ts:323-329 |
抖音映射查找加后台增量(mergeAffiliateDashSkuMapping) |
低 |
| 4 | services/worker-platform/sync-worker-orders-service.ts(循环内) |
防重复交付守卫:命中映射/已有 affiliate_dash 任务 → 跳过建工单 | 低(纯守卫) |
| 5 | services/worker-platform/work-product-match-mappers.ts:346-372 |
注释/白名单明确「抖音依赖 platform 留空」;可选按 sku_code 提升匹配优先级 |
低 |
| 6 | services/order/order-service.ts |
分类结果透传(classification 写入 snapshot 已完成在 #1/#2);确认未配置时不再返回误导性的 ignored 语义 |
低 |
| 7 | services/platforms/douyin-mashida/source-config-service.ts |
新增开关:workerDispatchEnabled(默认 false)、claimMessageEnabled 已有 |
低 |
| 8 | services/redeem/certificate-service.ts:137-180 |
去掉 /h5 捷径;按 classification.channel 决定 cert_link(或是否发券) |
中(影响买家链接) |
| 9 | 后台(AdminOrdersPage / 抖音配置面板) |
「待人工」筛选 + 重新匹配入口 + 分类结果展示 | 低 |
| 10 | 后台(AdminPlatformFulfillmentPage) |
履约路由规则说明补充「抖音按内部 SKU 匹配」 | 低 |
| 11 | 文档 | 本文件 + 更新 10/11 号文档 | — |
七、测试计划
7.1 单测(新增/修改)
| 用例 | 期望 |
|---|---|
| 抖音 SKU 命中映射 | sku_code = 内部 SKU(不被覆写);snapshot.affiliateDash.sku = 英文 sku |
| 抖音 SKU 未命中映射 | isConfigured=false → 不建任务(无 manual_dispatch) |
| 抖音 SKU 命中接单模板 | 建工单,且不建履约任务 |
| 抖音 SKU 命中映射 + 同时命中模板 | 不建工单(防重复交付) |
| 抖音履约路由规则(internal_sku)命中 | 规则生效(现在被 preferred 短路) |
workerDispatchEnabled=false |
不建工单 |
| 91 回归 | 现有行为完全不变(sku_code 覆写保留) |
7.2 端到端(联调/灰度)
用 scripts/douyin-mashida-push.ts 造三类 SKU 的付款消息 → 校验:任务/工单/券/私信 四类产物与分类一致。
7.3 只读核对 SQL
-- 分类分布(改造后新单)
SELECT oi.item_snapshot_json::jsonb->'classification'->>'channel' AS channel, count(*)
FROM order_items oi JOIN orders o ON o.id = oi.order_id
WHERE o.provider = 'douyin_mashida' GROUP BY 1;
-- 待人工队列(无任务无工单)——91 现状 102,674 笔
SELECT count(*) FROM orders o
WHERE o.provider = 'douyin_mashida'
AND NOT EXISTS (SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_id = o.id)
AND NOT EXISTS (SELECT 1 FROM work_orders w WHERE w.order_id = o.id);
-- 防重复交付自查(应为 0)
SELECT count(*) FROM work_orders w
JOIN orders o ON o.id = w.order_id
JOIN fulfillment_tasks ft ON ft.order_id = o.id AND ft.executor_key = 'affiliate_dash'
WHERE o.provider = 'douyin_mashida';
八、灰度与上线
- 代码合并但不打开开关:新分类逻辑上线,
workerDispatchEnabled=false→ 抖音行为 = 「命中映射自动发货 / 否则待人工」,不建工单; - 验证人工层:确认未命中单不再产生
manual_dispatch任务,且能进「待人工」筛选; - 打开发单开关(小批商品先配模板)→ 观察工单量与匹配诊断;
- 全量放开:补齐抖音商品模板(见 11 号文档);
- 任一步异常:关开关即可回到第 1 步状态。
九、未决问题(需产品/运营确认)
- ②③ 层是否发券给抖音平台?(现状:全发;若不发会影响买家在抖店看到的卡券入口)
- ②③ 层的
cert_link指向:/redeem(买家自助兑换)还是「客服/人工」页? - 人工层表达:显式
classification.channel='manual'(推荐)还是沿用 91 的order_status='pending_config'? - 91 侧是否同步整改:91 的 102,674 笔裸单 + 死掉的待配置链路要不要一起修(本次设计对 91 只做「不改」,但问题客观存在)?
- 打手材料字段口径:抖音模板的
requirement_json需交什么素材(买家留言buyer_words?UID?); - 抖音内部 SKU 命名规范:同一商品出现过「套装-浪漫天命」与「浪漫天命」两种写法,需回推抖音侧统一。
十、附:运营配置清单(配合 §5.3)
- 接单模板按
sku_code = 抖音内部 SKU+ 精确匹配 新建(现 662 条里只有 6 条配了sku_code); - 已自动发货的商品(
幸运币2个、套装-浪漫天命等)不要建模板; - 平台/来源一律留空(现已全空,勿再填);
- 「适用商品绑定」对抖音无效(缺 sellerId),勿依赖;
- 详细逐商品清单见 11-接单模板待补清单.md。