From 9ba90eaf6ecccfda450d651ccdda674b0629b873 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Mon, 15 Jun 2026 11:01:27 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E9=87=8D=E5=86=99=20SDK-API=E5=8F=82?= =?UTF-8?q?=E8=80=83.md=20=E4=BB=A5=E5=AF=B9=E9=BD=90=20v0.4.0=20=E6=96=B0?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 完整覆盖:XuqmSDK 初始化两种方式(含配置文件解密流程)、setUserInfo 认证枢纽; PushSDK 只暴露 setOfflinePushEnabled/setQuietHours/clearQuietHours/logout; UpdateSDK registerPlugins/updatePlugin/downloadAndInstallApk/setBundleCallbacks; ImSDK refreshToken 和订阅机制;LicenseSDK 无需初始化。 标注所有已移除 API 和替代方案。 Co-Authored-By: Claude Sonnet 4.6 --- docs/SDK-API参考.md | 929 ++++++++++++++------------------------------ 1 file changed, 297 insertions(+), 632 deletions(-) diff --git a/docs/SDK-API参考.md b/docs/SDK-API参考.md index fd095e4..43a5122 100644 --- a/docs/SDK-API参考.md +++ b/docs/SDK-API参考.md @@ -1,8 +1,8 @@ # XuqmGroup RN SDK API 参考 -> 版本:基于源码自动生成,最后更新 2026-06-15 -> -> 包名:`@xuqm/rn-common` · `@xuqm/rn-update` · `@xuqm/rn-xwebview` · `@xuqm/rn-push` · `@xuqm/rn-im` +> 版本:v0.4.0(待发布) · 最后更新 2026-06-15 +> 包名:`@xuqm/rn-common` · `@xuqm/rn-update` · `@xuqm/rn-push` · `@xuqm/rn-im` · `@xuqm/rn-license` +> 设计规范:`XuqmGroup-Docs/design/06-sdk-cross-platform-spec.md` --- @@ -10,110 +10,101 @@ > `import { XuqmSDK } from '@xuqm/rn-common'` -SDK 核心初始化模块。所有其他 SDK 模块依赖 XuqmSDK 完成初始化后才能正常工作。 +SDK 核心模块。负责从平台拉取服务配置,并作为所有子 SDK 用户状态的统一分发中心。 -### 1.1 类型定义 +### 类型定义 ```ts interface XuqmInitOptions { - appKey: string; // 应用标识(从租户平台获取) - debug?: boolean; // 是否开启调试日志 + appKey: string // 应用标识(从租户平台获取) + platformUrl?: string // 平台地址;不传则使用内置默认公有平台地址 + debug?: boolean } interface XuqmConfig { - appKey: string; - apiUrl: string; - imWsUrl: string; - fileServiceUrl: string; - debug: boolean; + appKey: string + apiUrl: string // 通用 API 地址 + imWsUrl: string // IM WebSocket 地址 + fileServiceUrl: string // 文件服务地址 + licenseUrl: string // License 服务地址 + debug: boolean + imEnabled: boolean // 是否开通 IM 服务 + pushEnabled: boolean // 是否开通推送服务 + licenseEnabled: boolean // 是否开通 License 服务 } interface XuqmUserInfo { - userId?: string; - name?: string; - email?: string; - phone?: string; + userId: string // 必填 + userSig?: string // IM 服务必填;未开通 IM 时可不传 + name?: string + phone?: string + avatar?: string } ``` -### 1.2 API 方法 +### 初始化 API #### `XuqmSDK.initialize(options): Promise` -异步初始化。从租户平台拉取远程配置(IM WebSocket URL、文件服务 URL 等),失败时回退到内置默认地址。 +手动初始化(方式 B)。请求平台获取 appKey 专属服务配置(各服务 URL 和开通状态)。 + +- `platformUrl` 未传时使用内置默认公有平台地址 +- **失败时直接抛出错误,不降级**,调用方必须处理异常 ```ts -await XuqmSDK.initialize({ appKey: "your-app-key", debug: __DEV__ }); +// 公有平台 +await XuqmSDK.initialize({ appKey: 'your-app-key', debug: __DEV__ }) + +// 私有化部署 +await XuqmSDK.initialize({ + appKey: 'your-app-key', + platformUrl: 'https://your-server.com', +}) ``` -#### `XuqmSDK.init(options): void` - -同步初始化(不拉取远程配置,直接使用内置默认 URL)。适用于网络不可用或无需远程配置的场景。 - -```ts -XuqmSDK.init({ appKey: "your-app-key" }); -``` - -#### `XuqmSDK.initializeFromLicense(file, options?): void` - -从已解密的 License 文件对象初始化。通常配合 License SDK 使用。 - -```ts -interface LicenseFile { - appKey: string; - baseUrl?: string; - serverUrl?: string; -} -XuqmSDK.initializeFromLicense(decryptedFile, { debug: false }); -``` - -#### `XuqmSDK.initWithConfigFile(encryptedContent, options?): Promise` - -从加密配置文件(`.xuqmconfig`)初始化。SDK 自动解密并初始化,支持 `XUQM-CONFIG-V1` 和 `XUQM-LICENSE-V1` 两种格式。 - -```ts -import config from "./assets/app.xuqmconfig"; -await XuqmSDK.initWithConfigFile(config); -``` +> 方式 A(配置文件自动初始化):将加密 `.xuqmconfig` 文件放置到 `src/assets/xuqm/config.xuqmconfig`,SDK 自动读取、解密(AES-GCM)并调用 `initialize()`,App 无需任何初始化代码。 #### `XuqmSDK.awaitInitialization(): Promise` -等待初始化完成。在异步初始化场景下,其他模块可调用此方法确保 SDK 已就绪。 +等待初始化完成。子 SDK 内部调用,确保配置就绪后再使用。 -```ts -await XuqmSDK.awaitInitialization(); -``` - -#### `XuqmSDK.setUserId(userId): void` - -设置当前用户 ID。登录成功后调用。 - -```ts -XuqmSDK.setUserId("user-123"); -``` - -#### `XuqmSDK.getUserId(): string | null` - -获取当前用户 ID。 +### 用户认证 API #### `XuqmSDK.setUserInfo(info): void` -设置用户信息(用于灰度发布和 License 验证)。登录成功后调用,同时会自动同步 `userId`。 +用户认证核心枢纽。**登录后调用一次,所有子 SDK 自动同步**;传 `null` 触发全局登出。 ```ts -XuqmSDK.setUserInfo({ userId: "user-123", name: "张三", phone: "13800138000" }); +// 登录 +XuqmSDK.setUserInfo({ + userId: 'user_001', + userSig: 'sig_from_server', // IM 必填 + name: '张三', + phone: '13800138000', +}) + +// 登出 +XuqmSDK.setUserInfo(null) ``` +| 子 SDK | `setUserInfo(info)` 触发动作 | `setUserInfo(null)` 触发动作 | +|--------|------------------------------|------------------------------| +| PushSDK | 自动检测厂商 → 获取配置 → 注册设备 → 上报 token | 解绑 token | +| ImSDK | 若 `userSig` 存在且 IM 已开通,自动登录 | 断开 WebSocket 连接 | +| UpdateSDK | 更新 userId(用于定向更新) | — | +| LicenseSDK | 更新用户上下文 | — | + +#### `XuqmSDK.getUserId(): string | null` + #### `XuqmSDK.getUserInfo(): XuqmUserInfo | null` -获取当前用户信息。 +### 已移除 API -### 1.3 独立导出 - -```ts -import { awaitInitialization } from "@xuqm/rn-common"; -// 等同于 XuqmSDK.awaitInitialization() -``` +| 方法 | 移除原因 | +|------|---------| +| `XuqmSDK.init(options): void` | 同步 init 不拉取服务配置,行为不完整 | +| `XuqmSDK.initializeFromLicense(file)` | License 不承担 SDK 初始化职责 | +| `XuqmSDK.initWithConfigFile(encrypted)` | 由自动初始化机制内部调用,不对外暴露 | --- @@ -121,646 +112,320 @@ import { awaitInitialization } from "@xuqm/rn-common"; > `import { UpdateSDK } from '@xuqm/rn-update'` -应用整包更新 + RN 插件(Bundle)热更新。 - -### 2.1 类型定义 +### 类型定义 ```ts -interface PluginMeta { - moduleId: string; // 插件唯一标识,如 'buz1' - version: string; // 当前 bundle 版本号 +interface PluginRegistration { + moduleId: string // 版本号由 SDK 自动从缓存读取,不需要传入 } interface AppUpdateInfo { - needsUpdate: boolean; - versionName?: string; // 最新版本名,如 '2.1.0' - versionCode?: number; // 最新版本号(整数) - downloadUrl?: string; // APK 直接下载地址 - changeLog?: string; // 更新日志 - forceUpdate?: boolean; // 是否强制更新 - appStoreUrl?: string; // iOS App Store 地址 - marketUrl?: string; // Android 应用商店地址 - requiresLogin?: boolean; // 服务端要求登录后才能检查 - alreadyDownloaded?: boolean; // APK 是否已下载(仅 Android) - apkHash?: string | null; // APK SHA-256 校验值 + needsUpdate: boolean + versionName?: string + versionCode?: number + downloadUrl?: string // APK 直接下载地址(Android) + changeLog?: string + forceUpdate?: boolean + appStoreUrl?: string // iOS App Store 地址 + marketUrl?: string // Android 应用商店地址 + apkHash?: string | null // APK SHA-256 } interface PluginUpdateInfo { - needsUpdate: boolean; - latestVersion: string; - downloadUrl: string; - md5: string; - minCommonVersion: string; // 要求的最低 common bundle 版本 - note: string; -} - -interface CachedRnBundle { - moduleId: string; - version: string; - md5: string; - downloadedAt: string; - source: string; + needsUpdate: boolean + latestVersion: string + currentVersion: string // SDK 自动填入当前版本 + downloadUrl: string + md5: string + minCommonVersion: string + note: string + forceUpdate?: boolean } ``` -### 2.2 API 方法 +### 插件注册 + +#### `UpdateSDK.registerPlugins(plugins): void` + +批量注册插件(推荐)。版本号由 SDK 自动从本地缓存读取(首次为 `0.0.0`)。 + +```ts +UpdateSDK.registerPlugins([ + { moduleId: 'buz1' }, + { moduleId: 'buz2' }, + { moduleId: 'buz3' }, +]) +``` #### `UpdateSDK.registerPlugin(meta): void` -注册插件元数据。在插件 bundle 入口文件顶部调用。 +单个注册(向后兼容)。`version` 字段已废弃,传入无效。 + +#### `UpdateSDK.setBundleCallbacks(callbacks): void` + +注入宿主 BundleRuntime 的写入和重载能力。宿主在初始化时调用一次。 ```ts -// src/plugins/buz1/bundle.ts -UpdateSDK.registerPlugin({ moduleId: "buz1", version: "1.0.0" }); +import { writeBundleFile, loadBundle } from '@native/BundleRuntime' + +UpdateSDK.setBundleCallbacks({ + writeBundle: writeBundleFile, + reloadBundle: loadBundle, +}) ``` -#### `UpdateSDK.getRegisteredPluginVersion(moduleId): string | undefined` +### 插件热更新 -获取已注册的插件版本号。 +#### `UpdateSDK.checkPluginUpdate(moduleId): Promise` -#### `UpdateSDK.getRegisteredPlugins(): PluginMeta[]` +检查指定插件是否有更新,返回版本信息(不执行更新)。 -获取所有已注册的插件列表。 +#### `UpdateSDK.updatePlugin(moduleId, options?): Promise` + +执行插件更新(一步完成)。SDK 内部自动:检查 → 下载 → 写文件 → 重载。 + +```ts +// 静默后台更新(下次启动生效) +await UpdateSDK.updatePlugin('buz1', { silent: true }) + +// 带进度的前台更新(立即重载) +const info = await UpdateSDK.checkPluginUpdate('buz1') +if (info.needsUpdate) { + const confirmed = info.forceUpdate || (await showConfirm(`发现 ${info.latestVersion},是否更新?`)) + if (confirmed) { + await UpdateSDK.updatePlugin('buz1', { onProgress: setProgress }) + } +} +``` + +### App 整包更新 #### `UpdateSDK.checkAppUpdate(bypassIgnore?): Promise` -检查 App 整包更新。自动检测平台(iOS/Android)并传给服务端。 +检查 App 整包更新。自动携带当前 versionCode 和 userId(定向更新)。 -- `bypassIgnore = false`(默认):静默检查,跳过用户已忽略的版本 -- `bypassIgnore = true`:用户主动检查,不跳过 +#### `UpdateSDK.downloadAndInstallApk(url, options?): Promise` + +Android 专用。下载 APK 并调起系统安装器。 ```ts -const info = await UpdateSDK.checkAppUpdate(true); -if (info.needsUpdate && info.forceUpdate) { - // 强制更新 -} +await UpdateSDK.downloadAndInstallApk(info.downloadUrl, { + onProgress: (p) => setProgress(p), + sha256: info.apkHash ?? undefined, +}) ``` #### `UpdateSDK.openStore(appStoreUrl?, marketUrl?): Promise` -打开应用商店。iOS 使用 `appStoreUrl`,Android 使用 `marketUrl`。 +跳转 iOS App Store 或 Android 应用商店。 -#### `UpdateSDK.checkPluginUpdate(moduleId): Promise` +### 已移除 API -检查指定插件的更新。插件必须已通过 `registerPlugin()` 注册。 - -```ts -const info = await UpdateSDK.checkPluginUpdate("buz1"); -if (info.needsUpdate) { - // 下载并缓存 -} -``` - -#### `UpdateSDK.downloadPluginBundle(downloadUrl): Promise` - -下载插件 bundle 源码文本。 - -#### `UpdateSDK.cachePluginBundle(moduleId, version, md5, source): Promise` - -缓存插件 bundle 到本地(AsyncStorage)。 - -#### `UpdateSDK.getCachedPluginBundle(moduleId): Promise` - -读取已缓存的插件 bundle。 - -#### `UpdateSDK.checkAndCachePlugin(moduleId): Promise` - -检查并下载插件更新(一步完成)。有更新时下载并缓存,无更新返回 `null`。调用方需在下次启动时通过 `reloadPlugin()` 加载新版本。 - -```ts -const cached = await UpdateSDK.checkAndCachePlugin("buz1"); -if (cached) { - // 下次启动生效 -} -``` - -#### `UpdateSDK.getAppVersionCode(): number` - -当前 App versionCode(原生读取)。 - -#### `UpdateSDK.getAppVersionName(): string` - -当前 App versionName(原生读取)。 - -#### `UpdateSDK._devSetAppVersion(versionCode, versionName?): void` - -开发环境手动设置版本号(仅调试用,生产环境不要调用)。 +| 方法 | 替代 | +|------|------| +| `UpdateSDK.checkAndCachePlugin(moduleId)` | `updatePlugin(moduleId, { silent: true })` | +| `UpdateSDK.downloadPluginBundle(url)` | 内部实现,不对外暴露 | +| `UpdateSDK.cachePluginBundle(...)` | 内部实现 | +| `UpdateSDK.getCachedPluginBundle(moduleId)` | 内部实现 | +| `PluginMeta.version`(注册时传 version)| SDK 自动从缓存读取 | --- -## 3. XWebView(xwebview) - -> `import { XWebViewScreen, XWebViewView, XWebViewConfig, ... } from '@xuqm/rn-xwebview'` - -WebView 容器组件,支持导航栏、文件下载、JS Bridge 等。 - -### 3.1 类型定义 - -```ts -type XWebViewConfig = { - showTopBar?: boolean; // 是否显示顶部导航栏 - showStatusBar?: boolean; // 是否显示状态栏 - doubleBackExit?: boolean; // 双击返回退出 - title?: string; // 自定义标题 - showTitle?: boolean; // 是否显示标题 - autoTitle?: boolean; // 自动从网页获取标题 - showMenu?: boolean; // 是否显示菜单按钮 - openForBrowser?: boolean; // 是否用外部浏览器打开 - clickMenu?: XWebViewClickMenu; // 自定义菜单 - url?: string; // 加载的 URL - content?: string; // 加载的 HTML 内容 - onMessage?: (event: XWebViewMessageEvent) => void; // 接收 Web 消息 - injectedJavaScript?: string; // 注入的 JS - onPermissionRequest?: (request: XWebViewPermissionRequest) => void; - autoDownload?: boolean; // 自动下载 - onDownloadStart?: (request: XWebViewDownloadRequest) => void; - onDownloadProgress?: (progress: XWebViewDownloadProgress) => void; - onDownloadComplete?: (result: XWebViewDownloadResult) => void; - onDownloadError?: (url: string, error: string) => void; - onDownloadDecide?: ( - request: XWebViewDownloadRequest, - ) => XWebViewDownloadDecision | Promise; - downloadConflict?: "rename" | "overwrite"; - onClose?: () => void; -}; - -type XWebViewControllerAPI = { - refresh: () => void; - close: () => void; - goBack: () => void; - goForward: () => void; - copyUrl: () => void; - postMessageToWeb: (jsString: string) => void; - getTitle: () => string; -}; - -type XWebViewClickMenu = { view?: React.ReactNode; onClick: () => void }; -type XWebViewMessageEvent = { nativeEvent: { data: string } }; -type XWebViewPermissionRequest = { - origin: string; - resources: string[]; - grant: (resources?: string[]) => void; - deny: () => void; -}; -type XWebViewDownloadRequest = { - url: string; - suggestedFilename: string; - mimeType?: string; - fileSize?: number; -}; -type XWebViewDownloadDecision = { - allowed: boolean; - filename?: string; - savePath?: string; -}; -type XWebViewDownloadProgress = { - url: string; - filename: string; - received: number; - total: number; - percentage: number; -}; -type XWebViewDownloadResult = { - url: string; - filename: string; - filePath: string; - fileSize: number; -}; -``` - -### 3.2 组件 - -#### `` - -全屏 WebView 页面组件,带顶部导航栏(返回、关闭、菜单)。作为独立屏幕使用,通过导航跳转进入。 - -#### `` - -嵌入式 WebView 组件,无顶部导航栏。适用于嵌入到其他页面中。 - -### 3.3 桥接函数 - -#### `openXWebView(navigate, config): void` - -打开 XWebView 页面。调用后会设置配置并执行导航。 - -```ts -import { openXWebView } from "@xuqm/rn-xwebview"; - -openXWebView(navigation.navigate, { - url: "https://example.com", - title: "详情", - showTopBar: true, -}); -``` - -#### `getXWebViewConfig(): XWebViewConfig` - -获取当前 XWebView 配置(由 `openXWebView` 设置)。 - -#### `setXWebViewController(controller: XWebViewControllerAPI | null): void` - -设置 XWebView 控制器实例(由组件内部调用)。 - -#### `XWebViewControl: XWebViewControllerAPI` - -全局 WebView 控制代理。可调用 `refresh()`、`close()`、`goBack()`、`goForward()`、`copyUrl()`、`postMessageToWeb(js)`、`getTitle()` 等方法控制当前 WebView。 - -```ts -import { XWebViewControl } from "@xuqm/rn-xwebview"; - -XWebViewControl.postMessageToWeb('window.dispatchEvent(new Event("refresh"))'); -XWebViewControl.close(); -``` - -### 3.4 子组件 - -#### `XWebViewProgress` - -WebView 加载进度条组件。 - ---- - -## 4. PushSDK(push) +## 3. PushSDK(push) > `import { PushSDK } from '@xuqm/rn-push'` -推送服务 SDK,支持华为、小米、OPPO、vivo、荣耀、FCM(Android)和 APNs(iOS)。 +**无需任何初始化**。`XuqmSDK.setUserInfo(info)` 被调用时,SDK 自动完成: +1. 检测设备厂商(华为/小米/OPPO/vivo/荣耀/FCM/APNs) +2. 从平台获取该厂商推送配置 +3. 调用厂商 SDK 完成设备注册 +4. 收到 token 后自动上报绑定到平台 -### 4.1 类型定义 +### API + +#### `PushSDK.setOfflinePushEnabled(enabled): Promise` + +设置是否接收离线推送。 + +#### `PushSDK.setQuietHours(start, end): Promise` + +设置免打扰时间段(24 小时制)。 ```ts -type PushVendor = - | "HUAWEI" - | "XIAOMI" - | "OPPO" - | "VIVO" - | "HONOR" - | "FCM" - | "APNS"; +await PushSDK.setQuietHours('22:00', '08:00') ``` -### 4.2 API 方法 +#### `PushSDK.clearQuietHours(): Promise` -#### `PushSDK.initialize(userId?): Promise` +清除免打扰设置。 -初始化推送服务。如果已有缓存的 token 且用户匹配,自动注册到服务端。 +#### `PushSDK.logout(): Promise` -```ts -await PushSDK.initialize("user-123"); -``` +主动解绑推送 token。通常不需要手动调用,`XuqmSDK.setUserInfo(null)` 会自动触发。 -#### `PushSDK.setPendingToken(token, vendor?): void` +### 已移除 API -缓存设备 token(不立即注册)。适用于原生层在用户登录前就收到 token 的场景。 - -```ts -PushSDK.setPendingToken(token, "HUAWEI"); -``` - -#### `PushSDK.getPendingToken(): { token: string; vendor?: PushVendor } | null` - -获取当前缓存的 pending token。 - -#### `PushSDK.setDeviceToken(token, vendor?): Promise` - -设置设备 token 并立即尝试注册到服务端。 - -#### `PushSDK.requestNativeRegistration(): Promise` - -触发原生推送注册。Android 尝试注册厂商 SDK,iOS 请求 APNs 注册。通过 `onPushToken()` 监听 token 回调。 - -#### `PushSDK.onPushToken(callback): () => void` - -监听原生层推送 token。返回取消监听函数。 - -```ts -const unsubscribe = PushSDK.onPushToken((token, vendor) => { - PushSDK.setDeviceToken(token, vendor as PushVendor); -}); -``` - -#### `PushSDK.registerToken(userId, token, vendor?): Promise` - -注册推送设备 token 到服务端。vendor 缺省时自动检测设备品牌。 - -```ts -await PushSDK.registerToken("user-123", deviceToken, "HUAWEI"); -``` - -#### `PushSDK.unregisterToken(userId): Promise` - -注销用户的推送 token。 - -#### `PushSDK.logout(userId?): Promise` - -登出推送服务(内部调用 `unregisterToken`)。userId 缺省时使用当前用户。 +| 方法 | 移除原因 | +|------|---------| +| `PushSDK.initialize(userId?)` | 由 `setUserInfo` 触发 | +| `PushSDK.requestNativeRegistration()` | 内部流程 | +| `PushSDK.registerToken(userId, token, vendor?)` | 内部流程 | +| `PushSDK.onPushToken(callback)` | 内部流程 | +| `PushSDK.unregisterToken(userId)` | 由 `setUserInfo(null)` 触发 | +| `PushSDK.setPendingToken / getPendingToken` | 内部状态 | --- -## 5. ImSDK(im) +## 4. ImSDK(im) > `import { ImSDK } from '@xuqm/rn-im'` -即时通讯 SDK,提供登录、消息收发、会话管理、群组、好友等功能。 +**无需初始化,通常不需要直接调用 `login()`**。`setUserInfo({ userId, userSig })` 自动触发 IM 登录(若租户开通 IM)。 -### 5.1 核心类型 +### 初始化相关 + +#### `ImSDK.login(userId, userSig): Promise` + +手动登录(保留,通常不需要直接调用)。 + +#### `ImSDK.refreshToken(userSig): Promise` + +刷新 IM userSig(过期时调用)。 ```ts -type ChatType = "SINGLE" | "GROUP"; -type MsgType = - | "TEXT" - | "IMAGE" - | "VIDEO" - | "AUDIO" - | "FILE" - | "CUSTOM" - | "LOCATION" - | "NOTIFY" - | "RICH_TEXT" - | "CALL_AUDIO" - | "CALL_VIDEO" - | "QUOTE" - | "MERGE" - | "REVOKED" - | "FORWARD"; -type MsgStatus = - | "SENDING" - | "SENT" - | "DELIVERED" - | "READ" - | "FAILED" - | "REVOKED"; - -interface ImMessage { - id: string; - appKey: string; - fromUserId: string; - fromId?: string; - toId: string; - chatType: ChatType; - msgType: MsgType; - content: string; - status: MsgStatus; - mentionedUserIds?: string; - groupReadCount?: number; - revoked?: boolean; - createdAt: number; - editedAt?: number | null; -} - -interface ImGroup { - id: string; - appKey: string; - name: string; - groupType?: string; - creatorId: string; - memberIds: string; - adminIds: string; - announcement?: string | null; - memberInfo?: string | null; - extAttributes?: string | null; - createdAt: number; -} - -interface ConversationData { - targetId: string; - chatType: ChatType; - lastMsgContent?: string | null; - lastMsgType?: string | null; - lastMsgTime: number; - unreadCount: number; - isMuted: boolean; - isPinned: boolean; - conversationGroup?: string | null; -} - -interface UserProfile { - id?: string; - appKey?: string; - userId: string; - nickname?: string | null; - avatar?: string | null; - gender?: string | null; - status?: string | null; - createdAt?: number | null; -} - -interface FriendRequest { - id: string; - appKey: string; - fromUserId: string; - toUserId: string; - remark?: string | null; - status: "PENDING" | "ACCEPTED" | "REJECTED"; - createdAt: number; - reviewedAt?: number | null; -} - -interface GroupJoinRequest { - id: string; - appKey: string; - groupId: string; - requesterId: string; - remark?: string | null; - status: "PENDING" | "ACCEPTED" | "REJECTED"; - createdAt: number; - reviewedAt?: number | null; -} - -interface ImEventListener { - onConnected?: () => void; - onDisconnected?: (reason?: string) => void; - onMessage?: (msg: ImMessage) => void; - onGroupMessage?: (msg: ImMessage) => void; - onSystemMessage?: (msg: ImMessage) => void; - onRead?: (msg: ImMessage) => void; - onRevoke?: (data: { msgId: string; operatorId: string }) => void; - onError?: (error: string) => void; -} +const newSig = await api.refreshUserSig() +await ImSDK.refreshToken(newSig) ``` -### 5.2 连接与认证 +#### `ImSDK.disconnect(): void` -| 方法 | 签名 | 说明 | -| ------------- | ---------------------------------------------------- | --------------------------------- | -| `login` | `(userId: string, userSig: string) => Promise` | 登录 IM 服务,建立 WebSocket 连接 | -| `reconnect` | `() => Promise` | 重新连接(使用已保存的 token) | -| `disconnect` | `() => void` | 断开连接并清理状态 | -| `isConnected` | `() => boolean` | 是否已连接 | +断开 WebSocket 连接(通常不需要直接调用,`setUserInfo(null)` 会自动触发)。 -### 5.3 消息发送 +### 消息 API -| 方法 | 签名 | 说明 | -| ---------------------- | ------------------------------------------------------------------ | ------------ | -| `sendMessage` | `(params: SendMessageParams) => Promise` | 通用发送 | -| `sendTextMessage` | `(toId, chatType, text) => Promise` | 文本消息 | -| `sendImageMessage` | `(toId, chatType, imageUri) => Promise` | 图片消息 | -| `sendVideoMessage` | `(toId, chatType, videoUri) => Promise` | 视频消息 | -| `sendAudioMessage` | `(toId, chatType, audioUri) => Promise` | 语音消息 | -| `sendFileMessage` | `(toId, chatType, fileUri) => Promise` | 文件消息 | -| `sendNotifyMessage` | `(toId, chatType, content) => Promise` | 通知消息 | -| `sendQuoteMessage` | `(toId, chatType, content, quotedMsgId) => Promise` | 引用消息 | -| `sendMergeMessage` | `(toId, chatType, title, summary, messages) => Promise` | 合并转发 | -| `sendCallAudioMessage` | `(toId, chatType, content) => Promise` | 语音通话消息 | -| `sendCallVideoMessage` | `(toId, chatType, content) => Promise` | 视频通话消息 | -| `sendCustomMessage` | `(toId, chatType, content) => Promise` | 自定义消息 | -| `sendLocationMessage` | `(toId, chatType, ...) => Promise` | 位置消息 | -| `sendRichTextMessage` | `(toId, chatType, content) => Promise` | 富文本消息 | -| `sendForwardMessage` | `(toId, chatType, messageId) => Promise` | 转发消息 | +```ts +await ImSDK.sendTextMessage(toId, chatType, content, mentionedUserIds?) +await ImSDK.sendImageMessage(toId, chatType, localUri, width?, height?) +await ImSDK.sendVideoMessage(toId, chatType, localUri, thumbnailUri?, duration?) +await ImSDK.sendAudioMessage(toId, chatType, localUri, duration) +await ImSDK.sendFileMessage(toId, chatType, localUri, filename, size) +await ImSDK.revokeMessage(messageId) +await ImSDK.editMessage(messageId, content) +``` -### 5.4 消息操作 +### 会话 API -| 方法 | 签名 | 说明 | -| ------------------------------ | --------------------------------------------------------------- | --------------------- | -| `revokeMessage` | `(messageId: string) => Promise` | 撤回消息 | -| `editMessage` | `(messageId: string, content: string) => Promise` | 编辑消息 | -| `fetchHistory` | `(toId, page?, size?) => Promise` | 获取单聊历史消息 | -| `fetchHistoryWithFilters` | `(toId, params) => Promise>` | 带筛选的历史消息 | -| `fetchGroupHistory` | `(groupId, page?, size?) => Promise` | 获取群聊历史消息 | -| `fetchGroupHistoryWithFilters` | `(groupId, params) => Promise>` | 带筛选的群聊历史 | -| `locateHistoryPage` | `(toId, messageId, size?) => Promise>` | 定位到指定消息所在页 | -| `locateGroupHistoryPage` | `(groupId, messageId, size?) => Promise>` | 群聊定位到指定消息 | -| `syncOfflineMessages` | `(maxCount?) => Promise` | 同步离线消息 | -| `offlineMessageCount` | `() => Promise` | 离线消息数量 | -| `searchMessages` | `(params) => Promise<...>` | 搜索本地消息(需 DB) | +```ts +await ImSDK.listConversations() +ImSDK.subscribeConversations(callback) // 返回 unsubscribe 函数 +await ImSDK.markRead(targetId, chatType) +await ImSDK.deleteConversation(targetId, chatType) +await ImSDK.getTotalUnreadCount() +``` -### 5.5 会话管理 +### 历史消息 API -| 方法 | 签名 | 说明 | -| -------------------------------- | --------------------------------------------------- | ---------------------- | -| `listConversations` | `() => Promise` | 获取会话列表 | -| `subscribeConversations` | `(callback) => () => void` | 订阅会话列表变化 | -| `markRead` | `(targetId, chatType?) => Promise` | 标记已读 | -| `setConversationMuted` | `(targetId, chatType, muted) => Promise` | 设置免打扰 | -| `setConversationPinned` | `(targetId, chatType, pinned) => Promise` | 设置置顶 | -| `setDraft` | `(targetId, chatType, draft) => Promise` | 保存草稿 | -| `getDraft` | `(targetId, chatType) => Promise` | 获取草稿 | -| `setConversationHidden` | `(targetId, chatType, hidden) => Promise` | 隐藏会话 | -| `setConversationGroup` | `(targetId, chatType, groupName?) => Promise` | 设置会话分组 | -| `listConversationGroups` | `() => Promise` | 获取会话分组列表 | -| `listConversationGroupItems` | `(groupName) => Promise` | 获取分组下的会话 | -| `deleteConversation` | `(targetId, chatType) => Promise` | 删除会话 | -| `getTotalUnreadCount` | `() => Promise` | 获取总未读数 | -| `syncHistoryForAllConversations` | `() => Promise` | 同步所有会话的历史消息 | +```ts +await ImSDK.fetchHistory(toId, page, size) +await ImSDK.fetchGroupHistory(groupId, page, size) +await ImSDK.searchMessages({ keyword, msgType, startTime, endTime }) +``` -### 5.6 群组管理 +### 群组 API -| 方法 | 签名 | 说明 | -| ------------------------- | ------------------------------------------------------------- | -------------------- | -| `createGroup` | `(name, memberIds, groupType?) => Promise` | 创建群组 | -| `listGroups` | `() => Promise` | 获取已加入的群组列表 | -| `listPublicGroups` | `(keyword?) => Promise` | 搜索公开群组 | -| `getGroupInfo` | `(groupId) => Promise` | 获取群组详情 | -| `listGroupMembers` | `(groupId) => Promise` | 获取群成员列表 | -| `searchGroupMembers` | `(groupId, keyword, size?) => Promise` | 搜索群成员 | -| `updateGroupInfo` | `(groupId, name?, announcement?) => Promise` | 更新群信息 | -| `addGroupMember` | `(groupId, userId) => Promise` | 添加群成员 | -| `removeGroupMember` | `(groupId, targetUserId) => Promise` | 移除群成员 | -| `batchAddGroupMembers` | `(groupId, userIds) => Promise` | 批量添加群成员 | -| `batchRemoveGroupMembers` | `(groupId, userIds) => Promise` | 批量移除群成员 | -| `leaveGroup` | `(groupId) => Promise` | 退出群组 | -| `setGroupRole` | `(groupId, userId, role) => Promise` | 设置群成员角色 | -| `muteGroupMember` | `(groupId, userId, minutes) => Promise` | 禁言群成员 | -| `transferGroupOwner` | `(groupId, newOwnerId) => Promise` | 转让群主 | -| `updateGroupAttributes` | `(groupId, attributes) => Promise` | 更新群属性 | -| `removeGroupAttributes` | `(groupId, keys) => Promise` | 删除群属性 | -| `dismissGroup` | `(groupId) => Promise` | 解散群组 | -| `modifyGroupMemberInfo` | `(groupId, userId, nickname?, role?) => Promise` | 修改群成员信息 | -| `adminGroupReadReceipts` | `(groupId, messageIds) => Promise` | 群消息已读回执 | +```ts +await ImSDK.createGroup(name, memberIds, groupType) +await ImSDK.listGroups() +await ImSDK.addGroupMember(groupId, userId) +await ImSDK.removeGroupMember(groupId, userId) +await ImSDK.setGroupRole(groupId, userId, role) +``` -### 5.7 群组加入请求 +### 关系链 API -| 方法 | 签名 | 说明 | -| ------------------------------ | --------------------------------------------------- | ---------------- | -| `sendGroupJoinRequest` | `(groupId, remark?) => Promise` | 发送入群申请 | -| `listGroupJoinRequests` | `(groupId) => Promise` | 获取入群申请列表 | -| `acceptGroupJoinRequest` | `(groupId, requestId) => Promise` | 同意入群申请 | -| `rejectGroupJoinRequest` | `(groupId, requestId) => Promise` | 拒绝入群申请 | -| `batchAcceptGroupJoinRequests` | `(groupId, requestIds) => Promise` | 批量同意 | -| `batchRejectGroupJoinRequests` | `(groupId, requestIds) => Promise` | 批量拒绝 | +```ts +await ImSDK.listFriends() +await ImSDK.addFriend(friendId) +await ImSDK.removeFriend(friendId) +await ImSDK.sendFriendRequest(toUserId, remark?) +await ImSDK.acceptFriendRequest(requestId) +await ImSDK.addToBlacklist(blockedUserId) +await ImSDK.checkBlacklist(targetUserId) +``` -### 5.8 好友管理 +### 事件监听 -| 方法 | 签名 | 说明 | -| --------------------------- | ----------------------------------------------- | ---------------- | -| `listFriends` | `() => Promise` | 获取好友列表 | -| `addFriend` | `(friendId) => Promise` | 添加好友 | -| `removeFriend` | `(friendId) => Promise` | 删除好友 | -| `removeAllFriends` | `() => Promise` | 删除所有好友 | -| `batchAddFriends` | `(friendIds) => Promise` | 批量添加好友 | -| `batchRemoveFriends` | `(friendIds) => Promise` | 批量删除好友 | -| `setFriendGroup` | `(friendId, groupName?) => Promise` | 设置好友分组 | -| `listFriendGroups` | `() => Promise` | 获取好友分组列表 | -| `listFriendsByGroup` | `(groupName) => Promise` | 按分组获取好友 | -| `listFriendRequests` | `(direction?) => Promise` | 获取好友请求列表 | -| `sendFriendRequest` | `(toUserId, remark?) => Promise` | 发送好友请求 | -| `acceptFriendRequest` | `(requestId) => Promise` | 同意好友请求 | -| `rejectFriendRequest` | `(requestId) => Promise` | 拒绝好友请求 | -| `batchAcceptFriendRequests` | `(requestIds) => Promise` | 批量同意 | -| `batchRejectFriendRequests` | `(requestIds) => Promise` | 批量拒绝 | - -### 5.9 黑名单 - -| 方法 | 签名 | 说明 | -| --------------------- | ------------------------------------------------- | -------------- | -| `listBlacklist` | `() => Promise` | 获取黑名单 | -| `addToBlacklist` | `(blockedUserId) => Promise` | 加入黑名单 | -| `removeFromBlacklist` | `(blockedUserId) => Promise` | 移出黑名单 | -| `checkBlacklist` | `(targetUserId) => Promise` | 检查黑名单状态 | - -### 5.10 用户资料 - -| 方法 | 签名 | 说明 | -| --------------- | --------------------------------------------------------------- | ------------ | -| `getProfile` | `(userId) => Promise` | 获取用户资料 | -| `updateProfile` | `(userId, nickname?, avatar?, gender?) => Promise` | 更新用户资料 | -| `searchUsers` | `(keyword, size?) => Promise` | 搜索用户 | - -### 5.11 事件监听 - -| 方法 | 签名 | 说明 | -| ------------------ | ------------------------------------- | ---------------- | -| `addListener` | `(listener: ImEventListener) => void` | 添加事件监听器 | -| `removeListener` | `(listener: ImEventListener) => void` | 移除事件监听器 | -| `subscribeGroup` | `(groupId) => void` | 订阅群组消息 | -| `unsubscribeGroup` | `(groupId) => void` | 取消订阅群组消息 | +```ts +ImSDK.addListener({ + onConnected: () => {}, + onDisconnected: () => {}, + onMessage: (msg) => {}, + onGroupMessage: (msg) => {}, + onRevoke: ({ msgId }) => {}, +}) +ImSDK.removeListener(listener) +``` --- -## 快速参考 +## 5. LicenseSDK(license) -### 初始化顺序 +> `import { checkLicense, getStatus } from '@xuqm/rn-license'` + +**无需独立初始化**。内部自动调用 `awaitInitialization()` 等待 `XuqmSDK` 就绪,使用公共 `appKey` 和 `licenseUrl`。 + +### API + +#### `checkLicense(userInfo?): Promise` + +验证设备 License。 ```ts -// 1. 初始化核心 SDK -await XuqmSDK.initialize({ appKey: "xxx" }); -// 或从加密文件初始化 -await XuqmSDK.initWithConfigFile(encryptedConfig); - -// 2. 登录后设置用户信息 -XuqmSDK.setUserInfo({ userId, name, phone }); - -// 3. 初始化推送 -PushSDK.onPushToken((token, vendor) => { - PushSDK.setDeviceToken(token, vendor); -}); -await PushSDK.initialize(userId); - -// 4. 登录 IM -await ImSDK.login(userId, userSig); - -// 5. 检查更新 -const appUpdate = await UpdateSDK.checkAppUpdate(); -const pluginUpdate = await UpdateSDK.checkAndCachePlugin("buz1"); +const result = await checkLicense() +if (result.type === 'success') { + console.log('License 验证通过:', result.reason) +} else { + console.error('License 验证失败:', result.message) +} ``` -### 包依赖关系 +#### `getStatus(): Promise` + +获取当前 License 状态(`'ok' | 'denied' | 'unknown'`)。 + +#### `clear(): Promise` + +清除本地 License 缓存(调试用)。 + +### 已移除 API + +| 方法 | 移除原因 | +|------|---------| +| `initialize(appKey, options)` | 依赖 XuqmSDK,无需独立初始化 | +| `initializeFromFile(encrypted)` | License 文件不承担 SDK 初始化职责 | + +--- + +## 内部机制说明 + +### 用户信息分发(_registerUserInfoHandler) + +`XuqmSDK.setUserInfo()` 内部维护一个订阅者列表。各子 SDK 在模块加载时注册处理器: ``` -@xuqm/rn-common ← XuqmSDK, 配置, HTTP, 工具函数(其他包的底层依赖) -@xuqm/rn-im ← ImSDK(依赖 common) -@xuqm/rn-push ← PushSDK(依赖 common) -@xuqm/rn-update ← UpdateSDK(依赖 common) -@xuqm/rn-xwebview ← XWebView 组件(依赖 common) +setUserInfo(info) + ├── PushSDK:检测厂商 → 获取厂商配置 → 注册 token + ├── ImSDK:若 userSig 存在且 imEnabled,自动登录 + ├── UpdateSDK:更新 userId + └── LicenseSDK:更新用户上下文 ``` + +该机制为内部实现,业务侧只需调用 `setUserInfo`,无需关心具体分发逻辑。 + +### 配置文件解密 + +`.xuqmconfig` 文件格式:`XUQM-CONFIG-V1.{salt}.{iv}.{ciphertext}`(Base64URL 编码) + +SDK 使用 `react-native-quick-crypto` 的 SubtleCrypto 进行 PBKDF2 派生密钥 + AES-GCM 解密,详见 `packages/common/src/configCrypto.ts`。 + +解密后包含:`{ appKey, platformUrl/serverUrl }`,SDK 自动调用 `initialize()` 完成远程配置拉取。