离线推送排障
“推送没收到”在本项目里有三种完全不同的原因:设备 token 没注册、厂商通道没有真实发送能力、离线 webhook 没被应用接住。本页按投递状态机逐层排查,避免把 sent=0 误判成“服务端坏了”。
厂商通道(APNs/FCM/华为/小米/OPPO/vivo)当前只存储与校验配置,不发起真实发送;投递尝试只会写入 no_token 或 not_configured,sent 恒为 0。
真正的“离线通知”链路是:接收方无在线 WS 会话 → 服务端 POST 到应用配置的 webhookUrl(3 秒超时、无签名、无重试)→ 应用自己调厂商推送。
通知点击跳转、角标、通知分组、多语言推送模板均未实现。
投递状态机
| 状态 | 含义 | 当前实现会写入吗 | 下一步 |
|---|---|---|---|
no_token | 该用户没有 active 设备 token | 会 | 客户端注册 token;确认用户未注销/封禁 |
not_configured | 有 token,但厂商适配器未配置/未实现 | 会(终态) | 这是预期结果;改用 webhook 链路或等待真实适配器 |
queued | 已入队待发送 | 预留 | — |
sent | 厂商已接受 | 预留(恒 0) | 当前不可能出现 |
failed | 厂商明确失败 | 预留 | 真实适配器接入后才可能 |
排查步骤
- 先确认 token 是否存在
管理端查询
GET /api/admin/apps/{appkey}/push/tokens?userId=…;客户端也可查本人 token。每用户活跃 token 上限 20 个。 - 再看投递记录状态
创建投递后查列表或单条:
no_token是设备问题,not_configured是能力问题(预期)。 - 确认 webhook 是否配置且可达
回调/推送 webhook 地址、HTTPS、接收端 2xx;服务端 3 秒超时、无签名、无重试,失败不会补发。
- 核对服务开通与用户状态
推送相关服务未开通返回
1021;注销/封禁用户1016。 - 最后才看厂商侧
本项目不代发厂商推送;如果 webhook 已收到,问题在应用自己的厂商 SDK/凭据/证书。
import { AdminClient } from '@ni-paas-im/server-sdk';
const admin = new AdminClient({ baseUrl: 'http://127.0.0.1:8080', adminSecret: process.env.IM_ADMIN_SECRET });
// 1) 查设备 token
const tokens = await admin.request('GET', '/apps/demo/push/tokens?userId=alice');
console.log(tokens.items.map((t) => ({ platform: t.platform, active: t.active })));
// 2) 创建投递(≤100 用户;title 1–128 字符、body 1–2048)
const created = await admin.createPushDeliveries({
appkey: 'demo',
userIds: ['alice'],
title: '订单提醒',
body: '你的订单已发货',
});
console.log(created.sent, created.deliveries[0].status); // 0 'no_token' | 'not_configured'
// 3) 仅 failed / not_configured 可重试;no_token 需先补设备 token
if (created.deliveries[0].status === 'not_configured') {
await admin.retryPushDelivery({ appkey: 'demo', deliveryId: created.deliveries[0].deliveryId });
}
// 4) 统计口径:deliveryCounts 来自服务端尝试记录;sent 只可能由未来真实适配器写入
const stats = await admin.getPushStats({ appkey: 'demo' });客户端注册 token
// Web/Electron SDK 已提供 token 方法;其它端可直接调客户端 REST
await client.registerPushToken({
platform: 'web',
token: await getVendorToken(), // 由宿主集成厂商 SDK 后取得
deviceId: 'browser-uuid',
appVersion: '2.0.0',
});
const mine = await client.getPushTokens();
await client.unregisterPushToken(mine.data[0].tokenId);curl -sS -X POST "http://127.0.0.1:8080/api/apps/demo/push/tokens" \
-H "authorization: Bearer $USER_TOKEN" \
-H 'content-type: application/json' \
-d '{"token":"vendor-device-token-abc","platform":"android","deviceId":"pixel-7","appVersion":"2.0.0"}'| 字段 / 规则 | 值 | 失败表现 |
|---|---|---|
platform | ios / android / web / huawei / xiaomi / oppo / vivo / fcm | 未知平台 1001 |
token | 8–512 字符(trim 后) | 长度非法 1001 |
| 每用户活跃上限 | 20 个 | 超限 1001 |
| 设备换绑 | 同一 (platform, token) 被其他用户注册 → 旧记录 active=false | 响应含 reboundFrom |
| 注销/封禁用户 | 不允许注册 token | 1016 |
webhook 链路自检
# 1) 配置 webhook(控制台或 Admin API)
curl -sS -X POST "http://127.0.0.1:8080/api/admin/apps/demo/push/channels" \
-H 'X-Admin-Secret: dev-admin' -H 'content-type: application/json' \
-d '{"channel":"apns","enabled":true,"fields":{"keyId":"ABC123","teamId":"TEAM123","bundleId":"com.example.app","privateKey":"-----BEGIN PRIVATE KEY-----..."}}'
# 2) 通道测试:明确返回未实现,不伪造成功
curl -sS -X POST "http://127.0.0.1:8080/api/admin/apps/demo/push/channels/apns/test" -H 'X-Admin-Secret: dev-admin'
# 3) 让接收者离线后发一条消息,观察你的 webhook 接收器是否收到 POST(3 秒超时、无重试)常见问题
为什么 sent 永远是 0?
厂商真实发送未实现;sent 只可能由真实适配器写入。用 webhook 链路或等待能力发布。
not_configured 需要修吗?
不需要:它表示“有 token 但适配器未实现/未配置”,是当前预期状态。
no_token 怎么修?
客户端注册设备 token;确认用户未注销/封禁;检查是否超过每用户 20 个上限。
webhook 没收到离线通知?
确认接收方确实无在线 WS 会话、webhookUrl 已配置且返回 2xx;该链路无重试,失败即丢。
能只给某个设备推吗?
投递按用户维度,不能选择具体 token;多设备会各自尝试。
点击通知能跳转吗?
未实现;webhook 载荷由应用自行决定跳转逻辑。