14 KiB
14 KiB
物流 Webhook 接收器 & Push Server(推送后台)总览
面向读者:需要理解“物流事件如何入库、如何触发消息、如何推送到 App”的后端/运维/联调同学。
本文只做总结性说明;细节请跳转到对应子文档与源码。
1. 这两套后台分别解决什么问题?
Webhook 接收器(webhook-receiver)
一句话:把第三方/承运方推过来的“物流轨迹事件”接进来,完成验签(可选)、留痕,并把事件写入 Supabase 的 platform_express_* 表。
它关注的是:
- Webhook 请求能否稳定接收(可公网暴露、可控验签)
- 原始 payload 是否可追溯(用于审计/排障)
- 轨迹事件是否能归一化写入(供业务查询、消息生成)
- 运单摘要状态是否能更新(用于页面“当前状态”展示)
Push Server(push-server)
一句话:维护“账号 ↔ 设备 CID”的映射,并提供“消息入队 + 消费下发”能力,把通知可靠地投递到云函数(由云函数完成 uni-push2 真实下发)。
它关注的是:
- 设备 CID 的注册/解绑与存储(本地 JSON 回退 + Supabase 表)
- 生成推送任务(写入
express_notifications) - 消费推送任务并调用
CLOUD_FUNC_URL(重试/回写状态)
1.1 用什么做的(技术栈/依赖)
这两套服务都是“轻量 Node.js HTTP 服务”,共同特点:
- 运行时:Node.js(建议 18+;你当前环境是 Node.js 22 也可以)
- HTTP 框架:Express
- JSON 解析:body-parser
- 与 Supabase 通信:直接调用 PostgREST REST API(
$SUPA_URL/rest/v1/...),通过apikey(可选 Bearer)鉴权 - 配置加载:复用
server/load-config.js,支持.env/.json/CONFIG_FILE指定
差异点:
- webhook-receiver:额外用
crypto做可选 HMAC 验签;主要写platform_express_*三表 - push-server:额外包含“消费者轮询 + 重试退避 + 调用云函数”的逻辑;本地 JSON 作为设备表回退(
server/data/push_devices.json)
2. 整体链路(从物流事件到手机通知)
建议把链路理解为 3 段:
- 事件入库段(Webhook 接收器负责)
- 第三方回调 → webhook-receiver → 写入/更新:
platform_express_event_raw(原始留痕)platform_express_tracking_events(归一化事件事实表)platform_express_waybills(运单摘要 current_status_*)
- 消息生成段(通常是“事件处理器/任务/触发器”,不在这两个服务里)
- 监听/轮询
platform_express_tracking_events的新增事件 - 按推送策略(关键状态、去噪、幂等)生成“消息中心记录/推送任务”
- 写入
express_notifications(或调用 push-server 的 HTTP 接口让其写入)
- 推送下发段(Push Server + 云函数负责)
- push-server consumer 读取
express_notifications的待处理记录 - 找到目标设备(
push_devices中的活跃 CID) - 对每个 CID POST 到
CLOUD_FUNC_URL - 云函数内部调用 uni-push2 下发 → App 收到通知
一个简化示意图:
flowchart LR
Third[第三方/承运方] -->|POST webhook| WH[webhook-receiver :7201]
WH --> RAW[(platform_express_event_raw)]
WH --> EV[(platform_express_tracking_events)]
WH --> WB[(platform_express_waybills)]
EV --> EP[事件处理器/任务/触发器]
EP --> N[(express_notifications)]
App[App 客户端] -->|注册CID| PS[push-server :7301]
PS --> D[(push_devices)]
PS -->|轮询待发通知| N
PS -->|POST| CF[CLOUD_FUNC_URL 云函数]
CF --> UP[uni-push2]
UP --> App
参考(更完整的业务口径与隐私约束):
pages/mall/delivery/doc/需求文档/物流消息推送方案_用户端与商家端.mdpages/mall/delivery/doc/需求文档/推送与设备需求文档.md
3. Webhook 接收器(webhook-receiver)
3.1 位置与入口
- 代码:
pages/mall/delivery/webhook-server/webhook-receiver.js - 说明文档:
pages/mall/delivery/webhook-server/README.md
3.2 对外接口
POST /webhook/express/status- 作用:接收轨迹变更回调
- 返回:
- 成功:
{ ok: true } - 运单不存在(不算系统错误):
{ ok: false, message: 'waybill not found' }(HTTP 200) - Supabase 鉴权失败等:HTTP 502 +
{ ok:false, message:'...' }
- 成功:
GET /health:健康检查
3.3 写库行为(核心表)
接收器在一次 webhook 调用中做三件事(顺序上:先留痕,再归一化,再更新摘要):
- 原始留痕:插入
platform_express_event_raw
- 保存:carrier、tracking_no、body(原始 payload)、received_at、signature_valid 等
- 定位运单:在
platform_express_waybills中查找对应记录
- 优先按
tracking_no,其次按order_no - 找不到时返回
{ ok:false, message:'waybill not found' }
- 写入事件 & 更新运单摘要
- 将第三方的状态字段映射到平台统一的
status_code(如OUT_FOR_DELIVERY/DELIVERED/EXCEPTION/...) PATCH platform_express_waybills:更新current_status_code/current_status_text/last_synced_atINSERT platform_express_tracking_events:写入归一化事件事实数据(包含 raw_payload、dedupe_key 等)
注意:当前实现的
dedupe_key使用WEBHOOK_ + Date.now(),更偏向“留痕与追溯”;如需严格幂等(重复回调不重复入库),建议按文档口径构造稳定 dedupe_key(例如tracking_no|event_code|event_time)。
3.4 验签(可选)
- 配置
WEBHOOK_SECRET后会校验:X-Timestamp、X-Signature- 签名算法:
HMAC-SHA256(secret, bodyText + timestamp),输出 hex
- 目前验签失败不会直接拒绝入库(会在 raw 表记录
signature_valid=false),可按需要升级为“验签失败直接 4xx”。
3.5 配置与启动方式
接收器会先通过 server/load-config.js 把配置注入 process.env,优先级是:
- 系统环境变量
CONFIG_FILE/CONFIG_PATH指定的文件- 接收器同目录的
webhook.config.json server/.env→server/config.json→server/config.json.example
常用环境变量:
SUPA_URL(必需)SUPA_KEY(必需,建议使用 service_role,仅后端使用)PORT(默认 7201)SUPA_USE_BEARER(默认 false)WEBHOOK_SECRET(可选)
4. Push Server(push-server)
4.1 位置与入口
- 代码:
server/push-server.js - 说明文档(偏“可运行/可运维”):
server/PUSH_SERVER_README.mdserver/README.md
4.2 核心能力
- 设备 CID 管理
POST /api/v1/push/register:注册/更新设备(写本地 JSON,并尝试 upsert 到push_devices)POST /api/v1/push/unregister:解绑/置 inactiveGET /api/v1/push/devices:列出设备(优先 Supabase)
- 推送下发(云函数模式)
POST /api/v1/push/send:- 直接按
cids或user_id发送(对每个 CID 调用CLOUD_FUNC_URL)
- 直接按
- 通知入队 + 消费者轮询下发
POST /api/v1/notifications:写入express_notifications(排队)- consumer(可选启用):
- 定时轮询
express_notifications的待处理记录 - 取到记录后查询目标 CID 列表
- 逐个调用
CLOUD_FUNC_URL,并回写状态/错误/重试次数
- 定时轮询
备注:本仓库当前为“仅云函数模式”,push-server 自身不直接对接 uni-push;真实推送逻辑在云函数里。
4.3 数据依赖(Supabase 表)
public.push_devices- 存储
cid ↔ user_id/merchant_id、is_active等
- 存储
public.express_notifications- 存储待发通知、状态、重试信息等(用于消息中心/推送队列)
迁移脚本参考:pages/mall/delivery/doc/需求文档/20260224_add_push_devices_and_notifications.sql
4.4 关键配置(env)
PORT:默认 7301SUPA_URL、SUPA_KEY/SERVICE_ROLE_KEYSUPA_USE_BEARER:同 webhook-receiver,默认只发apikey
消费者相关:
ENABLE_CONSUMER=true(或CONSUMER_ENABLED=true)CONSUMER_POLL_MS:轮询间隔(默认 2000)CLOUD_FUNC_URL:云函数 HTTP invoke 地址(必需)PUSH_TOKEN:可选鉴权透传给云函数- 重试配置:
MAX_RETRIES / RETRY_INITIAL_MS / RETRY_FACTOR / RETRY_MAX_MS
4.5 常见问题(定位方向)
- Supabase 报
PGRST301/ 401:通常是 Bearer/JWT_SECRET 不匹配导致,优先只用apikey(SUPA_USE_BEARER=false)。 /api/v1/push/send返回push_clientid required:云函数侧没有正确解析请求体(常见于 HTTP 触发器把 body 放在event.body)。- 中文变
????:Windows PowerShell 5.1 发送 JSON 编码问题,按server/PUSH_SERVER_README.md的 UTF-8 字节方式发送。 - 消费者不工作:检查
ENABLE_CONSUMER、CLOUD_FUNC_URL是否配置;再查express_notifications是否有待处理记录。
5. 给联调/运维的“最小心智模型”
- Webhook-receiver 只负责:接收 & 入库(
platform_express_*)。 - Push-server 只负责:设备表 & 通知队列 & 调用云函数(
push_devices/express_notifications)。 - 中间那段“什么时候该推、推给谁、推什么文案/脱敏后字段”通常在事件处理器/业务服务里实现(或数据库触发器 + 消费者)。
把这三段拆开,排障会非常快:
- webhook 是否收到?(看 webhook-receiver 日志 / raw 表)
- tracking_events 是否写入?(看事件表)
- notifications 是否生成?(看 express_notifications)
- push-server 是否消费?(看 push-server 日志 / 重试字段)
- 云函数是否下发成功?(看云函数日志 / uni-push 返回)
5.1 一页摘要
业务价值(为什么要做)
- 提升履约感知:物流关键节点及时触达(用户端/商家端),减少客服咨询与退款/纠纷风险。
- 可追溯:Webhook 原始留痕 + 事件事实表 + 消息队列表,出了问题能定位“卡在哪一段”。
- 解耦与可演进:Webhook 入库、消息生成、推送下发三段解耦,后续可替换推送通道/触发方式而不重写全链路。
范围边界(做了什么 / 没做什么)
- 已覆盖:第三方物流事件接收入库;设备 CID 注册与管理;通知入队与消费;调用云函数(由云函数实际对接 uni-push2)。
- 不在本两服务内:消息生成策略(哪些事件要推、文案/脱敏、收件人映射、幂等 dedupe_key 规则)通常由“事件处理器/业务服务/任务”负责。
外部依赖(上线前必须明确)
- Supabase/Postgres:两服务都依赖 REST 读写(需要稳定网络、正确的 key 权限、相关表已迁移)。
- 云函数:push-server 依赖
CLOUD_FUNC_URL可用;云函数内部需能成功调用 uni-push2。 - 第三方/承运方:Webhook 接入参数、验签口径、回调重试策略需要对齐。
风险与控制点
- 密钥风险:
service_role key属高权限,必须只在服务器环境变量中使用;严禁进入前端/日志/截图。 - 隐私合规:raw 表与 payload 可能含敏感字段,必须限制读取权限;推送内容只允许“脱敏/清洗后的摘要”。
- 可靠性:Webhook 可重复/乱序;需要稳定的幂等策略(事件 dedupe_key、消息 dedupe_key)避免重复消息与状态回退。
- 可用性:push-server consumer 失败要可重试、可观测、可告警;云函数故障不应影响主业务写库。
运维成本(需要投入多少人/怎么管)
- 部署形态:两个常驻 Node 进程(端口默认 webhook 7201、push 7301),建议用进程守护(Windows 服务 / pm2 / docker / supervisor 任选其一)。
- 日志与排障:至少保留 7~30 天日志;能按 request_id/message_id/tracking_no 追踪。
建议监控指标(可用来验收与周报)
- Webhook:每分钟请求量、2xx 比例、验签失败率、写库失败率、waybill not found 比例。
- 推送:待处理队列长度(pending/retrying 数)、每分钟消费量、成功率、平均重试次数、失败 Top 原因(HTTP 非 2xx / 超时 / 云函数业务 errCode)。
- 端到端:从事件入库到推送送达的 P50/P95 延迟(分钟级即可)。
责任分工(出现问题找谁)
- webhook-receiver:对“第三方回调接入、验签、入库”负责。
- 事件处理器/业务服务:对“消息生成规则、脱敏文案、收件人映射、幂等策略”负责。
- push-server + 云函数:对“队列消费、重试、推送通道调用成功率”负责。
上线检查清单(最小版)
- 表结构已迁移(
platform_express_*、push_devices、express_notifications)。 - 密钥仅在服务器环境变量,日志不打印敏感值。
- webhook
/health正常;能写 raw 与 events;运单状态能更新。 - push-server
/health正常;能注册设备;能入队;consumer 能调用云函数并回写状态。 - 监控与告警已配置(至少:连续失败、队列堆积、云函数不可达)。
6. 进一步阅读(从“总览”到“可落地”)
- webhook-receiver:
pages/mall/delivery/webhook-server/README.md - push-server(运行与变更记录):
server/PUSH_SERVER_README.md - push-server(消费者与云函数约定):
server/README.md - 配置加载器:
server/load-config.js - 业务方案与隐私口径:
pages/mall/delivery/doc/需求文档/物流消息推送方案_用户端与商家端.mdpages/mall/delivery/doc/需求文档/推送与设备需求文档.md
7. 安全提醒(强烈建议)
SUPA_KEY/service_role key只允许在后端/服务器环境使用,严禁下发到前端。- 如需把 push-server 的发送/入队接口对外开放,建议至少加一层鉴权(Bearer token、IP 白名单或内网访问)。
platform_express_event_raw.body/raw_payload可能包含敏感信息,生产上建议严格控制读取权限并记录审计。