- 新增 Android SDK 使用文档,包含模块结构、集成方式和快速开始指南 - 添加 SDK API 重设计规范,统一初始化和登录接口设计 - 补充安全设计规范,完善 UserSig 鉴权和敏感数据处理方案 - 创建平台 REST API 规范,定义服务端到服务端的调用接口 - 添加离线推送架构设计,集成各大厂商推送服务与 IM 联动方案
131 行
3.6 KiB
TypeScript
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
|
|
},
|
|
}
|