# 物流 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 段：

1) **事件入库段**（Webhook 接收器负责）
- 第三方回调 → webhook-receiver → 写入/更新：
  - `platform_express_event_raw`（原始留痕）
  - `platform_express_tracking_events`（归一化事件事实表）
  - `platform_express_waybills`（运单摘要 current_status_*）

2) **消息生成段**（通常是“事件处理器/任务/触发器”，不在这两个服务里）
- 触发器把 `platform_express_tracking_events` 的关键状态事件写入队列表 `notify_queue`
- 常驻 `notify-worker` 消费 `notify_queue`，按推送策略（关键状态、去噪、幂等）生成“消息中心记录/推送任务”
- 写入 `express_notifications`（push-server consumer 会复用该表进行下发）

相关实现（仓库内）：
- 迁移脚本：`pages/mall/delivery/doc/需求文档/20260309_add_notify_queue_and_trigger.sql`
- 常驻 worker：`server/notify-worker.js`

3) **推送下发段**（Push Server + 云函数负责）
- push-server consumer 读取 `express_notifications` 的待处理记录
- 找到目标设备（`push_devices` 中的活跃 CID）
- 对每个 CID POST 到 `CLOUD_FUNC_URL`
- 云函数内部调用 uni-push2 下发 → App 收到通知

一个简化示意图：

```mermaid
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 --> TRG[DB Trigger]
  TRG --> Q[(notify_queue)]
  Q --> W[notify-worker]
  W --> 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/需求文档/物流消息推送方案_用户端与商家端.md`
- `pages/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 调用中做三件事（顺序上：先留痕，再归一化，再更新摘要）：

1) **原始留痕**：插入 `platform_express_event_raw`
- 保存：carrier、tracking_no、body（原始 payload）、received_at、signature_valid 等

2) **定位运单**：在 `platform_express_waybills` 中查找对应记录
- 优先按 `tracking_no`，其次按 `order_no`
- 找不到时返回 `{ ok:false, message:'waybill not found' }`

3) **写入事件 & 更新运单摘要**
- 将第三方的状态字段映射到平台统一的 `status_code`（如 `OUT_FOR_DELIVERY/DELIVERED/EXCEPTION/...`）
- `PATCH platform_express_waybills`：更新 `current_status_code/current_status_text/last_synced_at`
- `INSERT platform_express_tracking_events`：写入归一化事件事实数据（包含 raw_payload、dedupe_key 等）

> 当前实现的 `dedupe_key` 使用稳定哈希（基于 tracking_no / carrier / status_code / event_time / event_code / event_text 的截断组合），用于保证 webhook 重复回调不会重复写入事件表，从而避免重复入队/重复通知。

### 3.4 验签（可选）
- 配置 `WEBHOOK_SECRET` 后会校验：
  - `X-Timestamp`、`X-Signature`
  - 签名算法：`HMAC-SHA256(secret, rawBodyText + timestamp)`，输出 hex（务必使用**原始请求体文本**参与计算）
- 默认行为：验签失败不会直接拒绝入库（会在 raw 表记录 `signature_valid=false`）。
- 可选严格模式：设置 `WEBHOOK_REJECT_INVALID_SIGNATURE=true` 后，验签失败将直接返回 HTTP 401。

### 3.5 配置与启动方式
接收器会先通过 `server/load-config.js` 把配置注入 `process.env`，优先级是：
1) 系统环境变量
2) `CONFIG_FILE/CONFIG_PATH` 指定的文件
3) 接收器同目录的 `webhook.config.json`
4) `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.md`
  - `server/README.md`

### 4.2 核心能力
1) **设备 CID 管理**
- `POST /api/v1/push/register`：注册/更新设备（写本地 JSON，并尝试 upsert 到 `push_devices`）
- `POST /api/v1/push/unregister`：解绑/置 inactive
- `GET /api/v1/push/devices`：列出设备（优先 Supabase）

2) **推送下发（云函数模式）**
- `POST /api/v1/push/send`：
  - 直接按 `cids` 或 `user_id` 发送（对每个 CID 调用 `CLOUD_FUNC_URL`）

3) **通知入队 + 消费者轮询下发**
- `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`
  - 存储待发通知、状态、重试信息等（用于消息中心/推送队列）

字段语义（重要，避免联调误判）：
- `status_code`：物流/业务状态（例如 `OUT_FOR_DELIVERY/DELIVERED/EXCEPTION`），由 webhook→tracking_events→notify-worker 生成。
- `send_status`：投递处理状态（`null`=待发送，`processing/retrying/success/failed/no-targets`），由 push-server consumer 读写。

迁移脚本：
- 新环节（入队+worker）：`pages/mall/delivery/doc/需求文档/20260309_add_notify_queue_and_trigger.sql`
- 字段拆分（consumer 必需）：`pages/mall/delivery/doc/需求文档/20260309_add_express_notifications_send_status.sql`

迁移脚本参考：`pages/mall/delivery/doc/需求文档/20260224_add_push_devices_and_notifications.sql`

### 4.4 关键配置（env）
- `PORT`：默认 7301
- `SUPA_URL`、`SUPA_KEY`/`SERVICE_ROLE_KEY`
- `SUPA_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.4.1 云函数过期/URL 变更时，改哪里？

