← 返回蜂巢洞察

主流电商平台订单怎么进自建仓?我们踩过推送不 ACK、漏历史单这两坑

仓库要打单、财务要对账,平台店加私域却各出各的单。本文按真实对接顺序写:WebSocket 怎么接、为什么必须 ACK、增量拉单怎么补历史,示例对齐蜂巢开放平台文档(https://doc.fw199.com/docs/h7b/)。适合正在写订单中台的研发。

主流电商平台订单怎么进自建仓?我们踩过推送不 ACK、漏历史单这两坑

上周五下午,已经高高兴兴背上托特回家,仓库同事在群里甩了张截图被同事艾特:主流电商平台后台有一单待发,我们 ERP 里没有。财务同时在催:私域小程序单和平台店订单对不齐,不得已周末加班。

解决问题要彻底,本质上这并不是「有没有订单」的问题,是线上单怎么一比一地灌进自家系统

现在不只是我们,很多人第一反应都是每个平台都接一遍开放平台。结果:这家一套、那家一套、授权一套、字段再映射一套——能做,但工期和合规成本直接劝退。最佳实践是:店铺授权到统一能力层,自己系统只对接一套消息 + 一套拉单 API。我们用的是蜂巢开放平台

下面按「怎么接」写,执行层面全干货,不讲模糊的概念。

先想清楚:推送和拉取都要,别图省米二选一

对接时我们就吃过两次暗亏:

  1. 只接了 WebSocket:新单是实时了,商家重新授权那天,历史空窗没人补,仓库又喊漏单。
  2. 只靠定时任务拉单:延迟到分钟级,大促时财务说「客户都催发货了系统里才冒出来」,所以还是得实时数据同步。

文档里写得很直白:推送实时,但不推历史;拉单能补历史和漏单,但要自己调度。两边一起上。

大概长这样:

订单同步架构:主流电商平台授权到蜂巢开放平台,经 WSS 推送与 HTTP 拉取进入企业订单表
订单同步架构:授权一次,WSS 推送 + HTTP 拉取进入企业订单表

私域单本来就在你们库里,platform=private 塞进同一张表即可。平台单用 platform + seller_nick + tid 做唯一键,别只拿 tid——多店会撞。

开工前准备

  • 控制台拿 AppId / AppSecret,余额别空(空了调用会莫名失败,我们排障排过一次才发现)
  • 商家完成店铺授权(授权说明见开放平台文档目录)
  • WSS 的 token 文档写法:MD5(AppSecret + AppId + AppSecret)

WebSocket:实时进单(别忘了 ACK)

连接:

wss://kf.fw199.com/acc?appid=YOUR_APPID&token=YOUR_TOKEN&version=v2.0&clientid=erp-order-01
String token = Utils.MD5(Config.AppSecret + Config.AppId + Config.AppSecret);
String url = "wss://kf.fw199.com/acc"
        + "?appid=" + Config.AppId
        + "&token=" + token
        + "&version=v2.0"
        + "&clientid=erp-order-01";
webSocketClient = new EasyWSClient(new URI(url));
webSocketClient.connect();

两件琐事必须做:大约 30 秒发一次 {"cmd":"beat"},以及断线重连。不做心跳,连着连着就被踢。

买家付款后常见 topic:tb_push_wait_seller_send_trade(具体 topic 以文档为准)。坑点:data 看起来像 JSON,文档说是字符串,要再 parse 一次。我们第一次直接当对象取字段,全是 null,还以为平台没推。

{
  "uuid": "20201010221640251138",
  "code": 0,
  "msg": "success",
  "topic": "tb_push_wait_seller_send_trade",
  "data": "{...tid / seller_nick / status / orders...}"
}

收到带 uuid 的消息一定要回执,否则会重推、积压,严重时还影响费用:

{"cmd":"ack_sync_data","seq":"20201010221640251138"}

处理建议:先打日志(uuid、tid、seller_nick)→ 丢队列 → 尽快 ACK → 业务侧按 tid 幂等 upsert。onMessage 里别同步调打印、别同步写一长串 SQL。

@Override
public void onMessageEvent(String message) {
    logger.info("ws_raw={}", message);
    SyncTradeResponse resp = JSON.parseObject(message, SyncTradeResponse.class);
    if (resp.getCode() != 0) {
        logger.warn("biz_error={}", resp.getMsg());
        return;
    }
    if ("tb_push_wait_seller_send_trade".equals(resp.getTopic())) {
        SyncTrade trade = JSON.parseObject(resp.getData(), SyncTrade.class);
        orderService.upsertFromPlatform("mall", trade);
    }
    if (resp.getUuid() != null && !resp.getUuid().isEmpty()) {
        send("{\"cmd\":\"ack_sync_data\",\"seq\":\"" + resp.getUuid() + "\"}");
    }
}

多店一般一个开发者开一条 WSS 就够,靠 seller_nick 区分。两个网站共用一个 AppId 各挂一条 WSS,消息可能只落到其中一台——生产别这么玩。

其它 topic(发货、退款、交易成功)见:https://doc.fw199.com/docs/h7b/ws-message

增量拉单:补历史、补授权空窗

推送救不了「授权前的单」。授权回调、对账、仓库喊漏单时,用增量列表按修改时间拉。注意:单次跨度 ≤ 1 天,文档还建议窗口尽量控制在 30 分钟内,成功率更高。

接口说明:https://doc.fw199.com/docs/h7b/tb-order-increment-list

常用参数:appid / timestamp / signtb_seller_nick(卖家登录账号,不是店铺名)、start_modified / end_modifiedstatus(如 WAIT_SELLER_SEND_GOODS)、page_no / page_size(最大 30)、use_has_next=true

@Test
public void getIncrementTradeList() throws Exception {
    Map<String, String> data = new HashMap<>();
    data.put("appid", Config.AppId);
    data.put("tb_seller_nick", Config.SellerNick);
    data.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
    data.put("start_modified", "2026-08-20 08:00:00");
    data.put("end_modified",   "2026-08-20 08:30:00");
    data.put("status", "WAIT_SELLER_SEND_GOODS");
    data.put("page_no", "1");
    data.put("page_size", "20");
    data.put("use_has_next", "true");
    data.put("sign", Utils.Sign(data, Config.AppSecret));
    // 增量列表 URL 以开放平台文档为准
    doHttpRequest(Config.TradeIncrementListUrl, data);
}

code == 0 后从 data.trades.trade[] 取。列表字段不够就按 tid 拉详情:https://doc.fw199.com/docs/h7b/trade-detail

data.put("tid", "1189348257255565830");
data.put("sign", Utils.Sign(data, Config.AppSecret));
doHttpRequest(Config.OrderDetailUrl, data);

水位怎么记:本地存 last_modified。上次到 T,下次从 T 拉到 T+Δ。授权刚回来,先补近 24~48 小时;落后太离谱(比如超 72 小时),先追最近一天再往后挪——具体频率你们按单量定。

增量的好处是:早上 8 点那单中午被改备注/改状态,你不用从凌晨重扫,从上次水位继续拉也能捞到。

表怎么建才够仓和财务用

我们落库最小集大致是:

platform, seller_nick, tid          -- 唯一键三件套
status, payment, post_fee
pay_time, modified                  -- modified 给增量水位
buyer_message, seller_memo
raw_json                            -- 对账扯皮时救命
updated_at
UNIQUE(platform, seller_nick, tid)

状态映射对照推送文档里的 WAIT_SELLER_SEND_GOODSWAIT_BUYER_CONFIRM_GOODSTRADE_FINISHEDTRADE_CLOSED 即可。推送和拉取走同一套 upsert,不然双通道必出重复单。

漏单时我们实际怎么查

别一上来就怪平台。顺序一般是:

  1. 授权是否过期(Session 过了必须让商家重新登一次)
  2. 对应消息 topic 有没有开通
  3. 本地日志有没有收到这个 tid——收到没入库,是自己的 bug
  4. 有 uuid 有没有 ACK
  5. 用增量接口按修改时间窗补一把,确认平台侧有没有这单

收束

要让主流电商平台订单和私域单进同一仓库、同一财务口径:实时靠 WSS(心跳、ACK、幂等),完整靠增量/详情拉单,归集靠一张统一订单表。

字段和各平台差异以文档为准:

你那边若卡在「推送有、库没有」或「授权后空窗」,评论区丢一下现象,我按排查顺序帮你对一下。

相关文章