# 物流消息推送方案(用户端 + 商家端) 日期:2026-02-11 ## 0. 文档定位 本方案用于把“第三方快递轨迹(platform_express_*)”与移动端推送(uni-push2)结合,向: - **用户端(C 端)**:推送订单物流节点更新,并在消息中心可回溯、可标记已读。 - **商家端(B 端)**:推送与发货/履约强相关的物流节点更新(重点关注异常/签收等),并在商家消息中心可回溯。 约束与口径继承自本目录既有文档: - 统一事件模型、字段契约、状态映射:见接口与字段相关文档。 - 敏感信息(raw_payload、完整手机号/地址、POD 等)必须按角色授权,**不得通过推送明文下发**。 ## 1. 背景与目标 ### 1.1 背景 配送模块的事实数据源是第三方轨迹事件流:Webhook/轮询接收后入库到 `platform_express_*` 三表。 ### 1.2 目标 1) **及时性**:当运单产生关键节点时,用户/商家能收到提醒。 2) **一致性**:推送只是“变更通知”,最终展示以平台查询接口返回的 `status_history` 为准。 3) **可追溯**:所有推送对应的“物流消息”可在消息中心列表查询,并可已读/未读统计。 4) **合规与隐私**:推送 payload 与通知文案不包含敏感信息,不透传 raw。 ### 1.3 不在本方案范围 - 自营骑手/同城配送任务(`ml_delivery_*`)的接单/导航/送达链路。 - 厂商通道(小米/华为等)离线能力细节(可在 uni-push2 联调文档基础上扩展)。 ## 2. 数据源与现有模型复用 ### 2.1 事实数据表(生产口径) - `platform_express_waybills`:运单摘要(current_status_*、last_synced_at)。 - `platform_express_tracking_events`:轨迹事件事实表(event_time/event_text/status_code 等)。 - `platform_express_event_raw`:原始留痕与验签审计(仅运维/客服可见)。 结论: - **消息生成以 `platform_express_tracking_events` 的新增事件为触发**。 - **通知展示以 `platform_express_waybills.current_status_code/text` + 最新事件摘要为基础**。 ### 2.2 统一状态码(必须遵循) 使用平台统一 `status_code`: `ORDER_PLACED / SHIPPED / IN_TRANSIT / OUT_FOR_DELIVERY / READY_FOR_PICKUP / DELIVERED / EXCEPTION / RETURNED` 映射规则来自“状态映射表”,平台内部应保证确定性。 ## 3. 总体架构(事件驱动 + 推送唤醒刷新) ### 3.1 核心原则 - **Push ≠ 数据源**:Push 仅承载“发生了什么/在哪里看”,不承载完整时间线。 - **App 打开详情页时以接口/DB查询为准**:避免推送丢失、乱序导致前端状态不一致。 ### 3.2 推荐链路 1) 第三方回调/轮询到达平台(Webhook handler / polling job) 2) 平台:验签、防重放、幂等去重、乱序入库 3) 平台:基于“新插入事件”计算是否需要生成物流消息(对 C/B 端分别判断) 4) 平台:写入“物流消息表”(用于消息中心/未读) 5) 平台:按收件人设备 CID 列表调用 uni-push2 发送通知/透传 6) 客户端:收到推送 -> 更新消息中心未读角标/缓存 -> 用户点击进入订单/物流详情 -> 拉取最新时间线 ## 4. 推送场景清单(C 端 + B 端) ### 4.1 场景分级(建议最小可用) 为了避免 IN_TRANSIT 等高频节点造成骚扰,MVP 建议: - **只对“状态级别变化”或“关键状态”推送**,并允许后续配置化扩展。 ### 4.2 用户端(C 端)推送场景 对单个订单/运单,建议推送: - `SHIPPED`:商家已发货/已绑定运单 - `OUT_FOR_DELIVERY`:派送中 - `READY_FOR_PICKUP`:待取件 - `DELIVERED`:已签收 - `EXCEPTION`:异常(破损/拒收/地址不详等) - `RETURNED`:退回/退件 可选(非必须、后续再做): - `IN_TRANSIT`:仅在“跨天/关键节点”或“长时间无更新后恢复更新”时推送。 ### 4.3 商家端(B 端)推送场景 商家端更关注履约风险与闭环结果,建议推送: - `SHIPPED`:发货成功(用于确认运单绑定成功/进入履约) - `DELIVERED`:已签收(用于完成售后风险关闭) - `EXCEPTION`:异常(优先级最高) - `RETURNED`:退回/退件 可选(按业务需要): - `OUT_FOR_DELIVERY`:派送中(对高价值订单可能有意义) ### 4.4 同一事件对不同端的文案差异 - 同一事件事实字段同源(event_time/event_text/status_code)。 - **C 端**文案可更贴近用户体验(避免网点内部信息、避免电话明文)。 - **B 端**文案以进度/风险为主(异常/退回明确提示)。 ## 5. 消息生成规则(幂等 + 去噪) ### 5.1 生成触发点 在平台完成 `platform_express_tracking_events` 插入成功后触发(建议异步队列/任务)。 ### 5.2 幂等与去重 事件表已有 `(waybill_id, dedupe_key)` 唯一约束;消息层建议再做一层幂等: - 推荐幂等键:`recipient_scope + recipient_id + waybill_id + status_code + event_time` - 若第三方提供 `event_id`,可用:`recipient_scope + recipient_id + waybill_id + event_id` 保证:同一条事件重试/重复推送不会造成消息中心重复。 ### 5.3 去噪(MVP) - 默认仅对 4.2/4.3 列出的状态集合生成消息。 - 同状态多事件:只推“最新 event_time”对应的一条(或按节点去重)。 ## 6. Push payload 规范(建议) ### 6.1 设计目标 - **足够定位**:能让客户端知道要刷新哪个订单/运单、点击去哪里。 - **不泄露敏感**:不含手机号/地址/raw_payload。 - **可审计**:含平台侧 message_id / request_id。 ### 6.2 字段建议(透传/通知通用) ```json { "biz": "express", "aud": "user", "message_id": "msg_xxx", "order_no": "ORD_2026...", "carrier": "YTO", "tracking_no": "YT123...", "status_code": "OUT_FOR_DELIVERY", "event_time": "2026-02-05T14:32:00+08:00", "event_text": "快件正在派送中", "deeplink": { "path": "/pages/order/detail", "query": {"order_no":"ORD_2026...", "tab":"logistics"} } } ``` 说明: - `event_text` 推荐使用“清洗后的文案”(避免把第三方原文中的手机号明文透传)。 - `aud`:区分用户端/商家端,有利于同一 App 多角色或同一推送通道多消费方。 ### 6.3 通知栏标题/摘要建议(示例) - 用户端: - 标题:`物流更新` / `订单物流更新` - 内容:`【派送中】快件正在派送中` - 商家端: - 标题:`订单履约提醒` - 内容:`【异常】包裹地址不详正在退回`(异常优先级最高) ## 7. 消息中心与未读(服务端为准) ### 7.1 为什么需要消息中心 - Push 可能丢失/被系统拦截;消息中心提供可回溯记录。 - 多设备登录需要统一未读。 ### 7.2 建议的数据模型(平台侧新增) 建议新增两类表(命名仅示例): 1) 设备注册表 `push_devices` - `id` - `user_id` / `merchant_id`(二选一或用统一 subject) - `cid` - `platform`(android/ios/web) - `appid`(用于区分环境) - `last_seen_at`、`is_active` 2) 物流消息表 `express_notifications` - `id`(message_id) - `aud`(user/merchant) - `recipient_id` - `order_id/order_no`、`waybill_id`、`tracking_no`、`carrier` - `status_code`、`event_time`、`event_text_safe` - `read_at`(或拆到 read 表做多端多设备) - `created_at` - 幂等键(unique) ### 7.3 API(建议) - 设备注册:`POST /api/v1/push/register`(登录后上报 CID,绑定到账号) - 设备解绑:`POST /api/v1/push/unregister` - 消息列表:`GET /api/v1/notifications?biz=express&aud=user|merchant&page=...` - 标记已读:`POST /api/v1/notifications/read`(按 message_id 或按订单批量) - 未读数:`GET /api/v1/notifications/unread-count?biz=express&aud=...` ## 8. 点击跳转(deeplink) ### 8.1 跳转原则 - 点击通知后进入“订单详情-物流区块”或“物流详情页”。 - 页面打开后必须调用平台查询接口刷新 `status_history`。 ### 8.2 跳转目标建议 - 用户端:订单详情页(展示物流时间线) - 商家端:订单详情页(展示物流时间线 + 发货信息) - 平台后台:不通过 App push 跳转(后台有自己的排障页) ## 9. 安全、权限与隐私 ### 9.1 收件人权限校验 - 用户端:仅能收到/查看自己订单的物流消息。 - 商家端:仅能收到/查看自己店铺订单的物流消息。 服务端生成消息时必须校验:`order_no -> 订单归属 -> recipient`。 ### 9.2 敏感字段控制(强约束) - 禁止通过 push payload/通知文案下发:完整手机号、完整地址、raw_payload、签名/密钥。 - `raw_payload` 仅允许客服/运维在后台折叠查看,并记录审计。 ### 9.3 CID 绑定安全 - CID 必须在**登录态**下上报并绑定到账号。 - 更换设备/卸载重装:CID 可能变化,需更新绑定。 - 退出登录时建议解绑或标记设备 inactive。 ## 10. 失败与补偿 - 推送失败(CID 失效/不属于应用):标记设备 inactive 并等待下次登录刷新。 - Webhook 入库成功但推送失败:不影响事实数据;消息中心仍可查询到记录(若已写入消息表)。 - 客户端未收到 push:用户进入订单详情时仍能通过查询接口看到最新轨迹。 ## 11. 验收清单(与联调口径一致) 1) 插入一条轨迹事件(模拟 webhook)后: - waybill `current_status_code/text` 更新 - events 表新增且幂等 - 消息中心新增 1 条(不重复) - 目标端(用户/商家)收到推送 2) 乱序/重复推送: - 事件表不重复、消息中心不重复 - 当前状态不回退(按平台选定算法) 3) 隐私校验: - push 文案与 payload 无手机号明文/无 raw 4) 点击跳转: - 落到订单/物流详情页并刷新出最新时间线 --- ## 12. 与本目录文档的对应关系(索引) - 统一模型与 Webhook 约束:接口规范 - 状态映射:状态映射表 - 展示字段与隐私规则:前端字段清单 - 生产表结构与幂等/乱序:生产表说明_platform_express - 移动端 push 联调与 CID 归属:uni-push2 安卓联调与取 CID 说明