23 KiB
配送对接接口规范(模拟三通一达后台 / Mock 承运方 Server)
本文档用于定义“模拟承运方后台服务(Mock Server)”的接口与行为,供平台在未接入真实承运方前完成联调与测试。
本文定位:
- 规定 Mock Server 与平台后端之间的交互(Webhook 推送 + 控制面 API)。
- 规定平台侧建议的统一事件模型(用于入库、时间线展示、监控告警)。
关联文档:
配送模块需求文档.md:目标/范围/验收与必须的故障注入状态映射表.md:状态码映射建议(Mock event_code -> 平台 status_code)前端字段清单.md:安卓/Web 时间线展示字段契约
包含两类接口:
- Mock Server -> 平台:Webhook 事件推送(模拟承运方回调)
- 平台/测试工具 -> Mock Server:控制面 API(创建运单、追加事件、运行场景、触发推送、查询轨迹)
统一约定
- 时间格式:ISO 8601(示例:"2026-02-05T10:30:00+08:00")
- 所有接口请求/响应均为 JSON,Content-Type: application/json
- 写操作建议携带
request_id用于幂等与排查 - 所有第三方回调应保存
raw_payload用于审计
补充说明(安卓端 & Web 端使用场景):
- 本文主要规范 Mock Server 与平台后端的交互;安卓/Web 客户端不应直接调用 Mock Server。
- Web 端联调时如需浏览器直连平台接口,应由平台网关配置 CORS(仅测试环境白名单),避免将 Mock Server 暴露给公网。
一、目标与约束
- 目标:用可控数据模拟关键配送节点与异常场景,覆盖验签、幂等、乱序/重复、延迟等测试。
- 约束:本规范服务于测试/预发布环境,不用于生产承运方直连。
二、核心数据模型(建议统一存储字段)
carrier:承运方标识(例如:YUNDA、YTO、KDN)tracking_no:运单号event_id:第三方事件唯一ID(用于去重)event_time:事件时间(ISO8601)event_code:第三方原始事件码(尽量不改写,用于审计与一致性对齐)event_text:第三方原始事件文案(前端时间线默认展示)status_code:平台统一状态(PENDING, IN_TRANSIT, ARRIVED_HUB, OUT_FOR_DELIVERY, DELIVERED, EXCEPTION)node_name:节点名称(中转站/网点/城市)location:节点位置描述(城市/网点地址,注意隐私)description:事件详细描述evidence_urls:证据照片/签名链接数组estimated_arrival:ETA(若承运方提供,可选)raw_payload:原始第三方 JSON(审计)last_synced_at:本地同步时间
三、接入模式
- 平台接收 Mock Server 的 Webhook 推送(推荐)。
- 平台亦可主动调用 Mock Server 查询轨迹(模拟轮询补偿)。
四、接口定义(示例)
- Mock Server -> 平台:事件推送(Webhook)
- URL: POST /webhook/express/status
- 描述: 承运方/聚合方主动推送运单事件(单条或批量事件)。平台应校验签名并返回 HTTP 200 表示成功接收。
- HTTP 头部(建议):
Content-Type: application/jsonX-Client-Id: 承运方或聚合方IDX-Timestamp: 推送时间戳(防重放)X-Signature: HMAC-SHA256(body + X-Timestamp) 使用双方共享 secret
- 回调示例(单事件): { "tracking_no":"YD123456789", "carrier":"YUNDA", "event_id":"e_20260205_0001", "event_code":"ARRIVED_HUB", "event_text":"到达北京分拨中心", "event_time":"2026-02-05T14:32:00+08:00", "node_name":"北京分拨中心", "location":"北京市朝阳区XXX", "evidence_urls":[], "raw_payload":{/* 原始承运方 JSON */} }
处理要求:
- 平台校验
X-Signature和X-Timestamp;若验签失败或时间差过大返回 4xx。若成功返回 HTTP 200。接收后异步完成映射与入库。 - 幂等:基于
event_id或tracking_no+event_code+event_time去重。
- 平台 -> Mock Server:轨迹主动拉取(模拟轮询)
- URL: GET /mock/v1/track?tracking_no={tracking_no}&carrier={carrier}
- 描述: 平台主动查询单运单轨迹,返回事件数组。
- 响应示例: { "tracking_no":"YD123456789", "carrier":"YUNDA", "events":[ {"event_id":"e1","event_code":"PICKED","event_text":"已揽收","event_time":"2026-02-04T18:00:00+08:00","node_name":"门店揽收网点","location":"北京市顺义"}, {"event_id":"e2","event_code":"ARRIVED_HUB","event_text":"到达北京分拨中心","event_time":"2026-02-05T14:32:00+08:00","node_name":"北京分拨中心","location":"北京市朝阳区"} ] }
- 平台:异常上报 API(用户/客服触发,平台侧,不属于 Mock Server)
- URL: POST /api/v1/delivery/express/report-exception
- 说明: 该接口为平台自身能力,Mock Server 不强制实现。
- 请求示例: { "tracking_no":"YD123456789", "reported_by":"user", "report_type":"damaged", "description":"包裹外包装破损,有压痕", "evidence_urls":["https://.../img1.jpg"] }
- 平台可选地回传给真实承运方(若未来接入)并记录回传结果。
- 平台/测试工具 -> Mock Server:控制面 API(建议实现)
4.1 配置回调目标
- URL: POST /mock/v1/config
- 描述: 设置 Mock Server 推送到平台的 Webhook 目标地址与验签参数。
- 请求示例: { "target_webhook_url":"https://api.yourplatform.com/webhook/express/status", "client_id":"carrier_mock_yunda", "secret":"shared_secret", "default_carrier":"YUNDA" }
4.2 创建运单
- URL: POST /mock/v1/waybills
- 描述: 创建一个 mock 运单,可由调用方指定或由服务生成。
- 请求示例: { "carrier":"YUNDA", "tracking_no":"YD123456789", "order_no":"202602050001" }
- 响应示例: { "carrier":"YUNDA", "tracking_no":"YD123456789", "created_at":"2026-02-05T14:00:00+08:00" }
4.3 追加事件(不推送,仅入库)
- URL: POST /mock/v1/waybills/{tracking_no}/events
- 请求示例: { "event_id":"e_20260205_0002", "event_code":"ARRIVED_HUB", "event_text":"到达北京分拨中心", "event_time":"2026-02-05T14:32:00+08:00", "node_name":"北京分拨中心", "location":"北京市朝阳区", "evidence_urls":[] }
4.4 运行预置场景(生成一组事件并可选立即推送)
- URL: POST /mock/v1/waybills/{tracking_no}/run-scenario
- 请求示例: { "scenario":"standard_delivered", "push":true, "inject":{ "delay_ms":300, "duplicate":0, "out_of_order":false, "bad_signature":false, "timestamp_skew_seconds":0 } }
4.5 触发推送(把该运单事件推到平台)
- URL: POST /mock/v1/waybills/{tracking_no}/push
- 请求示例: { "mode":"all", "inject":{ "delay_ms":0, "duplicate":1, "out_of_order":true } }
4.6 查询运单轨迹(Mock Server 自身查询接口)
- URL: GET /mock/v1/waybills/{tracking_no}/track
- 响应示例: { "carrier":"YUNDA", "tracking_no":"YD123456789", "events":[ {"event_id":"e1","event_code":"PICKED","event_text":"已揽收","event_time":"2026-02-04T18:00:00+08:00"} ] }
4.7 健康检查
- URL: GET /mock/v1/health
- 响应示例: {"status":"ok"}
五、字段映射与状态对照(示例)
- 平台统一状态
OUT_FOR_DELIVERY映射关系示例:- 韵达:承运方
派送中-> 平台OUT_FOR_DELIVERY - 聚合:原始
deliver-> 平台OUT_FOR_DELIVERY
- 韵达:承运方
状态一致性策略(尽量保持平台与第三方一致):
- 一致性定义:平台保存的
event_code/event_text与raw_payload应与第三方可查询到的轨迹语义一致;平台对外展示的status_code必须由映射表确定性生成(同一承运方同一event_code在同一版本映射下得到同一status_code)。 - 字段保真:
event_code/event_text仅做透传与脱敏展示,不建议“为了统一文案”而改写原文;统一展示口径使用status_code的标签/颜色/筛选实现。 - 映射表治理:映射表变更需要版本化(例如
mapping_version)并走发布流程;避免在生产环境频繁调整导致历史轨迹“同码不同态”。如必须调整,建议补充回放/回填策略以保证历史一致。 - 回查纠偏(可选增强):平台定期或按需对“关键单/争议单”触发第三方轨迹回查;若发现缺失事件则补采入库;若发现语义冲突,优先保留原始事件并追加“平台侧纠偏事件/备注”,避免静默覆盖造成审计对不上。
六、验签与安全
- 建议使用 HMAC-SHA256 签名,签名字段为
body + X-Timestamp,服务端使用共享secret校验。 - 防重放:校验
X-Timestamp与当前时间差(例如 < 5 分钟),并保存最近 N 个X-Signature或event_id用于去重。
七、幂等与重试策略
- webhook:承运方可能重试多次,平台必须基于
event_id或 (tracking_no+event_code+event_time) 去重。 - 主动调用承运方接口失败时采用指数退避重试;关键操作(打单)建议入队异步重试并人工告警。
入库层(幂等去重、乱序处理)建议:
- 幂等目标:同一条事件无论推送/重试多少次,最终数据库只保留一条(或同一条被安全更新),避免重复入库造成时间线膨胀。
- 幂等键优先级:
- 优先使用第三方提供的
event_id。 - 若缺少
event_id,使用兜底组合键:tracking_no + event_code + event_time(必要时可补充node_name/location以降低碰撞)。
- 优先使用第三方提供的
- 数据库约束:建议在事件表上建立唯一约束(例如
carrier + tracking_no + event_id或tracking_no + dedupe_key),写入采用 Upsert/Insert-Ignore,应用层无需“先查再写”来保证并发安全。 - 乱序处理:允许事件乱序写入(以免晚到的历史节点被丢弃);查询展示时按
event_time排序生成时间线。 - 状态不回退(平台侧可选规则):当先收到终态(如
DELIVERED)后又收到更早时间的在途事件时,不应将订单状态从终态回退;可采用“以最新event_time事件计算 current_status”或“按状态流转等级仅允许前进”的策略。
八、展示与 UI 要求(与前端对接点)
- 时间线按
event_time展示,标注carrier来源与last_synced_at。 - 节点可展开查看
description、node_name、location、evidence_urls(签收照片/回单)。若无证据则隐藏预览。 - 提供“查看第三方原文”链接,展示
raw_payload(仅客服/运维可见)。
平台对客户端返回建议(安卓/Web):
- 客户端请求应面向平台统一接口(例如:
GET /api/v1/delivery/express/track?order_no=...),由平台返回统一的status_history。 - 响应中建议包含:
carrier、tracking_no、status、status_history、last_synced_at,以及可选的eta。 raw_payload不建议下发给普通用户端;仅在 Web 客服/运维界面按权限下发并记录审计。
九、监控与告警
- 指标:webhook 接收成功率、平均同步时延、同步失败率、超过阈值未更新的运单数。
- 告警:承运方 5xx 增加、webhook 验签失败率异常、单运单长时间无更新。
十、示例 cURL(Webhook 验证场景模拟)
curl -X POST https://api.yourplatform.com/webhook/express/status \
-H "Content-Type: application/json" \
-H "X-Client-Id: carrier_yunda" \
-H "X-Timestamp: 2026-02-05T14:32:00+08:00" \
-H "X-Signature: <hmac-signature>" \
-d '{"tracking_no":"YD123456789","event_id":"e_20260205_0001","event_code":"ARRIVED_HUB","event_time":"2026-02-05T14:32:00+08:00","event_text":"到达北京分拨中心"}'
十一、日志与保留策略
- 建议保留
raw_payload与入库审计日志至少 30 天以便纠纷处理,审计记录包括接收时间、IP、头部信息与处理结果。
十二、兼容与扩展建议
- 建议实现承运商 Adapter 层:承运方差异在 Adapter 层转换为统一模型,便于后续扩展新承运方或替换聚合方。
- 若需签收照片或更高粒度信息,应优先与承运方签订企业级合同或直连,并在 UI 端明确注明凭证来源。
十二点一、平台后端适配架构(推荐实现方式)
目标:当不同第三方(直连承运方/聚合平台)API 结构、鉴权方式、事件码不同的时候,平台侧不修改核心业务与数据库结构即可接入。
核心原则:
- 统一领域模型入库:第三方差异在入库前完成映射;数据库保存统一事件字段 +
raw_payload。 - Adapter 可插拔:每家第三方实现一个 Adapter(验签/解析/查询/映射),核心服务只处理统一模型。
- 接入入口两条线:Webhook(推送)与 Poller(轮询补偿)最终都走同一条
EventIngestService。
模块划分(建议):
WebhookController:接收回调、路由到对应 Adapter、快速返回 200(业务异步处理)。CarrierAdapter(每家一个):验签、解析、字段映射、状态映射、(可选)轨迹查询。AdapterRegistry/Router:按carrier或X-Client-Id选择 Adapter(支持灰度与多配置)。EventIngestService:幂等去重、乱序处理策略、统一入库、写审计日志。TrackQueryService:给安卓/Web 提供统一查询接口(只读),不透出第三方差异。PollerJob:定时对“长时间无更新”的运单做轮询补偿(可按承运方频率配置)。
幂等与乱序建议(平台侧):
- 幂等键优先级:
event_id(优先) >tracking_no + event_code + event_time(兜底)。 - 入库:允许乱序写入;查询展示时按
event_time排序。 - 兼容缺字段:缺少
event_id时必须使用兜底组合键;缺少location/node_name时 UI 降级仅展示event_text。
架构图(Mermaid)
flowchart LR
subgraph Clients[客户端]
A[安卓 App] -->|GET 物流时间线| PAPI[平台 API]
W[Web/H5/PC] -->|GET 物流时间线| PAPI
end
subgraph Platform[平台后端]
PAPI --> TQS[TrackQueryService\n统一查询]
TQS --> DB[(Logistics DB\nWaybill + TrackingEvent)]
WH[WebhookController\n接收回调] --> REG[AdapterRegistry/Router\n按carrier或X-Client-Id路由]
REG --> AD1[YUNDA Adapter]
REG --> AD2[YTO Adapter]
REG --> AD3[Aggregator Adapter\n(快递鸟/快递100)]
AD1 --> ING[EventIngestService\n幂等/映射/入库]
AD2 --> ING
AD3 --> ING
ING --> DB
PJ[PollerJob\n轮询补偿] --> REG
PJ -->|queryTrack| AD1
PJ -->|queryTrack| AD2
PJ -->|queryTrack| AD3
end
subgraph ThirdParty[第三方/Mock]
MS[Mock Server 或真实承运方/聚合] -->|Webhook 推送| WH
end
回调时序图(Webhook 推送 -> 入库 -> 客户端展示)
sequenceDiagram
participant TP as Mock/第三方
participant WH as WebhookController
participant REG as AdapterRegistry
participant AD as CarrierAdapter
participant ING as EventIngestService
participant DB as LogisticsDB
participant API as 平台查询API
participant APP as 安卓/Web
TP->>WH: POST /webhook/express/status (headers + body)
WH->>REG: resolveAdapter(carrier/X-Client-Id)
REG->>AD: getAdapter()
WH->>AD: verify + parseWebhook(rawBody)
AD-->>WH: NormalizedEvent[]
WH-->>TP: 200 OK (快速返回)
WH->>ING: ingest(NormalizedEvent[])
ING->>DB: upsert events (幂等去重)
APP->>API: GET /api/v1/delivery/express/track?order_no=...
API->>DB: query events (order_no/tracking_no)
DB-->>API: events ordered by event_time
API-->>APP: timeLine(status_history + last_synced_at)
备注:Mermaid 图在不支持的 Markdown 渲染器中会降级为代码块,不影响内容阅读。
十三、附录:不同第三方 API 形态示例(用于理解差异)
说明:以下为“常见形态示例”,用于帮助团队理解不同第三方接口在鉴权、字段与能力上的差异。
- 不保证与任一承运方/聚合平台的官方文档 100% 一致;对接时必须以官方文档、沙箱与实际回文为准。
- 推荐做法:在平台侧实现 Adapter/Mapper,把第三方差异映射为本文定义的统一事件模型再入库。
13.1 聚合平台常见形态(如快递100/快递鸟类)
典型能力:
- 轨迹查询 API:传入
company_code + tracking_no(有些场景需要收件人手机号后四位) - 订阅/推送:先订阅,后续以 Webhook 回调推送事件
- 鉴权:常见
appKey + sign(MD5/HMAC)或 Token
示例:轨迹查询(示例)
POST https://api.aggregator.example.com/v1/track/query
Content-Type: application/json
{
"company_code": "yunda",
"tracking_no": "430123456789",
"phone_last4": "8000",
"nonce": "n_123",
"timestamp": 1738735200,
"sign": "md5_or_hmac_signature"
}
示例响应(示例)
{
"success": true,
"tracking_no": "430123456789",
"company_code": "yunda",
"state": "in_transit",
"events": [
{ "time": "2026-02-05T10:12:00+08:00", "context": "已揽收", "location": "深圳市" },
{ "time": "2026-02-05T22:01:00+08:00", "context": "到达广州分拨中心", "location": "广州市" }
]
}
差异点:
state/status枚举不统一;事件字段可能是context/desc、time/timestamp。- 不同快递公司可能要求手机号参与查询或订阅验证。
13.2 承运方直连常见形态(企业接口)
典型能力:
- 轨迹查询、签收回单(POD)查询可能拆成多个接口
- 鉴权更严格:HMAC、证书、IP 白名单或 OAuth2
示例:轨迹查询(示例)
POST https://open.carrier.example.com/v2/route/query
Content-Type: application/json
X-Client-Id: your_client_id
X-Timestamp: 2026-02-05T10:30:00+08:00
X-Signature: hmac_sha256(body + timestamp)
{
"tracking_no": "SF1234567890",
"include_pod": true
}
示例响应(示例)
{
"tracking_no": "SF1234567890",
"routes": [
{ "code": "PICKED", "desc": "已揽收", "time": "2026-02-05T09:10:00+08:00", "station": "深圳XX营业点" },
{ "code": "ARRIVED_HUB", "desc": "到达分拨中心", "time": "2026-02-05T21:45:00+08:00", "station": "广州分拨中心" }
],
"pod": { "signed": false, "photo_url": null }
}
差异点:
- 字段更细(网点编码、操作员等),但不统一;POD 能力通常受权限/合同影响。
13.3 Webhook 推送常见形态(订阅后回调)
典型能力:
- 订阅后由第三方主动推送轨迹事件到平台
- 具备重试机制,因此平台必须做幂等去重、乱序处理
示例:回调事件(示例,字段名因第三方而异)
{
"tracking_no": "430123456789",
"carrier": "YUNDA",
"event_id": "evt_0001",
"event_time": "2026-02-05T22:01:00+08:00",
"event_text": "到达广州分拨中心",
"location": "广州市",
"extra": { "pod_photo": null }
}
接入建议:
- 无论第三方字段如何变化,平台入库前统一映射到本文“核心数据模型”,并保留
raw_payload。 - 平台用
event_id(优先)或组合键(tracking_no+event_code+event_time)实现幂等。
13.4 圆通(YTO)物流轨迹查询接口(官方文档摘要)
文档入口:
定位:该文档描述“根据圆通运单号查询物流轨迹”的接口形态,典型属于“平台主动查询(轮询)”模式。
关键交互要点(按官方文档整理,具体以控制台配置与最新文档为准):
- 传输:HTTPS,POST。
- 报文结构:请求体包含
timestamp、param、format、sign。 param:以 JSON/XML 字符串承载业务参数;轨迹查询场景下包含圆通运单号字段(示例为NUMBER,一次查询一个单号)。
签名规则(按文档描述抽象):
- 参与签名的明文:
param + method + v(其中method与v来自控制台为该接口生成的配置)。 - 将上述明文与客户密钥(
secret)拼接后做 MD5,再对 MD5 的字节结果进行 Base64 编码,得到sign。 - 伪公式:
sign = base64(md5((param + method + v) + secret))
响应字段(按文档列举的 JSON 返回字段抽象):
- 运单号:
waybill_No - 走件时间:
upload_Time(yyyy-MM-dd HH:mm:ss) - 物流状态码:
infoContent(示例枚举包括 GOT/ARRIVAL/DEPARTURE/SENT_SCAN/SIGNED 等) - 物流信息文案:
processInfo - 城市/区县:
city、district(可选) - 重量:
weight(可选)
平台侧映射建议(把第三方差异收敛到统一模型):
- 时间:
upload_Time-> 平台event_time - 文案:
processInfo-> 平台event_text(可另存description) - 状态:
infoContent-> 平台event_code(保留原码),再映射到平台统一status_code(见状态映射表.md)
infoContent 到平台统一 status_code 的建议映射(示例):
- GOT(已揽收)-> IN_TRANSIT
- ARRIVAL(已收入/到达)-> ARRIVED_HUB
- DEPARTURE(已发出/离开节点)-> IN_TRANSIT
- SENT_SCAN(派件)-> OUT_FOR_DELIVERY
- INBOUND(自提柜入柜)-> IN_TRANSIT(或按业务定义为 OUT_FOR_DELIVERY)
- SIGNED(签收成功)-> DELIVERED
- FAILED(签收失败)-> EXCEPTION
- TMS_RETURN(退回)-> RETURNED
落库建议:
- 将圆通原始返回(整个数组或单条对象)保存到
raw_payload,便于客服/运维对照圆通官网。 - 幂等去重:若第三方无稳定
event_id,可用组合键tracking_no + infoContent + upload_Time生成dedupe_key。
13.5 韵达(YUNDA)开放平台(官方文档入口与调研清单)
文档入口:
说明:韵达开放平台文档页面存在较多交互式内容(需登录/控制台配置后才能看到每个接口的 method/v/测试地址 等关键信息)。因此本节先固化“对接时必须确认的要点清单”,避免对接过程中遗漏。
从官方文档导航可见的能力分类(用于判断覆盖范围):
- API 鉴权说明
- 电子面单、散件下单
- 物流轨迹
- 售后服务、国际件、基础服务等
韵达轨迹对接需要在控制台确认/落盘的信息(建议形成《承运方接入配置表》):
- 鉴权方式:签名算法(HMAC/MD5 等)、参与签名字段、编码/排序规则、是否包含时间戳与 nonce。
- 接口要素:轨迹查询接口 URL、
method、v(如平台/第三方采用“method+version+param”体系)。 - 订阅/回调能力:是否支持轨迹订阅与回调推送、回调重试策略、回调验签字段。
- 返回字段:事件时间字段、事件码/状态字段、事件文案字段、地点字段(城市/网点)与可选 POD 能力。
平台侧落地方式保持不变:
- 无论韵达返回结构如何,统一通过 Adapter 映射为平台
TrackingEvent领域模型入库,并保留raw_payload。
附:Mock 控制面通用错误码(示例)
- 40001: invalid_parameter
- 40002: missing_required_field
- 40901: duplicate_request
- 50001: internal_error
可选增强(非本期必需):
- 生成 OpenAPI 文档(控制面 API + Webhook 示例)
- 补齐“字段必填矩阵/容错矩阵”(配合
drop_fields故障注入)