Skip to content

物流通知:承运商事件、状态归并与用户触达 ​

物流状态通常来自承运商回调,缺少回调能力时按运单定期查询。用户打开订单页可以请求刷新,但后台同步仍要覆盖长期不登录的用户。

示例只对发货、派送、签收和异常等关键业务变化通知用户,普通轨迹明细保存在查询页,不把每条轨迹都转成一次推送。

从查看轨迹推导后台归并 ​

假设产品只提供“点击查询物流”的工具,按用户操作调用渠道可以成立。增加“发货和签收主动提醒、客服能追溯异常”的要求后,后台需要独立接收或补查,并保存可核对的历史事实。

候选方案能提供什么需要承担的成本
页面查询时直接调用渠道当前点击的即时查询未访问用户没有主动同步,突发点击消耗配额
周期查询所有未完结运单不依赖用户在线查询配额、延迟和终止条件
持久化回调并配合分层补查实时接续与缺失修复去重、乱序、状态归并和原始证据

本篇选择第三种,并把“渠道报告了一条事件”与“本地接受了一次业务转换”分开。通知绑定本地有效转换,才能避免同一来源消息重发时再次通知用户。

回调接收与状态归并时序 ​

接口只有保存原始事件成功后才确认接收。归并 Worker 按当前版本处理,渠道响应成功与本地状态已更新是两个阶段。

签收后迟到的运输中事件与合法退回事件不能用同一条“只取最大状态”规则处理。用户通知以接受的本地转换为身份,再独立进行渠道分发。

物流需求与完成标准 ​

示例覆盖订单拆包、多运输段、异常与退回:用户看到当前确认状态和更新时间,客服能追溯原始轨迹,关键变化生成通知。页面“物流未更新”与“包裹异常”是不同结果。

业务问题示例决策开发约束
一单多包何时算发货区分部分发货与全部发货包裹状态与订单汇总分离
什么事件要通知关键有效转换,普通轨迹只入历史转换 ID 与通知意图关联
查询渠道不可用怎么办保留最后确认状态,标注更新时间同步状态与物流状态分离
单号复用怎么办运单绑定承运商账户与运输段身份不单独拿运单字符串当永久主键
用户投诉怎样定位能找到来源事件、归并规则和发出通知原始数据与处理版本可追踪

先保存外部事件,再更新业务状态 ​

信息用途
承运商与运单身份区分来源,处理单号复用或不同运输段
来源事件 ID识别同一事件的重复投递
发生时间与接收时间分析迟到,而不是只保留一个时间
原始状态与轨迹支持规则修订和历史重放
本地状态与版本决定产品展示和后续动作

接收回调时校验来源并持久化,再异步处理。没有可靠事件 ID 时,需要根据渠道字段定义去重指纹,并承认指纹碰撞和近似去重的边界。

事件身份可以采用来源与事件 ID 组合的思路;CloudEvents 规范也以 source 与 id 标识事件。具体承运商字段与语义仍以它自己的接口契约为准。

数据结构与接口 ​

记录关键字段与约束
shipments订单、承运商账户、单号、运输段、当前状态、归并版本、最后确认时间
shipment_inbox运单及运输段关联、来源、事件 ID 或约定指纹、发生与接收时间、原始数据、处理状态
shipment_transitions本地转换 ID、前后状态、证据、归并规则版本
shipment_sync下次查询时间、尝试次数、最近成功时间和待核对原因
tracking_subscriptions运单与运输段、供应商订阅身份、订阅状态、最后确认时间与失败原因
shipment_scan_runs本轮扫描截止时间、分页游标与推进检查点
通知记录本地转换 ID、收件对象、渠道与发送结果

回调入口验证渠道签名、租户映射和事件格式后持久化,再返回渠道约定的成功响应。重复事件也可返回已接收结果;数据库没有保存成功时,不能提前声称已接收。

用户刷新接口优先返回缓存状态,并按去重规则建立一次补查任务;多个用户同时刷新同一运单,不应直接向渠道发出相同数量的同步查询。

