AOVIS Mobile Push Integration Guide
本文给手机 App 研发团队提供 APNS/FCM token 注册与 AI 事件推送对接说明。 后端状态:Sprint 1 Type 1 实时推送链路已完成生产部署并通过 mock 验证,等待 App 端注册真实 push token。 关联文档:
deploy-archive-2026-05-17-ai-push-sprint1.md|ai-push-pipeline-implementation-plan.md
1. 目的
- App 端从 iOS/Android 系统获取 APNS 或 FCM token
- App 调用后端
/api/internal/push/register注册 token - 后端通过 AWS SNS Platform Endpoint 给设备发送 AI 事件通知
本文仅覆盖 Type 1 实时分类推送(event-triggered push)。Daily Summary(Type 2)不在当前范围。
2. 当前后端状态
| 项目 | 状态 |
|---|---|
| IoT webhook → Nova Lite classification → CloudEvent | ✅ 已验证(mock event) |
| push token 注册 API | ✅ 已部署 |
| SNS endpoint push 函数层 | ✅ 已实现 |
| SNS Platform Application 配置 | ⏳ 需运维配置 ARN |
| 真机 push | ⏳ 等待 App 注册 token |
PushToken.endpointArn 当前数量 | 0 |
当前 pushSent=false 是预期状态——没有活跃的 endpointArn token 就不会触发推送。运维配置好 SNS Platform ARN 后,register API 会自动创建 Platform Endpoint 并回填 endpointArn。
3. App 端职责
- 请求系统 Push 权限 — iOS 请求
UNAuthorizationOptionAlert + Sound + Badge;Android 请求通知权限 - 获取 device token — iOS 通过
UIApplicationDelegate.didRegisterForRemoteNotificationsWithDeviceToken拿到 APNS token;Android 通过FirebaseMessaging.getInstance().getToken()拿到 FCM registration token - 登录后获取 AOVIS App Bearer Token — 通过登录流程获取,用于 API 请求中的
Authorizationheader - 调用 register API — 组装请求并 POST 到
/api/internal/push/register - Token refresh 时重新注册 — APNS/FCM 会在特定条件下刷新 token,当
UIApplicationDelegate.didRegisterForRemoteNotificationsWithDeviceToken再次收到新 token 时,或FirebaseMessaging.onNewToken回调时,重新调用 register - 用户切换账号后重新注册 — 退出登录时清除本地 token,重新登录后再次注册
- App reinstall 后重新注册 — 系统会重新分配 APNS/FCM token,按步骤 2-4 处理
注意事项:
- APNS device token ≠ Apple Login identity token
- FCM registration token ≠ Firebase Auth token
- 不要截断 token(后端接受最大 2048 字符)
- 后端使用 upsert 逻辑,同 user / platform / token 重复注册是安全的
4. 注册 API
Endpoint
POST https://aovis.app/api/internal/push/register
Headers
Authorization: Bearer <app-token>
Content-Type: application/json
Body
{
"platform": "ios" | "android",
"token": "string, 1..2048 chars",
"deviceId": "optional — Device.id or aoviseDeviceId",
"locale": "optional, default \"en\"",
"timezone": "optional, default \"America/New_York\""
}
字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
platform | 是 | 只能是 "ios" 或 "android" |
token | 是 | APNS device token(hex string)或 FCM registration token |
deviceId | 否 | 如果传入,后端会验证当前 user 对该 device 有 ACTIVE 状态的 ownership;可传 Prisma Device.id 或 aoviseDeviceId(如 aovis-n4k-xxxxxx);无绑定设备时可不传,完成全局 token 注册 |
locale | 否 | 语言偏好,默认 "en" |
timezone | 否 | IANA timezone,默认 "America/New_York" |
iOS 示例
curl -X POST https://aovis.app/api/internal/push/register \
-H "Authorization: Bearer <app-token>" \
-H "Content-Type: application/json" \
--data '{
"platform": "ios",
"token": "<apns-device-token>",
"locale": "en",
"timezone": "America/New_York"
}'
Android 示例
curl -X POST https://aovis.app/api/internal/push/register \
-H "Authorization: Bearer <app-token>" \
-H "Content-Type: application/json" \
--data '{
"platform": "android",
"token": "<fcm-registration-token>",
"locale": "en",
"timezone": "America/Los_Angeles"
}'
成功响应
{
"success": true,
"push_token_id": "cuid..."
}
错误响应
| HTTP | Body | 原因 |
|---|---|---|
| 401 | {"error":"invalid_session"} | App token 未传或无效 |
| 400 | {"error":"invalid_body"} | platform/token 格式错误,或 deviceId 不属于当前用户 |
| 500 | {"error":"internal_error"} | 服务端异常 |
5. 调用时机
建议 App 在以下时机调用 register API:
- 用户登录成功后
- 用户授权 Push 权限后(避免过早弹窗)
- APNS/FCM token 首次获取后
- APNS/FCM token refresh 后
- 用户切换账号后
- App reinstall 后首次启动并登录后
说明:
- 后端 upsert 基于
userId + platform + token唯一约束,重复注册安全 - token refresh 后直接用新 token 再次调用 register 即可,后端会更新记录
- 当前没有 unregister API。退出登录时无需删除 token
6. 推送 payload 与 deep link
title(后端 formatPushTitle 生成)
| classifyEventType | title | 备注 |
|---|---|---|
vehicle | Vehicle detected | 可附加 at <deviceName>(≤ 50 chars) |
person | Person detected | |
pet | Pet detected | |
package | Package delivered | |
other | Activity detected |
body
来自 Nova Lite thumbnail classification summary,English only,≤ 80 chars。
deepLink
aovis://event/<cloudEvent.id>
data payload
{
"cloudEventId": "<cloudEvent.id>",
"deviceId": "<aoviseDeviceId>",
"eventType": "person|vehicle|pet|package|other"
}
iOS APNS 最终结构
{
"aps": {
"alert": {
"title": "Vehicle detected",
"body": "A dark SUV entered the driveway"
},
"sound": "default",
"mutable-content": 1
},
"deepLink": "aovis://event/<cloudEvent.id>",
"cloudEventId": "<cloudEvent.id>",
"deviceId": "<aoviseDeviceId>",
"eventType": "vehicle"
}
Android FCM 最终结构
{
"fcmV1Message": {
"message": {
"notification": {
"title": "Vehicle detected",
"body": "A dark SUV entered the driveway"
},
"data": {
"deepLink": "aovis://event/<cloudEvent.id>",
"cloudEventId": "<cloudEvent.id>",
"deviceId": "<aoviseDeviceId>",
"eventType": "vehicle"
}
}
}
}
注意:
- 这是后端发送给 AWS SNS 的 GCM payload 结构;App 端实际处理 FCM
notification/data字段即可 - Android
data字段所有 value 均为 string - App 收到 push 后优先读取
deepLink进行路由 - 当前 event detail 页面(
aovis://event/<id>)如果尚未实现,可以先落到设备 activity 列表页
7. 真机联调步骤
按顺序确认:
- 运维配置
SNS_APNS_PLATFORM_ARN/SNS_FCM_PLATFORM_ARN - App 获取系统 push 权限并通过,拿到真实 APNS device token 或 FCM registration token
- App 用登录后的 Bearer Token 调用
POST /api/internal/push/register - 后端 DB 验证
PushToken.endpointArn非空 - 后端 mock IoT event → CloudEvent 出现
status=classified的记录 - 后端 DB 验证
CloudEvent.pushSent = true - 真机收到 push
- 点击 push 打开
aovis://event/<id>deep link
DB 验证 SQL(只读查询,需运维配合)
-- 检查 PushToken 注册状态
SELECT id, platform, "endpointArn", "isActive", "lastUsedAt"
FROM "PushToken"
ORDER BY "updatedAt" DESC
LIMIT 20;
-- 检查 CloudEvent 推送状态
SELECT id, "eventId", status, "classifyEventType", "classifySummaryShort", "pushSent", "pushSentAt"
FROM "CloudEvent"
ORDER BY "createdAt" DESC
LIMIT 20;
8. 当前限制与后续补充
- 无
unregisterAPI:退出登录不清除 token,后续 Sprint 补充 - 无用户级 push 偏好(静音某些设备/事件类型)
- 无 quiet hours 功能
- 无 Daily Summary(Sprint 2 内容)
- 无 App 内 event detail 页面(
aovis://event/<id>需要 App 端实现路由处理) - 固件
thumbnail_s3_key协议仍在 Sprint 3,当前 mock 事件使用手动上传的测试缩略图 - 真机 push 未验证前,不对外部宣称手机通知功能已完成
9. App 交付验收信息
App 侧完成 token 注册后,需向后端团队确认以下信息:
| 项 | 状态/值 |
|---|---|
| iOS bundle identifier | |
| APNS 环境(Sandbox / Production) | |
| Firebase 项目 / Android package name | |
至少一台 iOS 测试机 register API 返回 push_token_id | |
至少一台 Android 测试机 register API 返回 push_token_id | |
App 能否处理 aovis://event/<id> deep link | |
| 联调经过以下环境:Staging / Production |