2026-10-26 声音接入补充: 用户原生通知现支持按场景/语种选择前台、后台及锁屏声音;App 需携带 WAV 并初始化 Android 渠道。完整资源、字段与测试示例见 035 原生声音接入。匿名 POST /system/push/testNative 可选 language,场景仍放原 payload JSON 字符串,未知语言默认繁中;原生测试不改变默认 uni 模式。商家/骑手原声音保持,旧请求未含场景继续默认声音。
状态(2026-09-29):服务端具备 FCM/APNs 路由能力,默认 uni,尚未启用原生推送。本功能不处理音视频通话,也不包含 App 端 token 获取、上报和展示改造;未完成真机验证前不要切换生产环境。
三端真实凭据已按用户明确要求提交在 ruoyi-admin/src/main/resources/business-push/,并随 JAR 打包;每端只保留一份普通 APNs .p12,开发/正式只切换网关。任何能读取 Git 历史、JAR 或包含资源的镜像的人都能取得服务账号私钥与证书密码。须限制仓库及构建产物访问,不把密钥内容写入日志或公开分发;如发生泄露,应轮换 Firebase 服务账号与 APNs 证书,并重新构建发布。
| 接收端 | FCM 服务账号 | APNs 普通证书 | APNs topic/密码配置 |
|---|---|---|---|
| 用户 | user-fcm-service-account.json |
user-apns.p12 |
user-apns-config.json |
| 商家(含夜市及分管账号) | merchant-fcm-service-account.json |
merchant-apns.p12 |
merchant-apns-config.json |
| 骑手 | rider-fcm-service-account.json |
rider-apns.p12 |
rider-apns-config.json |
APNs配置JSON原文件的packetName/pwd分别提供topic和证书密码;2026-10-26按用户要求已将这些既有值直接固定到application.yml,各端路径也固定为自身classpath资源。不生成或重设证书密码。三端Firebase项目分别对应各自App,不共享server.json,不使用VoIP证书。
| 设置 | 本地配置项 | 当前值/要求 |
|---|---|---|
| 通道 | business-push.mode |
固定uni;如启用native直接改YAML并重启 |
| APNs环境 | business-push.apns-environment |
当前development(sandbox);正式签名包(含TestFlight/App Store)设为production |
| 三端FCM路径 | apps.{user/merchant/rider}.fcm-credentials-path |
固定各端classpath JSON |
| 三端APNs路径 | apps.{user/merchant/rider}.apns-certificate-path |
固定各端classpath .p12 |
| 三端配套JSON路径 | apps.{user/merchant/rider}.apns-config-path |
固定各端classpath配套JSON |
| 三端密码/topic | apps.{user/merchant/rider}.apns-certificate-password/apns-topic |
YAML固定值来自各自JSON,非服务端自行生成 |
不再通过BUSINESS_PUSH系列环境变量修改配置;直接维护YAML。两项APNs值均固定后,发送器优先使用YAML,无需再从JSON补空值;未来更换证书时须同步更新YAML中的密码/topic,只改JSON不会覆盖这些固定值。uni模式不初始化业务原生凭据;native模式启动时校验三端并加载资源,缺失/不可读/无私钥/过期会失败。匿名测试仍按需初始化所选端。
development/production分别对应sandbox/正式网关,当前配置development;匿名测试和业务原生发送均遵循该配置,不再固定正式网关。正式签名包部署前改为production。APNs环境取决于安装包签名与Token,不能仅凭业务服务器是测试还是生产来判断;TestFlight/App Store包即使连接测试服务器,也仍须使用production。已核对三端packetName与证书主题相符,但外部真实投递仍须真机验证。三端证书到期日为2027-07-30;改配置或资源后重新构建部署JAR并验证,不通过重新生成密码解决BadDeviceToken。
info_user.deviceToken,并把 cidType 设置为 android 或 ios;cid 只用于 uniPush,不能复制到 deviceToken。本次交付不实现 App 端上报。cidType/deviceToken。application.yml 设置对应 APNs 环境与 business-push.mode: native 并重启。先检查服务启动,再用三端 Android/iOS 各一台真机触发订单、闪送和支付通知;核对通知栏、点击 payload、消息中心、语言与接收人。iOS 骑手使用 pushsound.caf;Android 骑手 FCM 带 qsRing 通知渠道标识,须由 App 创建并配置该渠道才能保证自定义铃声,本次不修改 App。门店订单有可用分管账号时只通知分管账号,没有时才回退主账号。生产切换须先完成 App 端配套和真机验收。使用包含上述资源的受控构建,在 YAML 将 business-push.mode 设为 native,按实例逐一重启并检查启动及推送错误日志;一条通知只会选择当前实例的一个通道,滚动重启期间不同实例可能短暂处于不同模式。若原生投递异常,改回 YAML 的 business-push.mode: uni 并逐一重启;之后的新通知重新使用 cid,已失败的通知不会由服务端自动补发。回退时确认 App 仍维护有效的 uniPush CID。
自动化测试验证三端 classpath 凭据可加载、路由、请求构造和错误隔离;真实设备 token、FCM/APNs 外部投递、通知展示与回退操作仍需部署环境验证。
常驻接口 POST /system/push/testNative 允许匿名访问,Content-Type: application/json,无需账号 ID 或后台 token。即使 YAML 的 business-push.mode 保持默认 uni,该接口仍直接调用原生通道;Android 使用所选端的 FCM 项目,iOS 遵循 apns-environment,当前为 sandbox。请求示例:
{
"appType": "rider",
"platform": "android",
"deviceToken": "手机取得的原生 FCM 或 APNs Token",
"title": "原生推送测试",
"content": "请检查手机通知栏",
"payload": "{}"
}
appType 可填 user、merchant、rider;platform 可填 android、ios。测试 iOS 时改为 ios 并传与所配网关匹配的 64 位十六进制 APNs token:development 对应开发签名包,production 对应正式签名包;两类 Token 不能混用。payload 可省略。响应 code=200 只表示 FCM/APNs 接受发送请求,不证明手机通知栏已显示;失败时 code=500 和 msg 返回不含 token 的错误类别。需在真机核对通知权限、前台展示与通知栏,不调用业务订单流程,也不写入 push_message。
该接口按需求长期匿名开放,公网部署后任何知道地址的人都可以使用服务端凭据向其掌握的设备 Token 发送消息,可能造成滥用和推送配额消耗;如不接受此风险,应在网关限制可访问来源或后续改为鉴权接口。
外卖和闪送新单通知附近最多 20 名骑手时,native 模式会按骑手当前设备类型分流:Android 骑手按相同语言文案和订单 payload 使用骑手端 FCM multicast,逐 token 处理返回结果;iOS 骑手继续逐设备走 APNs。每位骑手的消息中心事件保持独立,批量失败不回退 uniPush。默认 uni 模式及其逐骑手新单通知完全不变,旧的 uni 批量方法未接入此流程。
后端改用参考工程相同的 Pushy 0.13.10,三端现有普通推送证书和 topic 继续使用,不需要换 App 接口或增加配置。业务原生模式和测试接口都按所配环境发送,客户端按接收端和环境缓存复用。
测试接口等待苹果返回结果:接受后才返回成功,拒绝继续显示 APNS_BadDeviceToken 等类别;等待超过 10 秒返回 APNS_TIMEOUT,网络错误返回 APNS_TRANSPORT_ERROR。超时不表示苹果一定未接受,请勿直接据此重复发送。日志记录拒绝的端、环境、类别,不记录完整 Token 或密钥。
本地证书加载和模拟苹果响应可通过自动测试验证;是否消除这台设备的 BadDeviceToken,仍须部署新 JAR 后用对应 App 当前上报且匹配网关的 Token 调用测试接口并核对真机通知。替换 SDK 不能替代真实投递验收。