跳到主要内容

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 端职责

  1. 请求系统 Push 权限 — iOS 请求 UNAuthorizationOptionAlert + Sound + Badge;Android 请求通知权限
  2. 获取 device token — iOS 通过 UIApplicationDelegate.didRegisterForRemoteNotificationsWithDeviceToken 拿到 APNS token;Android 通过 FirebaseMessaging.getInstance().getToken() 拿到 FCM registration token
  3. 登录后获取 AOVIS App Bearer Token — 通过登录流程获取,用于 API 请求中的 Authorization header
  4. 调用 register API — 组装请求并 POST 到 /api/internal/push/register
  5. Token refresh 时重新注册 — APNS/FCM 会在特定条件下刷新 token,当 UIApplicationDelegate.didRegisterForRemoteNotificationsWithDeviceToken 再次收到新 token 时,或 FirebaseMessaging.onNewToken 回调时,重新调用 register
  6. 用户切换账号后重新注册 — 退出登录时清除本地 token,重新登录后再次注册
  7. 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"
tokenAPNS device token(hex string)或 FCM registration token
deviceId如果传入,后端会验证当前 user 对该 device 有 ACTIVE 状态的 ownership;可传 Prisma Device.idaoviseDeviceId(如 aovis-n4k-xxxxxx);无绑定设备时可不传,完成全局 token 注册
locale语言偏好,默认 "en"
timezoneIANA 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..."
}

错误响应

HTTPBody原因
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

title(后端 formatPushTitle 生成)

classifyEventTypetitle备注
vehicleVehicle detected可附加 at <deviceName>(≤ 50 chars)
personPerson detected
petPet detected
packagePackage delivered
otherActivity detected

body

来自 Nova Lite thumbnail classification summary,English only,≤ 80 chars

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. 当前限制与后续补充

  • unregister API:退出登录不清除 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