From 0ca5d43fdba0c01c1922855f6d65c4230771b3e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=8B=A4=E6=B0=91?= Date: Fri, 1 May 2026 21:27:39 +0800 Subject: [PATCH] =?UTF-8?q?feat(sdk):=20=E6=9B=B4=E6=96=B0=20SDK=20?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E6=96=87=E6=A1=A3=E5=92=8C=20API=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 expiresAt 和 refreshUserSig 参数支持自动续签 - 修改 PushSDK 初始化方式,自动完成设备注册和厂商初始化 - 调整过期续签策略,从提前 15 分钟改为提前 5 分钟触发 - 重构 RN SDK 文档结构,简化安装和使用方式 - 更新统一登录流程,支持 profile 信息传递 - 添加 IM 数据库自动隔离功能 - 修复 Android 群消息聚合问题 - 补充自动化测试验证和错误处理机制 --- src/api/demo.ts | 10 ++++++++++ src/context/AuthContext.tsx | 32 ++++++++++++++++++++------------ src/utils/storage.ts | 3 ++- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/api/demo.ts b/src/api/demo.ts index f60bf8c..85d9fc9 100644 --- a/src/api/demo.ts +++ b/src/api/demo.ts @@ -43,7 +43,9 @@ export interface UserProfile { export interface AuthResult { demoToken: string + demoTokenExpiresAt?: number | null imToken: string + imTokenExpiresAt?: number | null profile: UserProfile } @@ -64,6 +66,14 @@ export const demoApi = { }) }, + refreshImToken(appId: string = APP_ID): Promise<{ imToken: string; imTokenExpiresAt?: number | null }> { + return request('/auth/refresh-im', { + method: 'POST', + skipAuth: false, + params: { appId }, + }) + }, + getProfile(): Promise { return request('/user/profile') }, diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx index ab954d3..4167be0 100644 --- a/src/context/AuthContext.tsx +++ b/src/context/AuthContext.tsx @@ -1,16 +1,12 @@ import React, { createContext, useCallback, useContext, useEffect, useState } from 'react' import { Alert, Linking } from 'react-native' -import { XuqmSDK, ImSDK, UpdateSDK } from '@xuqm/rn-sdk' +import { XuqmSDK, UpdateSDK } from '@xuqm/rn-sdk' import { demoApi, type UserProfile } from '../api/demo' import { save, load, clearSession, K } from '../utils/storage' import pluginMeta from '../../plugin.json' const APP_ID = 'ak_demo_chat' -function buildImDbName(appId: string, userId: string): string { - return `xuqm_im_${appId}_${userId}` -} - interface AuthState { ready: boolean userId: string | null @@ -30,11 +26,21 @@ const AuthContext = createContext(null) export function AuthProvider({ children }: { children: React.ReactNode }) { const [state, setState] = useState({ ready: false, userId: null, profile: null }) - const initSDK = useCallback(async (profile: UserProfile, imToken: string) => { + const initSDK = useCallback(async (profile: UserProfile, imToken: string, imTokenExpiresAt?: number | null) => { const appKey = profile.appId || APP_ID await XuqmSDK.initialize({ appKey }) - const appId = appKey - await ImSDK.loginWithToken(profile.userId, imToken, buildImDbName(appId, profile.userId)) + await XuqmSDK.login({ + userId: profile.userId, + userSig: imToken, + expiresAt: imTokenExpiresAt ?? undefined, + profile, + refreshUserSig: async () => { + const refreshed = await demoApi.refreshImToken(appKey) + await save(K.IM_TOKEN, refreshed.imToken) + await save(K.IM_TOKEN_EXPIRES_AT, refreshed.imTokenExpiresAt ?? null) + return { userSig: refreshed.imToken, expiresAt: refreshed.imTokenExpiresAt ?? undefined } + }, + }) setState({ ready: true, userId: profile.userId, profile }) UpdateSDK.checkAppUpdate() @@ -60,9 +66,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { try { const demoToken = await load(K.DEMO_TOKEN) const imToken = await load(K.IM_TOKEN) + const imTokenExpiresAt = await load(K.IM_TOKEN_EXPIRES_AT) const profile = await load(K.PROFILE) if (demoToken && imToken && profile) { - await initSDK(profile, imToken) + await initSDK(profile, imToken, imTokenExpiresAt ?? undefined) return } } catch { @@ -73,11 +80,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { restore() }, [initSDK]) - const handleAuthResult = useCallback(async (result: { demoToken: string; imToken: string; profile: UserProfile }) => { + const handleAuthResult = useCallback(async (result: { demoToken: string; demoTokenExpiresAt?: number | null; imToken: string; imTokenExpiresAt?: number | null; profile: UserProfile }) => { await save(K.DEMO_TOKEN, result.demoToken) await save(K.IM_TOKEN, result.imToken) + await save(K.IM_TOKEN_EXPIRES_AT, result.imTokenExpiresAt ?? null) await save(K.PROFILE, result.profile) - await initSDK(result.profile, result.imToken) + await initSDK(result.profile, result.imToken, result.imTokenExpiresAt ?? undefined) }, [initSDK]) const login = useCallback(async (userId: string, password: string) => { @@ -91,7 +99,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { }, [handleAuthResult]) const logout = useCallback(async () => { - ImSDK.disconnect() + await XuqmSDK.logout() await clearSession() setState({ ready: true, userId: null, profile: null }) }, []) diff --git a/src/utils/storage.ts b/src/utils/storage.ts index 827e775..356fa63 100644 --- a/src/utils/storage.ts +++ b/src/utils/storage.ts @@ -3,6 +3,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage' export const K = { DEMO_TOKEN: '@xuqm:demoToken', IM_TOKEN: '@xuqm:imToken', + IM_TOKEN_EXPIRES_AT: '@xuqm:imTokenExpiresAt', PROFILE: '@xuqm:profile', CONTACTS: '@xuqm:contacts', } as const @@ -22,5 +23,5 @@ export async function remove(key: string): Promise { } export async function clearSession(): Promise { - await Promise.all([K.DEMO_TOKEN, K.IM_TOKEN, K.PROFILE].map(k => AsyncStorage.removeItem(k))) + await Promise.all([K.DEMO_TOKEN, K.IM_TOKEN, K.IM_TOKEN_EXPIRES_AT, K.PROFILE].map(k => AsyncStorage.removeItem(k))) }