7.4 KiB
7.4 KiB
物流 Webhook → 通知入库 → 推送到手机:联调问题记录(2026-03-10)
本文记录本次将链路跑通时遇到的阻塞点、根因与修复方式,便于后续环境复现与排障。
目标链路:
- webhook-receiver 接收物流回调并落库
- DB/服务侧写入
notify_queue server/notify-worker.js消费notify_queue,生成express_notificationsserver/push-server.js的 consumer 轮询express_notifications,调用CLOUD_FUNC_URL推送到设备cid
1. 症状:notify-worker 报 order not found for waybill
现象
notify_queue.process_status=skipped或failedlast_error=order not found for waybill
根因
public.ml_orders启用了 RLS。- 后端通过 PostgREST 仅使用
apikey(或无法解码的 Bearer)访问时,auth.uid()为空,RLS policy 不成立,导致对ml_orders的查询返回200 [](看起来像“无数据”,实为被行级安全过滤)。
验证方法
- 对比:SQL Editor 里能查到订单,但经
/rest/v1/ml_orders查不到。
临时修复(用于快速跑通链路)
ALTER TABLE public.ml_orders DISABLE ROW LEVEL SECURITY;
恢复建议(生产化方向)
- 不建议长期关闭 RLS。
- 建议让后端使用可被 PostgREST 正确解码/信任的 service_role JWT,或通过 RPC/安全视图完成必要查询;避免依赖
DISABLE RLS。
2. 症状:写入 express_notifications 时报 42P10
现象
notify_queue.process_status=failedlast_error包含:there is no unique or exclusion constraint matching the ON CONFLICT specification
根因
- notify-worker / push-server 使用 PostgREST upsert:
POST /rest/v1/express_notifications?on_conflict=message_id
- 但数据库侧
express_notifications(message_id)只有“部分唯一索引”(例如WHERE message_id IS NOT NULL),无法匹配ON CONFLICT(message_id),触发42P10。
修复(改为普通唯一索引)
- 执行脚本:
pages/mall/delivery/doc/需求文档/20260310_fix_express_notifications_on_conflict_message_id.sql
核心 SQL(摘要):
DROP INDEX IF EXISTS public.ux_express_notifications_message_id;
CREATE UNIQUE INDEX IF NOT EXISTS ux_express_notifications_message_id
ON public.express_notifications(message_id);
修复后验证
notify_queue最新记录不再失败;express_notifications能正常生成/更新。
3. 症状:push-server 启动失败 EADDRINUSE 0.0.0.0:7301
现象
node server/push-server.js直接退出- 提示端口占用
根因
- 7301 端口已有 node 进程占用(重复启动或遗留进程)
排查/处理(Windows)
netstat -ano | Select-String ":7301 "
Get-Process -Id <PID>
Stop-Process -Id <PID> -Force
4. 症状:PGRST301(JWT 无法解码)导致访问异常
现象
- PostgREST 返回
PGRST301或“JWT cannot be decoded/verified”
根因
- 在自托管/网关配置不一致时,手动发送
Authorization: Bearer <SUPA_KEY>可能触发 JWT 解码失败。 - 本仓库对 Supabase REST 调用默认只发
apikey,仅在显式SUPA_USE_BEARER=true时才附加 Bearer。
处理建议
- 保持
SUPA_USE_BEARER=false(默认) - 使用可用的
apikey/service_role key走 Kong key-auth
相关实现:
server/push-server.js的supaFetch逻辑
5. 症状:数据库显示 send_status=success,但手机没弹通知
现象
express_notifications.send_status=success- 但手机端没有系统通知/横幅
常见原因
- 推送内容为空(部分通道/客户端不会展示空内容通知)
- 客户端处于前台/透传模式:消息到达但不自动弹系统通知(需要客户端本地展示)
- 手机系统通知权限/渠道被关闭、省电策略限制
本次定位与修复
- notify-worker 生成的
express_notifications通常只有event_text_safe(可作为标题),body/content 可能为空。 - push-server consumer 调云函数时原先传入的
content可能为空,导致“云函数返回成功,但不展示”。 - 已在
server/push-server.js增加兜底:当 body/content 为空时,使用 title/event_text_safe 作为 content。
验证方法(直接调用云函数对 CID 推送)
- 观察云函数返回中是否包含类似
successed_online;并对比手机是否实际展示。
6. 症状:merchant 侧 send_status=no-targets
现象
express_notifications.aud=merchant的记录send_status=no-targetslast_error=no active devices
根因
- 设备绑定表
push_devices仅存在user_id绑定,没有merchant_id绑定。
处理建议
- 让商家端也调用注册接口写入
merchant_id维度的设备(或插入对应记录)。 - push-server consumer 对 merchant 的设备查询路径是:
push_devices?merchant_id=eq.<recipient_id>&is_active=eq.true
7. 端到端自检清单(最短路径)
- 触发 webhook 测试:
node pages/mall/delivery/webhook-server/test-send.js
- 查队列:
notify_queue最新应为process_status=queued且last_error=null
- 查通知:
express_notifications最新应生成aud=user与aud=merchant两条aud=user通常应推进到send_status=success
- 查设备:
push_devices中对应user_id/merchant_id必须存在is_active=true的cid
7.1 Windows 一键启动三服务(建议)
为了保证与联调时一致的效果,推荐用脚本一次性启动 3 个后台进程(并落日志):
- 启动(会默认释放 7201/7301 端口占用):
powershell -ExecutionPolicy Bypass -File .\server\scripts\start-delivery-backend.ps1
- 触发与联调一致的 webhook 测试(打到本机 7201):
node .\pages\mall\delivery\webhook-server\test-send.js
- 停止:
powershell -ExecutionPolicy Bypass -File .\server\scripts\stop-delivery-backend.ps1
日志文件默认在仓库根目录:
webhook-receiver.log/.err.lognotify-worker.log/.err.logpush-server.log/.err.log
PID 文件(用于停止/排查进程)在:
server/.runtime/delivery-backend.pids.json
查看当前是否仍在运行(示例):
Get-Content .\server\.runtime\delivery-backend.pids.jsonGet-Process -Id <PID>
注意:test-send.js 默认请求 http://localhost:7201/webhook/express/status,所以必须先启动 webhook-receiver。
如果 test-send.js 返回 { ok: false, message: 'waybill not found' }:
- 说明数据库里没有
platform_express_waybills.tracking_no = 'TEST_YT_20260206_0007'的运单记录。 - 处理方式:先在
platform_express_waybills补一条对应 tracking_no 的运单(并关联到你的ml_orders),再重试发送。
8. 常用 SQL / 操作片段
8.1 重跑某条队列记录(把 failed/skipped 重置为可再次消费)
UPDATE public.notify_queue
SET processed_at = NULL,
process_status = NULL,
last_error = NULL
WHERE id = '<notify_queue.id>';
8.2 检查 message_id 是否存在重复(创建唯一索引前)
SELECT message_id, COUNT(*)
FROM public.express_notifications
WHERE message_id IS NOT NULL
GROUP BY message_id
HAVING COUNT(*) > 1;
8.3 查看 push_devices 是否有绑定
SELECT *
FROM public.push_devices
WHERE user_id = '<user_id>' OR merchant_id = '<merchant_id>'
ORDER BY updated_at DESC
LIMIT 20;