现象：云函数链接过期或更换后，push-server 下发会出现 HTTP 4xx/5xx 或网络错误（日志里通常能看到调用 `CLOUD_FUNC_URL` 失败），并导致 `express_notifications.send_status` 长期处于 `retrying/failed`。

需要修改的配置键：
- `CLOUD_FUNC_URL`：云函数 HTTP invoke 地址（push-server consumer 与 `/api/v1/push/send` 都依赖它）
- （如云函数鉴权也变化）`PUSH_TOKEN`

配置可能存在于以下位置（按优先级理解即可）：
1) **系统环境变量**（临时验证/单次启动最方便）
2) **显式配置文件**：通过 `CONFIG_FILE/CONFIG_PATH` 指定的 `.json` 或 `.env`
3) **默认配置文件**：`server/config.json`（`server/load-config.js` 会读取并注入 env）

最常用改法（推荐）：修改 `server/config.json` 里的 `CLOUD_FUNC_URL`，然后重启 push-server。

PowerShell 快速验证（不改文件，先确认新 URL 可用）：
```powershell
$env:CLOUD_FUNC_URL='https://new-cloudfunc.example/invoke'
node server/push-server.js
```

改完必须做的动作：
- **重启 push-server**：`CLOUD_FUNC_URL` 在进程启动时读取，改配置后不重启不会生效。

CI/自动化（如果你用了 smoke test）：
- GitHub Actions / CI 里若有云函数 smoke test，通常还需要同步更新 Secrets（例如 `CLOUD_FUNC_URL`）。

最小 smoke test（确认云函数通不通）：
```powershell
curl.exe -sS -X POST "$env:CLOUD_FUNC_URL" -H "Content-Type: application/json" -d "{\"token\":\"$env:PUSH_TOKEN\",\"push_clientid\":\"<DEVICE_CID>\",\"title\":\"ping\",\"content\":\"pong\",\"payload\":{}}"
```

### 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` 是否有待处理记录。

---

## 4.6 notify-worker（消息生成入队）

### 4.6.1 作用（它负责哪一段）
**一句话**：从 `notify_queue` 消费“需要生成消息的事件”，把它转换成 `express_notifications`（消息中心记录/推送任务），供 push-server consumer 后续下发。

它关注的是：
- **把事件变成消息**：把“物流事件”落成“可按 user/merchant 查询的消息记录”。
- **收件人映射**：根据运单 → 订单，解析出 `user_id` / `merchant_id`。
- **幂等入队**：对同一事件避免重复生成（靠稳定的 `message_id` upsert）。

> 边界：notify-worker **不负责推送下发**；真正调用 `CLOUD_FUNC_URL` 的是 push-server consumer。

### 4.6.2 位置与入口
- 代码：`server/notify-worker.js`
- 说明文档（更详细）：`server/NOTIFY_WORKER_README.md`

### 4.6.3 核心行为（做了什么）
1) 拉取待处理队列：`notify_queue?processed_at=is.null`（按 `created_at` 升序，批量处理）。
2) 解析业务上下文：
  - 查 `platform_express_waybills` 取 `order_id/order_no, tracking_no, carrier`
  - 查 `ml_orders` 取 `user_id, merchant_id`
3) 为每个受众写消息：
  - 若 `user_id` 存在：写 `aud='user'`
  - 若 `merchant_id` 存在：写 `aud='merchant'`
  - 写入方式：upsert 到 `express_notifications`（`on_conflict=message_id`），避免重复。
4) 回写队列处理结果：把 `notify_queue.processed_at` 置上，并写 `process_status/last_error`（queued/skipped/failed）。