状态归并处理器 ​

text
领取待处理事件,读取运单当前状态与归并版本
规则能确定合法转换:准备转换候选
规则无法确定:在事务外查询渠道,保存可引用的查询证据
BEGIN
  锁定运单,重新检查归并版本是否仍与读取时相同
  若已变化:退出本次候选,重新评估,不覆盖新状态
  若需要有效转换:
    写入 shipment_transition 并推进运单状态与归并版本
    为本地转换 ID 保存通知意图
  标记原始事件已处理或待进一步核对
COMMIT

本地转换 ID 只在真正接受状态变化时产生,重复来源事件不会生成新的转换。渠道查询结果也要记录自己的确认时间与身份,不能把慢查询拿到的旧快照无条件覆盖新回调。

归并版本用于检测“我读取之后,当前事实是否又变化了”。它不判断承运商事件本身的真伪;真实性来自来源校验与渠道契约,合法状态变化来自业务转换规则,三者各有职责。

重复和乱序怎样处理 ​

同一条发货回调收到三次,原始事件去重后只形成一次有效状态变化。签收已经确认后,迟到的普通运输中事件不应直接把页面退回运输中。

但物流并非永远单向前进:拒收、退回、签收纠正和再次派送可能是合法变化。需要显式的状态转换规则和运输段标识,不能只给状态排一个数字然后永远取最大值。

来源有可信版本号时按契约使用;只有时间戳时,不能仅凭本地接收顺序建立事实顺序。冲突事件可以保存后查询承运商当前结果,记录归并依据。

1000 个包裹同时发货 ​

每个运单独立保存状态与通知意图,通知阶段再按用户、订单和时间窗口决定是否汇总。一个订单拆成多个包裹时,要明确“订单已发货”表示部分发货还是全部发货。

发现批次、状态更新批次与渠道发送额度分别控制。第 200 个运单的短信失败,不影响其他运单状态已经更新,也不让整批用户重新收到推送。

通知逻辑键可以采用 shipment_id + normalized_transition_id + channel。同一运输段内相同状态的重复回调不重复发送;再次派送形成的新轮次仍可通知。

用户离线和页面展示 ​

站内通知先保存供下次访问查看,App 推送通过可用设备渠道发送。页面重新读取最新物流状态,消息链接不要把旧轨迹直接当成当前状态。

供应商接受推送不等于用户看到通知。应分别记录通知意图、提交结果与可获得的送达或已读回执。

补查频率与停止条件 ​

运单阶段示例策略
新发货但尚无轨迹在渠道允许范围内等待并补查
运输中周期更新,按时效和查询配额控制频率
物流异常建立异常处理任务,必要时通知负责人
已签收减少主动查询,保留渠道纠正事件的处理能力

1000 条运单分散补查时间,避免每轮整点同时打满渠道额度。查询游标、失败次数与最老未更新运单年龄需要可追踪;渠道临时不可用时保留上次确认状态,并显示更新时间。

这里的游标是 shipment_scan_runs 中的批量扫描进度,单运单的下次补查与失败次数属于 shipment_sync,订阅是否停止则属于 tracking_subscriptions。三者分别表示发现进度、同步进度与供应商订阅生命周期,不能用包裹的运输状态代替。

批量补查的检查点 ​

补查扫描固定本轮截止时间,按 next_poll_at + shipment_id 分页发现候选;本页补查任务可靠保存后才推进扫描检查点,独立任务再调用渠道。若任务创建与检查点不能同事务提交,按稳定任务业务键重放本页,不能先推进游标再建任务。

单运单的下一次查询时间、失败原因与本次确认结果一起提交;有新的有效回调时可以重新计算补查计划。next_poll_at 是可变排序键,本轮分页不代替后续重新扫描,扫描推进记录也不代表所有运单已经同步完成。

对已签收、已退回等状态设置不同的查询策略与最终复核窗口,而不是永久无限轮询。未解决的异常进入客服待办;渠道配额不足以满足承诺时,显示同步延迟并告警。

