完整覆盖:XuqmSDK 初始化两种方式(含配置文件解密流程)、setUserInfo 认证枢纽; PushSDK 只暴露 setOfflinePushEnabled/setQuietHours/clearQuietHours/logout; UpdateSDK registerPlugins/updatePlugin/downloadAndInstallApk/setBundleCallbacks; ImSDK refreshToken 和订阅机制;LicenseSDK 无需初始化。 标注所有已移除 API 和替代方案。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
432 行
12 KiB
Markdown
432 行
12 KiB
Markdown
# XuqmGroup RN SDK API 参考
|
||
|
||
> 版本: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`
|
||
|
||
---
|
||
|
||
## 1. XuqmSDK(common)
|
||
|
||
> `import { XuqmSDK } from '@xuqm/rn-common'`
|
||
|
||
SDK 核心模块。负责从平台拉取服务配置,并作为所有子 SDK 用户状态的统一分发中心。
|
||
|
||
### 类型定义
|
||
|
||
```ts
|
||
interface XuqmInitOptions {
|
||
appKey: string // 应用标识(从租户平台获取)
|
||
platformUrl?: string // 平台地址;不传则使用内置默认公有平台地址
|
||
debug?: boolean
|
||
}
|
||
|
||
interface XuqmConfig {
|
||
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 // 必填
|
||
userSig?: string // IM 服务必填;未开通 IM 时可不传
|
||
name?: string
|
||
phone?: string
|
||
avatar?: string
|
||
}
|
||
```
|
||
|
||
### 初始化 API
|
||
|
||
#### `XuqmSDK.initialize(options): Promise<void>`
|
||
|
||
手动初始化(方式 B)。请求平台获取 appKey 专属服务配置(各服务 URL 和开通状态)。
|
||
|
||
- `platformUrl` 未传时使用内置默认公有平台地址
|
||
- **失败时直接抛出错误,不降级**,调用方必须处理异常
|
||
|
||
```ts
|
||
// 公有平台
|
||
await XuqmSDK.initialize({ appKey: 'your-app-key', debug: __DEV__ })
|
||
|
||
// 私有化部署
|
||
await XuqmSDK.initialize({
|
||
appKey: 'your-app-key',
|
||
platformUrl: 'https://your-server.com',
|
||
})
|
||
```
|
||
|
||
> 方式 A(配置文件自动初始化):将加密 `.xuqmconfig` 文件放置到 `src/assets/xuqm/config.xuqmconfig`,SDK 自动读取、解密(AES-GCM)并调用 `initialize()`,App 无需任何初始化代码。
|
||
|
||
#### `XuqmSDK.awaitInitialization(): Promise<void>`
|
||
|
||
等待初始化完成。子 SDK 内部调用,确保配置就绪后再使用。
|
||
|
||
### 用户认证 API
|
||
|
||
#### `XuqmSDK.setUserInfo(info): void`
|
||
|
||
用户认证核心枢纽。**登录后调用一次,所有子 SDK 自动同步**;传 `null` 触发全局登出。
|
||
|
||
```ts
|
||
// 登录
|
||
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
|
||
|
||
| 方法 | 移除原因 |
|
||
|------|---------|
|
||
| `XuqmSDK.init(options): void` | 同步 init 不拉取服务配置,行为不完整 |
|
||
| `XuqmSDK.initializeFromLicense(file)` | License 不承担 SDK 初始化职责 |
|
||
| `XuqmSDK.initWithConfigFile(encrypted)` | 由自动初始化机制内部调用,不对外暴露 |
|
||
|
||
---
|
||
|
||
## 2. UpdateSDK(update)
|
||
|
||
> `import { UpdateSDK } from '@xuqm/rn-update'`
|
||
|
||
### 类型定义
|
||
|
||
```ts
|
||
interface PluginRegistration {
|
||
moduleId: string // 版本号由 SDK 自动从缓存读取,不需要传入
|
||
}
|
||
|
||
interface AppUpdateInfo {
|
||
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
|
||
currentVersion: string // SDK 自动填入当前版本
|
||
downloadUrl: string
|
||
md5: string
|
||
minCommonVersion: string
|
||
note: string
|
||
forceUpdate?: boolean
|
||
}
|
||
```
|
||
|
||
### 插件注册
|
||
|
||
#### `UpdateSDK.registerPlugins(plugins): void`
|
||
|
||
批量注册插件(推荐)。版本号由 SDK 自动从本地缓存读取(首次为 `0.0.0`)。
|
||
|
||
```ts
|
||
UpdateSDK.registerPlugins([
|
||
{ moduleId: 'buz1' },
|
||
{ moduleId: 'buz2' },
|
||
{ moduleId: 'buz3' },
|
||
])
|
||
```
|
||
|
||
#### `UpdateSDK.registerPlugin(meta): void`
|
||
|
||
单个注册(向后兼容)。`version` 字段已废弃,传入无效。
|
||
|
||
#### `UpdateSDK.setBundleCallbacks(callbacks): void`
|
||
|
||
注入宿主 BundleRuntime 的写入和重载能力。宿主在初始化时调用一次。
|
||
|
||
```ts
|
||
import { writeBundleFile, loadBundle } from '@native/BundleRuntime'
|
||
|
||
UpdateSDK.setBundleCallbacks({
|
||
writeBundle: writeBundleFile,
|
||
reloadBundle: loadBundle,
|
||
})
|
||
```
|
||
|
||
### 插件热更新
|
||
|
||
#### `UpdateSDK.checkPluginUpdate(moduleId): Promise<PluginUpdateInfo>`
|
||
|
||
检查指定插件是否有更新,返回版本信息(不执行更新)。
|
||
|
||
#### `UpdateSDK.updatePlugin(moduleId, options?): Promise<void>`
|
||
|
||
执行插件更新(一步完成)。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<AppUpdateInfo>`
|
||
|
||
检查 App 整包更新。自动携带当前 versionCode 和 userId(定向更新)。
|
||
|
||
#### `UpdateSDK.downloadAndInstallApk(url, options?): Promise<void>`
|
||
|
||
Android 专用。下载 APK 并调起系统安装器。
|
||
|
||
```ts
|
||
await UpdateSDK.downloadAndInstallApk(info.downloadUrl, {
|
||
onProgress: (p) => setProgress(p),
|
||
sha256: info.apkHash ?? undefined,
|
||
})
|
||
```
|
||
|
||
#### `UpdateSDK.openStore(appStoreUrl?, marketUrl?): Promise<void>`
|
||
|
||
跳转 iOS App Store 或 Android 应用商店。
|
||
|
||
### 已移除 API
|
||
|
||
| 方法 | 替代 |
|
||
|------|------|
|
||
| `UpdateSDK.checkAndCachePlugin(moduleId)` | `updatePlugin(moduleId, { silent: true })` |
|
||
| `UpdateSDK.downloadPluginBundle(url)` | 内部实现,不对外暴露 |
|
||
| `UpdateSDK.cachePluginBundle(...)` | 内部实现 |
|
||
| `UpdateSDK.getCachedPluginBundle(moduleId)` | 内部实现 |
|
||
| `PluginMeta.version`(注册时传 version)| SDK 自动从缓存读取 |
|
||
|
||
---
|
||
|
||
## 3. PushSDK(push)
|
||
|
||
> `import { PushSDK } from '@xuqm/rn-push'`
|
||
|
||
**无需任何初始化**。`XuqmSDK.setUserInfo(info)` 被调用时,SDK 自动完成:
|
||
1. 检测设备厂商(华为/小米/OPPO/vivo/荣耀/FCM/APNs)
|
||
2. 从平台获取该厂商推送配置
|
||
3. 调用厂商 SDK 完成设备注册
|
||
4. 收到 token 后自动上报绑定到平台
|
||
|
||
### API
|
||
|
||
#### `PushSDK.setOfflinePushEnabled(enabled): Promise<void>`
|
||
|
||
设置是否接收离线推送。
|
||
|
||
#### `PushSDK.setQuietHours(start, end): Promise<void>`
|
||
|
||
设置免打扰时间段(24 小时制)。
|
||
|
||
```ts
|
||
await PushSDK.setQuietHours('22:00', '08:00')
|
||
```
|
||
|
||
#### `PushSDK.clearQuietHours(): Promise<void>`
|
||
|
||
清除免打扰设置。
|
||
|
||
#### `PushSDK.logout(): Promise<void>`
|
||
|
||
主动解绑推送 token。通常不需要手动调用,`XuqmSDK.setUserInfo(null)` 会自动触发。
|
||
|
||
### 已移除 API
|
||
|
||
| 方法 | 移除原因 |
|
||
|------|---------|
|
||
| `PushSDK.initialize(userId?)` | 由 `setUserInfo` 触发 |
|
||
| `PushSDK.requestNativeRegistration()` | 内部流程 |
|
||
| `PushSDK.registerToken(userId, token, vendor?)` | 内部流程 |
|
||
| `PushSDK.onPushToken(callback)` | 内部流程 |
|
||
| `PushSDK.unregisterToken(userId)` | 由 `setUserInfo(null)` 触发 |
|
||
| `PushSDK.setPendingToken / getPendingToken` | 内部状态 |
|
||
|
||
---
|
||
|
||
## 4. ImSDK(im)
|
||
|
||
> `import { ImSDK } from '@xuqm/rn-im'`
|
||
|
||
**无需初始化,通常不需要直接调用 `login()`**。`setUserInfo({ userId, userSig })` 自动触发 IM 登录(若租户开通 IM)。
|
||
|
||
### 初始化相关
|
||
|
||
#### `ImSDK.login(userId, userSig): Promise<void>`
|
||
|
||
手动登录(保留,通常不需要直接调用)。
|
||
|
||
#### `ImSDK.refreshToken(userSig): Promise<void>`
|
||
|
||
刷新 IM userSig(过期时调用)。
|
||
|
||
```ts
|
||
const newSig = await api.refreshUserSig()
|
||
await ImSDK.refreshToken(newSig)
|
||
```
|
||
|
||
#### `ImSDK.disconnect(): void`
|
||
|
||
断开 WebSocket 连接(通常不需要直接调用,`setUserInfo(null)` 会自动触发)。
|
||
|
||
### 消息 API
|
||
|
||
```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)
|
||
```
|
||
|
||
### 会话 API
|
||
|
||
```ts
|
||
await ImSDK.listConversations()
|
||
ImSDK.subscribeConversations(callback) // 返回 unsubscribe 函数
|
||
await ImSDK.markRead(targetId, chatType)
|
||
await ImSDK.deleteConversation(targetId, chatType)
|
||
await ImSDK.getTotalUnreadCount()
|
||
```
|
||
|
||
### 历史消息 API
|
||
|
||
```ts
|
||
await ImSDK.fetchHistory(toId, page, size)
|
||
await ImSDK.fetchGroupHistory(groupId, page, size)
|
||
await ImSDK.searchMessages({ keyword, msgType, startTime, endTime })
|
||
```
|
||
|
||
### 群组 API
|
||
|
||
```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)
|
||
```
|
||
|
||
### 关系链 API
|
||
|
||
```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)
|
||
```
|
||
|
||
### 事件监听
|
||
|
||
```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<LicenseResult>`
|
||
|
||
验证设备 License。
|
||
|
||
```ts
|
||
const result = await checkLicense()
|
||
if (result.type === 'success') {
|
||
console.log('License 验证通过:', result.reason)
|
||
} else {
|
||
console.error('License 验证失败:', result.message)
|
||
}
|
||
```
|
||
|
||
#### `getStatus(): Promise<LicenseStatus>`
|
||
|
||
获取当前 License 状态(`'ok' | 'denied' | 'unknown'`)。
|
||
|
||
#### `clear(): Promise<void>`
|
||
|
||
清除本地 License 缓存(调试用)。
|
||
|
||
### 已移除 API
|
||
|
||
| 方法 | 移除原因 |
|
||
|------|---------|
|
||
| `initialize(appKey, options)` | 依赖 XuqmSDK,无需独立初始化 |
|
||
| `initializeFromFile(encrypted)` | License 文件不承担 SDK 初始化职责 |
|
||
|
||
---
|
||
|
||
## 内部机制说明
|
||
|
||
### 用户信息分发(_registerUserInfoHandler)
|
||
|
||
`XuqmSDK.setUserInfo()` 内部维护一个订阅者列表。各子 SDK 在模块加载时注册处理器:
|
||
|
||
```
|
||
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()` 完成远程配置拉取。
|