### 4.6.4 数据依赖（读/写哪些表）
- 读取：`notify_queue`、`platform_express_waybills`、`ml_orders`
- 写入：`express_notifications`

关键字段口径：
- `notify_queue.dedupe_key`：应尽量稳定（同一事件重复到达也一致），否则会造成重复消息。
- `express_notifications.message_id`：脚本按 `aud|waybill_id|dedupe_key` 计算 hash 生成，用于幂等。

### 4.6.5 配置与运行（最小要点）
配置加载与 webhook/push-server 一致（复用 `server/load-config.js`），并支持同目录 `server/notify-worker.config.json`。

常用环境变量：
- `SUPA_URL`（必需）
- `SERVICE_ROLE_KEY` 或 `SUPA_KEY`（必需，推荐 service_role）
- `SUPA_USE_BEARER`（可选，默认 false）
- `NOTIFY_WORKER_POLL_MS`（默认 2000）
- `NOTIFY_WORKER_BATCH_SIZE`（默认 20）
- `RUN_ONCE=true`（只跑一轮便退出，适合验证）

启动示例（PowerShell）：
```powershell
node server/notify-worker.js
```

### 4.6.6 常见问题（定位方向）
- 队列一直堆积：看 `notify-worker.err.log`/控制台错误；重点检查 `SUPA_URL/SERVICE_ROLE_KEY` 是否正确、表/字段是否存在。
- 都是 skipped：通常是运单找不到订单（`platform_express_waybills` 缺 `order_id/order_no` 或 `ml_orders` 查不到）。
- 重复消息：检查上游写入 `notify_queue.dedupe_key` 是否稳定（避免 `Date.now()` 这种随机值）。
- RLS/403：优先使用 `SERVICE_ROLE_KEY`，并确保 PostgREST 有权限访问相关表。

---

## 5. 给联调/运维的“最小心智模型”

- Webhook-receiver 只负责：**接收** & **入库**（`platform_express_*`）。
- Push-server 只负责：**设备表** & **通知队列** & **调用云函数**（`push_devices/express_notifications`）。
- 中间那段“什么时候该推、推给谁、推什么文案/脱敏后字段”通常在**事件处理器/业务服务**里实现（或数据库触发器 + 消费者）。

把这三段拆开，排障会非常快：
1) webhook 是否收到？（看 webhook-receiver 日志 / raw 表）
2) tracking_events 是否写入？（看事件表）
3) notifications 是否生成？（看 express_notifications）
4) push-server 是否消费？（看 push-server 日志 / 重试字段）
5) 云函数是否下发成功？（看云函数日志 / 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 + 云函数：对“队列消费、重试、推送通道调用成功率”负责。

### 上线检查清单（最小版）
1) 表结构已迁移（`platform_express_*`、`push_devices`、`express_notifications`）。
2) 密钥仅在服务器环境变量，日志不打印敏感值。
3) webhook `/health` 正常；能写 raw 与 events；运单状态能更新。
4) push-server `/health` 正常；能注册设备；能入队；consumer 能调用云函数并回写状态。
5) 监控与告警已配置（至少：连续失败、队列堆积、云函数不可达）。

---

## 5.2 联调/运维速记（2026-03-10）

下面是本仓库在“webhook → 入库 → 入队 → notify-worker 生成通知 → push-server consumer 调云函数推送到手机”端到端跑通时，最容易踩到的坑与对应结论（只写要点；细节见排障文档）。

更详细的排障与 SQL 清单：`docs/DELIVERY_E2E_TROUBLESHOOTING_20260310.md`

### 5.2.1 最常见的故障与根因（按出现频率）

1) **notify-worker 报 “order not found for waybill” 但数据库明明有订单**
- 典型根因：`ml_orders` 开了 RLS，PostgREST 用 `apikey` 查询会返回空数组（看起来像“没数据”）。
- 处理建议：生产上用 `SERVICE_ROLE_KEY` 或服务端 JWT + 正确策略；联调阶段可以临时放开/调整策略以确认链路。

2) **写 `express_notifications` upsert 报 `42P10`（ON CONFLICT 找不到唯一约束）**
- 典型根因：表上是“部分唯一索引（partial unique index）”，而 PostgREST 的 `on_conflict=message_id` 只能匹配“普通 UNIQUE 约束/索引”。
- 处理建议：确保存在普通唯一索引：`UNIQUE(message_id)`（不要带 WHERE 条件）。