一次乱序回调与补查竞争 ​

假设运单 S5 当前为运输中,归并版本为 10:

动作本地记录应采取的判断
补查读取版本 10 后请求渠道尚未发布查询结果外部调用不持有长事务
新签收回调先到并确认保存转换 T11,归并版本推进为 11保存签收通知意图
较慢的补查返回运输中请求依据仍是版本 10检查冲突后重新核对,不覆盖签收
相同签收回调重发命中已有来源事件不再生成一份 T11 通知
后续收到合法退回证据保存原始事件并按运输段规则评估可以产生新转换,不靠状态数字大小拒绝

如果仅把每次收到的 status 覆盖到运单表,就无法区分迟到旧状态和合法纠正。保留原始证据和归并结果,可以在规则修订时重放核对,而不篡改当时收到的事实。

验收与排查依据 ​

场景应断言的结果
同一来源事件重发 10 次原始身份去重,只有一次有效转换和通知意图
签收后收到迟到运输事件不回退;原始事件仍可追溯
签收后收到有效退回事件按运输段规则处理,不因状态数字较小而丢弃
页面 100 次刷新同一运单补查请求合并,不打出 100 次渠道查询
渠道慢查询与新回调竞争归并版本阻止旧查询覆盖新状态
一单三个包裹仅一个发货页面和通知按部分发货规则展示

观察原始事件滞留、待核对事件年龄、最后确认状态年龄和渠道限流。告警应能指向运单归并证据,而不把每次接口波动都发给用户。

快递100接入说明中的订阅状态 ​

腾讯云开发者社区里,“快递100API”发布的接口问题说明给出了一个具体边界:停止跟踪的通知也需要返回协议要求的成功响应,否则会被重推;重开订阅、重推上次内容和关闭跟踪是不同操作。这是 2021 年的供应商接入经验,不能作为腾讯云自身物流架构或当前所有接口参数的说明。

本文据此建议把 tracking_subscription_status 与包裹的 shipment_status 分开保存。“停止跟踪”不证明包裹已经签收或丢失,回调处理成功也只证明消息已接收。先持久化接收记录,再返回确认,之后由归并任务判断是否更新轨迹、继续补查或进入人工异常处理。

如果 1000 个单号同时出现订阅异常,应按原因分类修复并限制重开速率,避免无条件重订阅形成调用风暴。历史文中的重试次数和时间不能硬编码为通用规则;上线时需按所接服务的现行协议核对签名、确认报文、重试及订阅终止条件,本文原有的来源校验仍须保留。

美团即时物流:业务分区与变更同步 ​

美团的即时物流分布式系统架构文章讨论了分区扩容、按区域或城市路由,以及通过数据库变更同步支撑其他存储的最终一致性。这是物流平台的整体架构经验,并未公开本文所用的承运商回调归并算法。

对物流通知的启发是:同一运单的回调、补查和重放,应能定位到当前负责它的业务分区;路由或容灾切换后不能让新旧分区各自建立一份有效通知。可以将稳定运单身份、路由信息与归并版本一并记录。业务状态落库后若还有缓存、查询索引或通知副本,需分别定义同步进度与恢复方式,不能把回调接口成功返回当作所有副本已经更新。

阿里云 EventBridge:重试耗尽后如何保留待处理轨迹 ​

EventBridge 的事件流重试和死信说明区分重试策略与异常容错行为:按配置,失败可能进入死信、被丢弃或阻塞任务;死信并非默认启用。这是事件流产品的具体行为,不应套用于所有消息队列。

若采用事件流转发物流轨迹,应明确坏消息能否阻塞后续轨迹、重试耗尽后谁处理,以及如何从原始接收记录重新投递。本文建议将无法识别的承运商状态隔离并告警;重放时沿用原事件身份,经当前归并规则判断,不能重新制造发货或签收通知。运单本身的运输异常与技术投递死信需要分别运营。

参考资料 ​