消息推送后台打通
This commit is contained in:
@@ -1,20 +1,26 @@
|
||||
**Push Server - 使用与变更说明**
|
||||
|
||||
简要说明
|
||||
- 该文档记录对 `server/push-server.js` 的修改、运行所需的环境变量、表结构依赖、以及如何把 Supabase 的 cid 与通知通过 dCloud UNI‑PUSH 下发的端到端操作步骤。
|
||||
- 该文档记录对 `server/push-server.js` 的修改、运行所需的环境变量、表结构依赖、以及如何把 Supabase 的 cid 与通知通过云函数(`CLOUD_FUNC_URL`)下发的端到端操作步骤。
|
||||
|
||||
变更要点(代码修改摘要)
|
||||
- supaFetch: 默认仅发送 `apikey`;仅当 `SUPA_USE_BEARER=true` 或 `SUPA_KEY` 看起来像 JWT(包含两处 ".")时,才发送 `Authorization: Bearer`。避免把明文 service key 当作 JWT 发出导致 PostgREST 拒绝。
|
||||
- 新增 endpoint `/api/v1/notifications`:将通知写入 `express_notifications`,根据 `aud` 与 `recipient_id` 查询 `push_devices`,再发送推送(proxy 或 mock),并写回通知状态。
|
||||
- 新增 uni-push adapter `sendToUniPush(targets, notification, payload)`:当设置了 `UNI_PUSH_URL` 时,`/api/v1/push/send` 与 `/api/v1/notifications` 会调用该适配器优先发送到 UNI‑PUSH;否则若设置了 `PUSH_PROXY_URL` 则转发到该 URL。
|
||||
- supaFetch: 默认仅发送 `apikey`;仅当 `SUPA_USE_BEARER=true` 时才发送 `Authorization: Bearer`。用于避免自托管 Supabase/Kong 场景下因 JWT_SECRET 不一致触发 `PGRST301`。
|
||||
- 新增 endpoint `/api/v1/notifications`:将通知写入 `express_notifications`(排队);由消费者(轮询)读取待处理记录并 POST 到 `CLOUD_FUNC_URL`。
|
||||
- 仅云函数模式:`server/push-server.js` 已禁用 UNI‑PUSH/代理分支,发送只走 `CLOUD_FUNC_URL`。
|
||||
|
||||
<!--
|
||||
历史能力(已注释/不再使用):
|
||||
- uni-push adapter sendToUniPush(targets, notification, payload)
|
||||
- PUSH_PROXY_URL / PUSH_PROXY_TOKEN 转发
|
||||
-->
|
||||
|
||||
新增/修改的接口(简要)
|
||||
- GET `/health` — 健康检查。
|
||||
- POST `/api/v1/push/register` — 注册/更新设备;会写本地 `server/data/push_devices.json`,并尝试 upsert 到 Supabase `push_devices` 表(如果配置了 SUPA_URL + SERVICE_ROLE_KEY)。
|
||||
- POST `/api/v1/push/unregister` — 注销设备(本地并尝试同步 Supabase)。
|
||||
- GET `/api/v1/push/devices` — 列出设备(优先从 Supabase 获取)。
|
||||
- POST `/api/v1/push/send` — 直接按 `cids` 或 `user_id` 发送推送;若 `UNI_PUSH_URL` 存在使用 adapter,否则若 `PUSH_PROXY_URL` 存在转发,默认 mock 返回。
|
||||
- POST `/api/v1/notifications` — 将通知写入 `express_notifications` 并基于 `aud`/`recipient_id` 拉取 `push_devices` 发推送,成功/失败状态写回 `express_notifications.status_code`。
|
||||
- POST `/api/v1/push/send` — 直接按 `cids` 或 `user_id` 发送推送(仅云函数:对每个 cid POST 到 `CLOUD_FUNC_URL`)。
|
||||
- POST `/api/v1/notifications` — 将通知写入 `express_notifications`(排队);实际下发由消费者完成。
|
||||
|
||||
依赖的数据库表(必须存在)
|
||||
- `public.push_devices`:用于存储设备 cid、user_id/merchant_id、is_active 等(见仓库迁移脚本 `20260224_add_push_devices_and_notifications.sql`)。
|
||||
@@ -24,10 +30,14 @@
|
||||
- SUPA_URL — Supabase REST(PostgREST)地址(内部建议 `http://rest:3000`)。
|
||||
- SERVICE_ROLE_KEY 或 SUPA_KEY — 用作 `apikey` 向 PostgREST 请求(不要把明文放到 Authorization 除非该值确为 JWT)。
|
||||
- SUPA_USE_BEARER — (可选) 若为 `true` 则强制发送 Authorization: Bearer <SUPA_KEY>。
|
||||
- UNI_PUSH_URL — (可选) 若设置则使用内置 `sendToUniPush` adapter 直接调用 dCloud uni-push 接口。
|
||||
- UNI_PUSH_APPID / UNI_PUSH_SECRET — adapter 用于构造或鉴权(按你现有 uni-push 接口调整)。
|
||||
- PUSH_PROXY_URL / PUSH_PROXY_TOKEN — 若不使用 adapter,可把此设置为你现有的推送代理 URL 与 token,后端会将 {targets, notification, payload} 转发过去。
|
||||
- PUSH_PROXY_URL 优先级低于 UNI_PUSH_URL:若 UNI_PUSH_URL 存在,优先使用本地 adapter。
|
||||
- ENABLE_CONSUMER / CONSUMER_ENABLED — 启用消费者轮询(从 express_notifications 读取待处理记录)。
|
||||
- CONSUMER_POLL_MS — 轮询间隔(毫秒)。
|
||||
- CLOUD_FUNC_URL — 云函数外网调用 URL(每个目标 cid 会对该 URL 发 POST)。
|
||||
- PUSH_TOKEN — (可选) 云函数鉴权 token(会在 POST body 的 token 字段中透传)。
|
||||
|
||||
<!--
|
||||
UNI_PUSH_URL / UNI_PUSH_APPID / UNI_PUSH_SECRET / PUSH_PROXY_URL / PUSH_PROXY_TOKEN:仅在非云函数模式下使用;当前已不再使用。
|
||||
-->
|
||||
|
||||
运行与测试(本地示例)
|
||||
1) 安装依赖并启动:
|
||||
@@ -57,6 +67,46 @@ curl -X POST http://localhost:7301/api/v1/notifications \
|
||||
-d '{"aud":"user","recipient_id":"<USER_UUID>","notification":{"title":"测试","body":"uni-push 测试"}}'
|
||||
```
|
||||
|
||||
> 说明(仅云函数模式):`/api/v1/notifications` 仅写入 `express_notifications`(排队)。实际下发由消费者轮询后对 `CLOUD_FUNC_URL` 执行 POST。
|
||||
|
||||
端到端验证(推荐)
|
||||
-----------------
|
||||
|
||||
前置条件
|
||||
- `CLOUD_FUNC_URL` 可访问且返回 2xx(否则会被标记为 failed/retrying)。
|
||||
- 目标用户在 `push_devices` 中存在至少一个 `is_active=true` 的设备(否则会被标记为 no-targets)。
|
||||
|
||||
PowerShell 示例(Windows)
|
||||
```powershell
|
||||
# 1) 注册设备(写入 push_devices)
|
||||
Invoke-RestMethod -Uri 'http://localhost:7301/api/v1/push/register' -Method POST -ContentType 'application/json' -Body (@{
|
||||
cid='CID_TEST_001'
|
||||
user_id='a8e3a568-fc1f-4237-bcc5-5722e2fca0a3'
|
||||
platform='android'
|
||||
} | ConvertTo-Json)
|
||||
|
||||
# 2) 写入通知(排队)
|
||||
$body = @{
|
||||
aud='user'
|
||||
recipient_id='a8e3a568-fc1f-4237-bcc5-5722e2fca0a3'
|
||||
notification=@{ title='测试'; body='hello' }
|
||||
payload=@{ order_id='123' }
|
||||
} | ConvertTo-Json -Depth 6
|
||||
Invoke-RestMethod -Uri 'http://localhost:7301/api/v1/notifications' -Method POST -ContentType 'application/json' -Body $body
|
||||
|
||||
# 3) 等待 2 秒(CONSUMER_POLL_MS 默认 2000),观察 push-server 控制台日志:应出现对 CLOUD_FUNC_URL 的 POST
|
||||
```
|
||||
|
||||
如何确认处理结果
|
||||
- 在 Supabase 中查询最近记录:
|
||||
|
||||
```sql
|
||||
select id, message_id, status_code, retry_count, last_error, updated_at
|
||||
from public.express_notifications
|
||||
order by created_at desc
|
||||
limit 10;
|
||||
```
|
||||
|
||||
5) 直接按 cid 发(跳过 DB):
|
||||
```bash
|
||||
curl -X POST http://localhost:7301/api/v1/push/send \
|
||||
@@ -65,8 +115,9 @@ curl -X POST http://localhost:7301/api/v1/push/send \
|
||||
```
|
||||
|
||||
UNI‑PUSH 集成注意事项
|
||||
- adapter 当前构造 body 使用 `cidList` 与 `message:{title,content,payload}`。请根据你已经验证成功的 uni-push curl 请求体调整字段名与鉴权 header(可使用 `UNI_PUSH_APPID`、`UNI_PUSH_SECRET`、或 `PUSH_PROXY_TOKEN`)。
|
||||
- 建议:把你成功的 uni-push curl 发给我,我可以把 adapter 的 body/header 精确改成一致格式。
|
||||
<!--
|
||||
仅云函数模式:此服务不再直接对接 UNI‑PUSH;请在云函数内部完成 UNI‑PUSH2 调用与鉴权。
|
||||
-->
|
||||
|
||||
故障与排查要点
|
||||
- 如果 Supabase 报 401 或 PGRST301:不要把明文 service key 作为 Bearer;使用 `apikey` header,或生成并使用与 `PGRST_JWT_SECRET` 匹配的 JWT。可通过 `docker inspect` 检查容器 env 中的 `PGRST_JWT_SECRET`。
|
||||
@@ -138,7 +189,8 @@ node push-server.js
|
||||
|
||||
```
|
||||
Push server listening on http://0.0.0.0:7301
|
||||
ENV: UNI_PUSH_URL= https://restapi.getui.com/v2/...
|
||||
ENV: CLOUD_FUNC_URL configured? true
|
||||
ENV: ENABLE_CONSUMER= true
|
||||
Auto-deploy: spawning node D:\...\server\tools\deploy-cloudfunc.js
|
||||
[auto-deploy stdout] 打包目录: D:\...\uniCloud-alipay\cloudfunctions\testUnipush2
|
||||
[auto-deploy stdout] 打包完成 -> D:\...\server\dist\testUnipush2.zip (1324 bytes)
|
||||
@@ -286,11 +338,19 @@ Auto-deploy process exited with code=0 signal=null
|
||||
- 目的:从数据库 `express_notifications` 拉取待发送的消息(pending),解耦写入与下发流程,保证可重试、可审计与可运维。
|
||||
- 实现位置:`server/push-server.js`(已新增 consumer 逻辑)。
|
||||
- 启用:设置环境变量 `ENABLE_CONSUMER=true`(或 `CONSUMER_ENABLED=true`),可选配置轮询间隔 `CONSUMER_POLL_MS`(默认 2000 ms)。
|
||||
|
||||
- 轮询频率说明:默认 `CONSUMER_POLL_MS` 为 `2000`(即每 2000 毫秒,2 秒轮询一次)。若需调整频率,请设置环境变量 `CONSUMER_POLL_MS` 为毫秒数。例如将间隔改为 5 秒:
|
||||
|
||||
```powershell
|
||||
$env:ENABLE_CONSUMER="true"
|
||||
$env:CONSUMER_POLL_MS="5000"
|
||||
node push-server.js
|
||||
```
|
||||
- 关键环境变量:`SUPA_URL`、`SUPA_KEY`(Supabase REST)、`CLOUD_FUNC_URL`(云函数 invoke URL)、`PUSH_TOKEN`(云函数鉴权)。
|
||||
- 行为摘要:
|
||||
- 轮询 `express_notifications`(status_code IS NULL)并选取记录;
|
||||
- 通过带过滤的 PATCH 抢占(将 `status_code` 设为 `processing`)以避免并发重复处理;
|
||||
- 查询目标设备(`push_devices`),对每个 `cid` 构造 event 并 POST 到 `CLOUD_FUNC_URL`(若未配置则回退到 `UNI_PUSH_URL` 适配器);
|
||||
- 查询目标设备(`push_devices`),对每个 `cid` 构造 event 并 POST 到 `CLOUD_FUNC_URL`;若未配置 `CLOUD_FUNC_URL` 则本次处理将失败并写入错误原因;
|
||||
- 根据调用结果回写 `express_notifications.status_code` 为 `success` / `failed` / `no-targets`。
|
||||
- 限制与扩展点:当前 consumer 依赖 Supabase REST;尚未在 DB 中新增 `retry_count`/`last_error` 字段(建议在迁移中加入以支持指数退避与重试);为保证高可用建议配合 `FOR UPDATE SKIP LOCKED` 或 Supabase Realtime 优化并发策略。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user