# 配送对接接口规范(模拟三通一达后台 / Mock 承运方 Server) 本文档用于定义“模拟承运方后台服务(Mock Server)”的接口与行为,供平台在未接入真实承运方前完成联调与测试。 本文定位: - 规定 Mock Server 与平台后端之间的交互(Webhook 推送 + 控制面 API)。 - 规定平台侧建议的统一事件模型(用于入库、时间线展示、监控告警)。 关联文档: - `配送模块需求文档.md`:目标/范围/验收与必须的故障注入 - `状态映射表.md`:状态码映射建议(Mock event_code -> 平台 status_code) - `前端字段清单.md`:安卓/Web 时间线展示字段契约 包含两类接口: 1) Mock Server -> 平台:Webhook 事件推送(模拟承运方回调) 2) 平台/测试工具 -> 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`:平台统一状态(ORDER_PLACED, SHIPPED, IN_TRANSIT, OUT_FOR_DELIVERY, READY_FOR_PICKUP, DELIVERED, EXCEPTION, RETURNED) - `node_name`:节点名称(中转站/网点/城市) - `location`:节点位置描述(城市/网点地址,注意隐私) - `description`:事件详细描述 - `evidence_urls`:证据照片/签名链接数组 - `estimated_arrival`:ETA(若承运方提供,可选) - `raw_payload`:原始第三方 JSON(审计) - `last_synced_at`:本地同步时间 ## 三、接入模式 - 平台接收 Mock Server 的 Webhook 推送(推荐)。 - 平台亦可主动调用 Mock Server 查询轨迹(模拟轮询补偿)。 范围说明(避免误解): - 若要实现“电子面单/在线下单自动获取运单号”,需要对接第三方(快递公司直连或聚合平台)提供的下单/面单接口。 - 该类“下单/面单”接口不属于本 Mock Server 接口规范范围;本文只覆盖轨迹事件的推送/查询与相关验签、幂等与字段口径。 ## 四、接口定义(示例) 1) Mock Server -> 平台:事件推送(Webhook) - URL: POST /webhook/express/status - 描述: 承运方/聚合方主动推送运单事件(单条或批量事件)。平台应校验签名并返回 HTTP 200 表示成功接收。 - HTTP 头部(建议): - `Content-Type: application/json` - `X-Client-Id`: 承运方或聚合方ID - `X-Timestamp`: 推送时间戳(防重放) - `X-Signature`: HMAC-SHA256(body + X-Timestamp) 使用双方共享 secret - 回调示例(单事件): { "tracking_no":"YD123456789", "carrier":"YUNDA", "event_id":"e_20260205_0001", "event_code":"TRANSIT", "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` 去重。 2) 平台 -> 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":"ARRIVAL","event_text":"运输中","event_time":"2026-02-05T14:32:00+08:00","node_name":"北京分拨中心","location":"北京市朝阳区"} ] } 3) 平台:异常上报 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"] } - 平台可选地回传给真实承运方(若未来接入)并记录回传结果。 4) 平台/测试工具 -> 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":"TRANSIT", "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: " \ -d '{"tracking_no":"YD123456789","event_id":"e_20260205_0001","event_code":"TRANSIT","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) ```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 推送 -> 入库 -> 客户端展示) ```mermaid 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 示例:轨迹查询(示例) ```http 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" } ``` 示例响应(示例) ```json { "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 示例:轨迹查询(示例) ```http 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 } ``` 示例响应(示例) ```json { "tracking_no": "SF1234567890", "routes": [ { "code": "PICKED", "desc": "已揽收", "time": "2026-02-05T09:10:00+08:00", "station": "深圳XX营业点" }, { "code": "TRANSIT", "desc": "运输中", "time": "2026-02-05T21:45:00+08:00", "station": "广州分拨中心" } ], "pod": { "signed": false, "photo_url": null } } ``` 差异点: - 字段更细(网点编码、操作员等),但不统一;POD 能力通常受权限/合同影响。 ### 13.3 Webhook 推送常见形态(订阅后回调) 典型能力: - 订阅后由第三方主动推送轨迹事件到平台 - 具备重试机制,因此平台必须做幂等去重、乱序处理 示例:回调事件(示例,字段名因第三方而异) ```json { "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://open.yto.net.cn/interfaceDocument/menu251/submenu258 定位:该文档描述“根据圆通运单号查询物流轨迹”的接口形态,典型属于“平台主动查询(轮询)”模式。 关键交互要点(按官方文档整理,具体以控制台配置与最新文档为准): - 传输: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(已收入/到达)-> IN_TRANSIT(中转/到达节点统一视为运输中) - 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)开放平台(官方文档入口与调研清单) 文档入口: - https://open.yundaex.com/api/apiDoc 说明:韵达开放平台文档页面存在较多交互式内容(需登录/控制台配置后才能看到每个接口的 `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` 故障注入)