XuqmGroup-RNSDK/src/update/updateSDK.ts
XuqmGroup 67d54bf1f2 docs(sdk): 添加 Android SDK 文档和 API 设计规范
- 新增 Android SDK 使用文档,包含模块结构、集成方式和快速开始指南
- 添加 SDK API 重设计规范,统一初始化和登录接口设计
- 补充安全设计规范,完善 UserSig 鉴权和敏感数据处理方案
- 创建平台 REST API 规范,定义服务端到服务端的调用接口
- 添加离线推送架构设计,集成各大厂商推送服务与 IM 联动方案
2026-04-29 15:46:40 +08:00

131 行
3.6 KiB
TypeScript

import AsyncStorage from '@react-native-async-storage/async-storage'
import { Linking, Platform } from 'react-native'
import { getConfig } from '../core/config'
import { apiRequest } from '../core/http'
export interface AppUpdateInfo {
needsUpdate: boolean
versionName?: string
versionCode?: number
downloadUrl?: string
changeLog?: string
forceUpdate?: boolean
appStoreUrl?: string
marketUrl?: string
}
export interface RnUpdateInfo {
needsUpdate: boolean
latestVersion: string
downloadUrl: string
md5: string
minCommonVersion: string
note: string
packageName?: string
packageMatched?: boolean
}
export interface CachedRnBundle {
moduleId: string
version: string
md5: string
downloadedAt: string
source: string
}
function getBundleCacheKey(moduleId: string) {
return `@xuqm_sdk_rn_bundle:${moduleId}`
}
function normalizeDownloadUrl(rawUrl?: string) {
if (!rawUrl) return rawUrl
// Compatible with legacy server config where update base URL already includes /api/v1/updates.
if (rawUrl.includes('/api/v1/updates/api/v1/rn/files/')) {
return rawUrl.replace('/api/v1/updates/api/v1/rn/files/', '/api/v1/rn/files/')
}
if (rawUrl.includes('/files/apk/')) {
try {
const url = new URL(rawUrl)
if (url.pathname.startsWith('/files/apk/')) {
return `${url.origin}/api/v1/updates${url.pathname}${url.search}`
}
} catch {
return rawUrl
}
}
return rawUrl
}
export const UpdateSDK = {
async checkAppUpdate(currentVersionCode: number): Promise<AppUpdateInfo> {
const config = getConfig()
const result = await apiRequest<AppUpdateInfo>('/api/v1/updates/app/check', {
params: {
appId: config.appKey,
platform: Platform.OS === 'android' ? 'ANDROID' : 'IOS',
currentVersionCode: String(currentVersionCode),
},
})
return {
...result,
downloadUrl: normalizeDownloadUrl(result.downloadUrl),
}
},
async openAppStore(appStoreUrl?: string, marketUrl?: string): Promise<void> {
const url = Platform.OS === 'ios' ? appStoreUrl : marketUrl
if (url) await Linking.openURL(url)
},
async checkRnUpdate(moduleId: string, currentVersion: string, packageName?: string): Promise<RnUpdateInfo> {
const config = getConfig()
const result = await apiRequest<RnUpdateInfo>('/api/v1/rn/update/check', {
params: {
appId: config.appKey,
moduleId,
platform: Platform.OS === 'android' ? 'ANDROID' : 'IOS',
currentVersion,
...(packageName ? { packageName } : {}),
},
})
if (packageName && result.packageMatched === false) {
return {
...result,
needsUpdate: false,
}
}
return {
...result,
downloadUrl: normalizeDownloadUrl(result.downloadUrl) ?? result.downloadUrl,
}
},
async downloadRnBundle(downloadUrl: string): Promise<string> {
const response = await fetch(downloadUrl)
if (!response.ok) {
throw new Error(`Failed to download bundle: ${response.status}`)
}
return response.text()
},
async cacheRnBundle(moduleId: string, version: string, md5: string, source: string): Promise<CachedRnBundle> {
const payload: CachedRnBundle = {
moduleId,
version,
md5,
source,
downloadedAt: new Date().toISOString(),
}
await AsyncStorage.setItem(getBundleCacheKey(moduleId), JSON.stringify(payload))
return payload
},
async getCachedRnBundle(moduleId: string): Promise<CachedRnBundle | null> {
const raw = await AsyncStorage.getItem(getBundleCacheKey(moduleId))
return raw ? JSON.parse(raw) as CachedRnBundle : null
},
}