3) **push-server 启动失败：`EADDRINUSE` / 7301 端口被占用**
- 典型根因：旧进程未退出、或端口被其它程序占用。
- 处理建议：先释放端口再启动；一键启动脚本会自动尝试清理 7201/7301。

4) **日志显示云函数调用 success，但手机不弹通知**
- 典型根因：下发的 `content/body` 为空或过短，在某些系统/机型上会被折叠为“不展示”。
- 处理建议：确保推送请求里至少有稳定的 `title` + `content`（本仓库已在 consumer 侧做了 content 兜底）。

5) **`send_status=no-targets`（找不到目标设备）**
- 典型根因：`push_devices` 里没有对应 `user_id/merchant_id` 的活跃 `cid`（未注册/已注销/绑定字段为空）。
- 处理建议：先用 App 触发注册接口写入设备表；若要商家端收推送，必须正确绑定 `merchant_id`。

### 5.2.2 Windows 下一键启动/停止（后台常驻）

为方便“可运维 + 可自启动”，仓库提供了 PowerShell 脚本把 3 个常驻进程统一拉起/停掉：

- 启动（会尽量释放 7201/7301，并把进程 PID 写入 runtime 文件）：
```powershell
powershell -ExecutionPolicy Bypass -File .\server\scripts\start-delivery-backend.ps1
```

- 停止（按 PID 文件停止进程）：
```powershell
powershell -ExecutionPolicy Bypass -File .\server\scripts\stop-delivery-backend.ps1
```

- PID 文件位置（便于运维查进程/做自启接入）：
  - `server/.runtime/delivery-backend.pids.json`

> 说明：脚本按自身路径定位仓库 root，因此可以在任意工作目录执行；并会按 `server/load-config.js` 的规则加载配置（最常见是 `server/config.json`）。`CLOUD_FUNC_URL` 等配置变更后需重启进程才会生效。

**脚本兼容性说明（Windows PowerShell 5.1）**

- `start-delivery-backend.ps1` 会把 PID 文件写成 **UTF-8 无 BOM**，避免 `ConvertFrom-Json` 在 5.1 下因 BOM/编码细节解析失败。
- `stop-delivery-backend.ps1` 会：
  - 优先按 PID 文件停止进程，并删除 PID 文件；
  - 解析异常时也会兜底尝试释放端口（7201/7301），避免“端口占用导致下次启动失败”。

**如果不停止会怎么样**

- 常见现象：下次启动报 `EADDRINUSE`（7201/7301 端口被旧进程占用）。
- 风险：重复启动多个 `notify-worker`/consumer，可能造成重复消费、重复写消息/推送，排障会非常混乱。
- 风险：旧进程仍在用旧配置/旧代码，你改了配置但看起来“不生效”。

### 5.2.3 快速复现（和联调一致的 E2E）

1) 先启动后台（见上节）。
2) 触发 webhook 测试请求：
```powershell
node .\pages\mall\delivery\webhook-server\test-send.js
```
3) 观察链路：
- webhook-receiver 日志是否收到请求；若返回 `{ ok:false, message:'waybill not found' }`，先补齐 `platform_express_waybills` 对应运单。
- notify-worker 是否把队列变成 `express_notifications`。
- push-server consumer 是否把 `send_status` 推进到 `success`（或 `no-targets/retrying/failed`）。

---

## 6. 进一步阅读（从“总览”到“可落地”）

- 数据流详细流转图谱与分析：`server/消息推送文档/DELIVERY_NOTIFICATION_DATA_FLOW.md`
- webhook-receiver：`pages/mall/delivery/webhook-server/README.md`
- push-server（运行与变更记录）：`server/PUSH_SERVER_README.md`
- notify-worker（消息生成入队）：`server/NOTIFY_WORKER_README.md`
- push-server（消费者与云函数约定）：`server/README.md`
- 配置加载器：`server/load-config.js`
- 业务方案与隐私口径：
  - `pages/mall/delivery/doc/需求文档/物流消息推送方案_用户端与商家端.md`
  - `pages/mall/delivery/doc/需求文档/推送与设备需求文档.md`

---

## 7. 安全提醒（强烈建议）

- `SUPA_KEY/service_role key` 只允许在后端/服务器环境使用，**严禁下发到前端**。
- 如需把 push-server 的发送/入队接口对外开放，建议至少加一层鉴权（Bearer token、IP 白名单或内网访问）。
- `platform_express_event_raw.body/raw_payload` 可能包含敏感信息，生产上建议严格控制读取权限并记录审计。
