From 9482bb19f6951e9f89def0a1b21de61f30a8bbec Mon Sep 17 00:00:00 2001 From: richjohnrambo Date: Fri, 18 Sep 2026 13:51:22 +0800 Subject: [PATCH] fix: wake app for call pushes --- docs/IM-前后端对接文档.md | 5 +++-- server/push/jpush/push_jpush.go | 29 ++++++++++++++++++---------- server/push/jpush/push_jpush_test.go | 3 +++ 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/docs/IM-前后端对接文档.md b/docs/IM-前后端对接文档.md index 8036d52..4fd29eb 100644 --- a/docs/IM-前后端对接文档.md +++ b/docs/IM-前后端对接文档.md @@ -585,6 +585,7 @@ X-MeetingIM-APIKey: <预置的普通 API Key> "sound": "default", "badge": "+1", "category": "im_call", + "content-available": 1, "extras": { "type": "call_invite", "tenant_code": "default", @@ -606,7 +607,7 @@ X-MeetingIM-APIKey: <预置的普通 API Key> } ``` -来电推送必须走普通 APNs/极光 alert 通知,不使用 PushKit VoIP topic;iOS `notification.ios.category` 固定为 `im_call`,用于客户端通知按钮。`time_to_live` 和 `expires_at` 以 30 秒左右短有效期为准,过期后服务端不再投递。 +来电推送必须走普通 APNs/极光 alert 通知,不使用 PushKit VoIP topic;iOS `notification.ios.category` 固定为 `im_call`,用于客户端通知按钮,同时带 `content-available: 1` 唤醒后台 App 拉取 `topic/seq` 并启动 Live Activity。`time_to_live` 和 `expires_at` 以 30 秒左右短有效期为准,过期后服务端不再投递。 #### 0.5.5 Long Polling 与文件基础示例 @@ -2414,7 +2415,7 @@ GET /v1/im/topics/{topic}/message-mutations?since_version={localMutationVersion} - 来电是 Topic 下行的初始 `{data}`:`head.webrtc="started"`、`head.x-im-type="call"`,`content` 至少含 `callId,callType,audio,video`。客户端保存该消息 `seq` 作为后续所有 call note 的关联 ID。 - 来电端开始响铃后发送 `{note:{topic,what:"call",event:"ringing",seq,payload:{"callId":"..."}}}`;接听发送 `event:"accept"`;拒绝发送 `event:"hang-up"`。 -- 后台 Push 只携带 `callId,topic,seq,caller_id,caller_display,callType,expires_at`,不含 SDP、ICE 或 Token;iOS 来电 Push 使用普通 alert,`category=im_call`,短 TTL 约 30 秒,不使用 PushKit VoIP。 +- 后台 Push 只携带 `callId,topic,seq,caller_id,caller_display,callType,expires_at`,不含 SDP、ICE 或 Token;iOS 来电 Push 使用普通 alert,`category=im_call`,`content-available=1`,短 TTL 约 30 秒,不使用 PushKit VoIP。 - 被叫在未发送 `ringing/accept` 前也可以直接发送 `hang-up`,服务端按 `topic+seq+callId` 校验后立即结束主叫侧振铃,并把原通话消息替换为 `declined`;主叫主动取消则替换为 `missed`。 ### 8.3 语音通话-对方待接受状态.png diff --git a/server/push/jpush/push_jpush.go b/server/push/jpush/push_jpush.go index d776227..81f5233 100644 --- a/server/push/jpush/push_jpush.go +++ b/server/push/jpush/push_jpush.go @@ -79,11 +79,12 @@ type androidPayload struct { } type iosPayload struct { - Alert iosAlert `json:"alert"` - Sound string `json:"sound,omitempty"` - Badge string `json:"badge,omitempty"` - Category string `json:"category,omitempty"` - Extras map[string]string `json:"extras,omitempty"` + Alert iosAlert `json:"alert"` + Sound string `json:"sound,omitempty"` + Badge string `json:"badge,omitempty"` + Category string `json:"category,omitempty"` + ContentAvailable int `json:"content-available,omitempty"` + Extras map[string]string `json:"extras,omitempty"` } type iosAlert struct { @@ -501,11 +502,12 @@ func buildPayload(audience map[string][]string, topic string, data map[string]st Priority: 1, }, IOS: &iosPayload{ - Alert: iosAlert{Title: title, Body: alert}, - Sound: "default", - Badge: badge, - Category: iosCategory(data), - Extras: data, + Alert: iosAlert{Title: title, Body: alert}, + Sound: "default", + Badge: badge, + Category: iosCategory(data), + ContentAvailable: iosContentAvailable(data), + Extras: data, }, HMOS: &hmosPayload{ Title: title, @@ -548,6 +550,13 @@ func iosCategory(data map[string]string) string { return "" } +func iosContentAvailable(data map[string]string) int { + if data["type"] == "call_invite" { + return 1 + } + return 0 +} + func hydrateCallInviteData(tenantID t.TenantID, data map[string]string) { if data["type"] != "call_invite" || strings.TrimSpace(data["caller_display"]) != "" { return diff --git a/server/push/jpush/push_jpush_test.go b/server/push/jpush/push_jpush_test.go index c8ea81c..815f511 100644 --- a/server/push/jpush/push_jpush_test.go +++ b/server/push/jpush/push_jpush_test.go @@ -190,6 +190,9 @@ func TestBuildPayloadUsesShortTTLForCalls(t *testing.T) { if payload.Notification.IOS.Category != callCategory { t.Fatalf("unexpected iOS category: %s", payload.Notification.IOS.Category) } + if payload.Notification.IOS.ContentAvailable != 1 { + t.Fatalf("unexpected iOS content-available: %d", payload.Notification.IOS.ContentAvailable) + } if payload.Notification.IOS.Alert.Body != "张三邀请你语音通话" { t.Fatalf("unexpected body: %s", payload.Notification.IOS.Alert.Body) }