feat(sdk): 添加鸿蒙SDK核心功能模块
- 实现SDKContext用于配置管理和数据持久化存储 - 定义完整的类型系统包括消息、用户、群组等接口 - 集成更新SDK支持原生应用和RN热更新检查 - 提供统一的XuqmSDK入口类和模块导出 - 编写详细的开发文档和使用示例
这个提交包含在:
父节点
215859f03f
当前提交
3d1873b6a3
12
README.md
12
README.md
@ -207,19 +207,25 @@ Harmony 版本不提供本地安装包下载,更新只跳转应用市场。
|
|||||||
### 检查 RN Bundle 热更新
|
### 检查 RN Bundle 热更新
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const rnResult = await XuqmSDK.update.checkRnUpdate('ak_xxx', 'main', 100)
|
const rnResult = await XuqmSDK.update.checkRnUpdate('ak_xxx', 'main')
|
||||||
|
|
||||||
if (rnResult.hasUpdate && rnResult.info) {
|
if (rnResult.hasUpdate && rnResult.info) {
|
||||||
const filePath = await XuqmSDK.update.downloadRnBundle(
|
const filePath = await XuqmSDK.update.downloadRnBundle(
|
||||||
context,
|
context,
|
||||||
rnResult.info.downloadUrl,
|
rnResult.info.downloadUrl,
|
||||||
'main.harmony.bundle'
|
'main.harmony.zip',
|
||||||
|
'main',
|
||||||
|
rnResult.info
|
||||||
)
|
)
|
||||||
// 文件下载至 context.cacheDir/main.harmony.bundle
|
// 文件下载至 context.cacheDir/main.harmony.zip
|
||||||
|
// 如需在本地缓存当前 RN 版本,downloadRnBundle 会顺手写入 bundleVersion
|
||||||
// 通知 RN 引擎重新加载
|
// 通知 RN 引擎重新加载
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`checkRnUpdate` 会优先读取本地保存的 RN bundleVersion;如果本地没有缓存,再回退到调用方手工传入的版本号。
|
||||||
|
如果项目里还有别的 bundle 安装入口,不经过 `downloadRnBundle`,那条链路也要在安装成功后调用 `XuqmSDK.update.rememberRnBundleInfo(...)`,否则本地缓存会落后于真实已安装版本。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 发版(ohpm)
|
## 发版(ohpm)
|
||||||
|
|||||||
@ -10,6 +10,7 @@ export type {
|
|||||||
SendMessageParams,
|
SendMessageParams,
|
||||||
AppVersionInfo,
|
AppVersionInfo,
|
||||||
RnBundleInfo,
|
RnBundleInfo,
|
||||||
|
InstalledRnBundleInfo,
|
||||||
PushTokenInfo,
|
PushTokenInfo,
|
||||||
MsgType,
|
MsgType,
|
||||||
ChatType,
|
ChatType,
|
||||||
|
|||||||
@ -0,0 +1,262 @@
|
|||||||
|
/**
|
||||||
|
* XuqmGroup Update Service — Harmony RN Bundle Release Task
|
||||||
|
*
|
||||||
|
* Copy this file to your Harmony project and apply it:
|
||||||
|
* apply(from = "xuqm_rn_release.gradle.kts")
|
||||||
|
*
|
||||||
|
* Then run:
|
||||||
|
* ./hvigorw xuqmRnRelease
|
||||||
|
*
|
||||||
|
* Config: xuqm.properties in the project root (or module root)
|
||||||
|
* ---
|
||||||
|
* xuqm.serverUrl=https://update.dev.xuqinmin.com
|
||||||
|
* xuqm.appKey=your-app-key
|
||||||
|
* xuqm.apiToken=your-api-token
|
||||||
|
* xuqm.moduleId=main
|
||||||
|
* xuqm.bundleFile=/path/to/index.bundle
|
||||||
|
* xuqm.resourceDir=/path/to/resources # optional, but recommended
|
||||||
|
* xuqm.bundleVersion=100
|
||||||
|
* xuqm.minCommonVersion=1.0.0 # optional
|
||||||
|
* xuqm.packageName=com.example.app # optional
|
||||||
|
* xuqm.note=optional note
|
||||||
|
* ---
|
||||||
|
*
|
||||||
|
* The task:
|
||||||
|
* 1. Reads local RN release metadata from xuqm.properties
|
||||||
|
* 2. Packages bundle + resources + rn-manifest.json into a zip
|
||||||
|
* 3. Uploads the zip to update-service so the server can auto-detect metadata
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.net.URI
|
||||||
|
import java.net.http.HttpClient
|
||||||
|
import java.net.http.HttpRequest
|
||||||
|
import java.net.http.HttpResponse
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.Properties
|
||||||
|
import java.util.UUID
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
|
fun loadXuqmConfig(projectDir: File): Properties {
|
||||||
|
val props = Properties()
|
||||||
|
listOf(
|
||||||
|
File(projectDir, "xuqm.properties"),
|
||||||
|
File(projectDir.parentFile, "xuqm.properties"),
|
||||||
|
).firstOrNull { it.exists() }?.inputStream()?.use(props::load)
|
||||||
|
?: throw GradleException("xuqm.properties not found in module or project root")
|
||||||
|
return props
|
||||||
|
}
|
||||||
|
|
||||||
|
fun httpGet(url: String, token: String): String {
|
||||||
|
val client = HttpClient.newHttpClient()
|
||||||
|
val builder = HttpRequest.newBuilder(URI.create(url))
|
||||||
|
if (token.isNotBlank()) {
|
||||||
|
builder.header("Authorization", "Bearer $token")
|
||||||
|
}
|
||||||
|
val req = builder.GET().build()
|
||||||
|
return client.send(req, HttpResponse.BodyHandlers.ofString()).body()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun httpMultipartPost(url: String, token: String, parts: Map<String, Any>): String {
|
||||||
|
val boundary = UUID.randomUUID().toString()
|
||||||
|
val baos = java.io.ByteArrayOutputStream()
|
||||||
|
|
||||||
|
fun writeln(s: String) = baos.write(("$s\r\n").toByteArray())
|
||||||
|
|
||||||
|
for ((name, value) in parts) {
|
||||||
|
writeln("--$boundary")
|
||||||
|
when (value) {
|
||||||
|
is File -> {
|
||||||
|
writeln("Content-Disposition: form-data; name=\"$name\"; filename=\"${value.name}\"")
|
||||||
|
writeln("Content-Type: application/octet-stream")
|
||||||
|
writeln("")
|
||||||
|
baos.write(value.readBytes())
|
||||||
|
writeln("")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
writeln("Content-Disposition: form-data; name=\"$name\"")
|
||||||
|
writeln("")
|
||||||
|
writeln(value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writeln("--$boundary--")
|
||||||
|
|
||||||
|
val body = baos.toByteArray()
|
||||||
|
val client = HttpClient.newHttpClient()
|
||||||
|
val builder = HttpRequest.newBuilder(URI.create(url))
|
||||||
|
.header("Content-Type", "multipart/form-data; boundary=$boundary")
|
||||||
|
if (token.isNotBlank()) {
|
||||||
|
builder.header("Authorization", "Bearer $token")
|
||||||
|
}
|
||||||
|
val req = builder.POST(HttpRequest.BodyPublishers.ofByteArray(body)).build()
|
||||||
|
return client.send(req, HttpResponse.BodyHandlers.ofString()).body()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun parseJson(json: String, key: String): String? =
|
||||||
|
Regex("\"$key\"\\s*:\\s*\"([^\"]+)\"").find(json)?.groupValues?.get(1)
|
||||||
|
|
||||||
|
fun parseJsonInt(json: String, key: String): Int? =
|
||||||
|
Regex("\"$key\"\\s*:\\s*(\\d+)").find(json)?.groupValues?.get(1)?.toIntOrNull()
|
||||||
|
|
||||||
|
fun parseCsv(value: String?): List<String> =
|
||||||
|
value?.split(",")?.map { it.trim() }?.filter { it.isNotBlank() } ?: emptyList()
|
||||||
|
|
||||||
|
fun promptLine(message: String): String? {
|
||||||
|
val console = System.console() ?: return null
|
||||||
|
print(message)
|
||||||
|
return console.readLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun escapeJson(value: String?): String {
|
||||||
|
if (value == null) return ""
|
||||||
|
return buildString(value.length + 8) {
|
||||||
|
value.forEach { ch ->
|
||||||
|
when (ch) {
|
||||||
|
'\\' -> append("\\\\")
|
||||||
|
'"' -> append("\\\"")
|
||||||
|
'\b' -> append("\\b")
|
||||||
|
'\u000C' -> append("\\f")
|
||||||
|
'\n' -> append("\\n")
|
||||||
|
'\r' -> append("\\r")
|
||||||
|
'\t' -> append("\\t")
|
||||||
|
else -> append(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeZipEntry(zip: ZipOutputStream, entryName: String, content: ByteArray) {
|
||||||
|
val entry = ZipEntry(entryName)
|
||||||
|
zip.putNextEntry(entry)
|
||||||
|
zip.write(content)
|
||||||
|
zip.closeEntry()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addFileToZip(zip: ZipOutputStream, file: File, entryName: String) {
|
||||||
|
val entry = ZipEntry(entryName)
|
||||||
|
zip.putNextEntry(entry)
|
||||||
|
file.inputStream().use { input ->
|
||||||
|
input.copyTo(zip)
|
||||||
|
}
|
||||||
|
zip.closeEntry()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDirectoryToZip(zip: ZipOutputStream, dir: File, prefix: String) {
|
||||||
|
if (!dir.exists()) return
|
||||||
|
dir.walkTopDown()
|
||||||
|
.filter { it.isFile }
|
||||||
|
.forEach { file ->
|
||||||
|
val relative = file.relativeTo(dir).path.replace(File.separatorChar, '/')
|
||||||
|
addFileToZip(zip, file, "$prefix/$relative")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun packRnReleaseZip(bundleFile: File, resourceDir: File?, manifestJson: String): File {
|
||||||
|
val zipPath = Files.createTempFile("xuqm-rn-release-", ".zip").toFile()
|
||||||
|
ZipOutputStream(FileOutputStream(zipPath)).use { zip ->
|
||||||
|
writeZipEntry(zip, "rn-manifest.json", manifestJson.toByteArray(StandardCharsets.UTF_8))
|
||||||
|
addFileToZip(zip, bundleFile, "bundle/${bundleFile.name}")
|
||||||
|
if (resourceDir != null && resourceDir.exists()) {
|
||||||
|
addDirectoryToZip(zip, resourceDir, "resources")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zipPath
|
||||||
|
}
|
||||||
|
|
||||||
|
fun findHighestVersion(listResp: String): Int {
|
||||||
|
return Regex("\"version\"\\s*:\\s*\"?(\\d+)\"?").findAll(listResp)
|
||||||
|
.mapNotNull { it.groupValues[1].toIntOrNull() }
|
||||||
|
.maxOrNull() ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register("xuqmRnRelease") {
|
||||||
|
group = "xuqm"
|
||||||
|
description = "Package RN bundle + resources into zip and upload to XuqmGroup Update Service"
|
||||||
|
|
||||||
|
doLast {
|
||||||
|
val cfg = loadXuqmConfig(projectDir)
|
||||||
|
val serverUrl = cfg.getProperty("xuqm.serverUrl") ?: throw GradleException("xuqm.serverUrl missing")
|
||||||
|
val appKey = cfg.getProperty("xuqm.appKey") ?: throw GradleException("xuqm.appKey missing")
|
||||||
|
val apiToken = cfg.getProperty("xuqm.apiToken") ?: throw GradleException("xuqm.apiToken missing")
|
||||||
|
val moduleId = cfg.getProperty("xuqm.moduleId") ?: throw GradleException("xuqm.moduleId missing")
|
||||||
|
val bundleFilePath = cfg.getProperty("xuqm.bundleFile") ?: throw GradleException("xuqm.bundleFile missing")
|
||||||
|
val bundleVersion = cfg.getProperty("xuqm.bundleVersion")
|
||||||
|
?.trim()
|
||||||
|
?.toIntOrNull()
|
||||||
|
?: throw GradleException("xuqm.bundleVersion missing or invalid")
|
||||||
|
val resourceDirPath = cfg.getProperty("xuqm.resourceDir", "").trim()
|
||||||
|
val minCommonVersion = cfg.getProperty("xuqm.minCommonVersion", "").trim()
|
||||||
|
val packageName = cfg.getProperty("xuqm.packageName", "").trim()
|
||||||
|
val note = cfg.getProperty("xuqm.note", "").trim()
|
||||||
|
|
||||||
|
val bundleFile = File(bundleFilePath)
|
||||||
|
if (!bundleFile.exists()) {
|
||||||
|
throw GradleException("Bundle file not found: ${bundleFile.absolutePath}")
|
||||||
|
}
|
||||||
|
val resourceDir = if (resourceDirPath.isNotBlank()) File(resourceDirPath) else null
|
||||||
|
if (resourceDir != null && !resourceDir.exists()) {
|
||||||
|
throw GradleException("Resource directory not found: ${resourceDir.absolutePath}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val listResp = httpGet("$serverUrl/api/v1/rn/list?appId=$appKey&moduleId=$moduleId&platform=HARMONY", apiToken)
|
||||||
|
val serverVersion = findHighestVersion(listResp)
|
||||||
|
println("[xuqm] Server latest bundleVersion: $serverVersion")
|
||||||
|
|
||||||
|
var releaseVersion = bundleVersion
|
||||||
|
if (releaseVersion <= serverVersion && System.console() != null) {
|
||||||
|
println("[xuqm] Local bundleVersion is not greater than server latest. Please enter corrected release version info.")
|
||||||
|
val inputVersion = promptLine("Release bundleVersion [$bundleVersion]: ")?.trim().orEmpty()
|
||||||
|
if (inputVersion.isNotBlank()) {
|
||||||
|
releaseVersion = inputVersion.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (releaseVersion <= serverVersion) {
|
||||||
|
throw GradleException(
|
||||||
|
"Release bundleVersion ($releaseVersion) must be greater than server ($serverVersion). " +
|
||||||
|
"Bump the local RN release metadata or override intentionally via script config."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val manifestJson = buildString {
|
||||||
|
append("{")
|
||||||
|
append("\"moduleId\":\"").append(escapeJson(moduleId)).append("\",")
|
||||||
|
append("\"platform\":\"HARMONY\",")
|
||||||
|
append("\"version\":\"").append(releaseVersion).append("\",")
|
||||||
|
append("\"bundleVersion\":").append(releaseVersion).append(",")
|
||||||
|
append("\"minCommonVersion\":\"").append(escapeJson(minCommonVersion)).append("\",")
|
||||||
|
append("\"packageName\":\"").append(escapeJson(packageName)).append("\",")
|
||||||
|
append("\"note\":\"").append(escapeJson(note)).append("\"")
|
||||||
|
append("}")
|
||||||
|
}
|
||||||
|
val zipFile = packRnReleaseZip(bundleFile, resourceDir, manifestJson)
|
||||||
|
|
||||||
|
println("[xuqm] RN bundle zip: ${zipFile.absolutePath}")
|
||||||
|
println("[xuqm] bundleVersion=$releaseVersion, moduleId=$moduleId, minCommonVersion=${minCommonVersion.ifBlank { "-" }}, packageName=${packageName.ifBlank { "-" }}")
|
||||||
|
|
||||||
|
val parts = mutableMapOf<String, Any>(
|
||||||
|
"appId" to appKey,
|
||||||
|
"moduleId" to moduleId,
|
||||||
|
"platform" to "HARMONY",
|
||||||
|
"version" to releaseVersion.toString(),
|
||||||
|
"bundle" to zipFile,
|
||||||
|
)
|
||||||
|
if (minCommonVersion.isNotBlank()) parts["minCommonVersion"] = minCommonVersion
|
||||||
|
if (packageName.isNotBlank()) parts["packageName"] = packageName
|
||||||
|
if (note.isNotBlank()) parts["note"] = note
|
||||||
|
|
||||||
|
println("[xuqm] Uploading RN release zip...")
|
||||||
|
val uploadResp = httpMultipartPost("$serverUrl/api/v1/rn/upload", apiToken, parts)
|
||||||
|
val versionId = parseJson(uploadResp, "id")
|
||||||
|
?: throw GradleException("[xuqm] Upload failed:\n$uploadResp")
|
||||||
|
println("[xuqm] Uploaded, version ID: $versionId")
|
||||||
|
println("[xuqm] Release zip contains rn-manifest.json + bundle + resources")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ import common from '@ohos.app.ability.common'
|
|||||||
import type { SDKConfig } from './core/Types'
|
import type { SDKConfig } from './core/Types'
|
||||||
import { SDKContext } from './core/SDKContext'
|
import { SDKContext } from './core/SDKContext'
|
||||||
import { ImClient } from './im/ImClient'
|
import { ImClient } from './im/ImClient'
|
||||||
|
import { UpdateSDK } from './update/UpdateSDK'
|
||||||
|
|
||||||
export class XuqmSDK {
|
export class XuqmSDK {
|
||||||
private static _imClient: ImClient | null = null
|
private static _imClient: ImClient | null = null
|
||||||
@ -33,4 +34,8 @@ export class XuqmSDK {
|
|||||||
}
|
}
|
||||||
return XuqmSDK._imClient
|
return XuqmSDK._imClient
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get update(): UpdateSDK {
|
||||||
|
return UpdateSDK
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import preferences from '@ohos.data.preferences'
|
import preferences from '@ohos.data.preferences'
|
||||||
import type { SDKConfig } from './Types'
|
import type { InstalledRnBundleInfo, SDKConfig } from './Types'
|
||||||
|
|
||||||
const TOKEN_KEY = 'xuqm_token'
|
const TOKEN_KEY = 'xuqm_token'
|
||||||
|
const RN_BUNDLE_PREFIX = 'xuqm_rn_bundle_'
|
||||||
const PREF_NAME = 'xuqm_sdk_prefs'
|
const PREF_NAME = 'xuqm_sdk_prefs'
|
||||||
|
|
||||||
export class SDKContext {
|
export class SDKContext {
|
||||||
@ -53,4 +54,37 @@ export class SDKContext {
|
|||||||
static getUserId(): string | null {
|
static getUserId(): string | null {
|
||||||
return SDKContext._userId
|
return SDKContext._userId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static rnBundleKey(bundleName: string): string {
|
||||||
|
return RN_BUNDLE_PREFIX + bundleName
|
||||||
|
}
|
||||||
|
|
||||||
|
static async setRnBundleInfo(bundleName: string, info: InstalledRnBundleInfo): Promise<void> {
|
||||||
|
if (!SDKContext._pref || !bundleName.trim()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const payload = JSON.stringify(info)
|
||||||
|
await SDKContext._pref.put(SDKContext.rnBundleKey(bundleName), payload)
|
||||||
|
await SDKContext._pref.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getRnBundleInfo(bundleName: string): Promise<InstalledRnBundleInfo | null> {
|
||||||
|
if (!SDKContext._pref || !bundleName.trim()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const raw = await SDKContext._pref.get(SDKContext.rnBundleKey(bundleName), '') as string
|
||||||
|
if (!raw) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return JSON.parse(raw) as InstalledRnBundleInfo
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getRnBundleVersion(bundleName: string): Promise<number | null> {
|
||||||
|
const info = await SDKContext.getRnBundleInfo(bundleName)
|
||||||
|
return info?.bundleVersion ?? null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -155,9 +155,17 @@ export interface RnBundleInfo {
|
|||||||
md5: string
|
md5: string
|
||||||
forceUpdate: boolean
|
forceUpdate: boolean
|
||||||
packageName?: string
|
packageName?: string
|
||||||
|
minCommonVersion?: string
|
||||||
packageMatched?: boolean
|
packageMatched?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InstalledRnBundleInfo {
|
||||||
|
bundleVersion: number
|
||||||
|
packageName?: string
|
||||||
|
minCommonVersion?: string
|
||||||
|
installedAt: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface PushTokenInfo {
|
export interface PushTokenInfo {
|
||||||
vendor: string
|
vendor: string
|
||||||
token: string
|
token: string
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import common from '@ohos.app.ability.common'
|
|||||||
import request from '@ohos.request'
|
import request from '@ohos.request'
|
||||||
import type { BusinessError } from '@ohos.base'
|
import type { BusinessError } from '@ohos.base'
|
||||||
import { HttpClient } from '../core/HttpClient'
|
import { HttpClient } from '../core/HttpClient'
|
||||||
import type { AppVersionInfo, RnBundleInfo } from '../core/Types'
|
import type { AppVersionInfo, InstalledRnBundleInfo, RnBundleInfo } from '../core/Types'
|
||||||
import { SDKContext } from '../core/SDKContext'
|
import { SDKContext } from '../core/SDKContext'
|
||||||
|
|
||||||
export interface AppUpdateResult {
|
export interface AppUpdateResult {
|
||||||
@ -43,14 +43,15 @@ export class UpdateSDK {
|
|||||||
static async checkRnUpdate(
|
static async checkRnUpdate(
|
||||||
appKey: string,
|
appKey: string,
|
||||||
bundleName: string,
|
bundleName: string,
|
||||||
currentBundleVersion: number,
|
currentBundleVersion?: number,
|
||||||
packageName?: string
|
packageName?: string
|
||||||
): Promise<RnUpdateResult> {
|
): Promise<RnUpdateResult> {
|
||||||
|
const localBundleVersion = currentBundleVersion ?? await SDKContext.getRnBundleVersion(bundleName) ?? 0
|
||||||
const data = await HttpClient.get<RnBundleInfo>(
|
const data = await HttpClient.get<RnBundleInfo>(
|
||||||
`/api/v1/rn/update/check?appKey=${appKey}&bundleName=${bundleName}&bundleVersion=${currentBundleVersion}${packageName ? `&packageName=${encodeURIComponent(packageName)}` : ''}`
|
`/api/v1/rn/update/check?appKey=${appKey}&bundleName=${bundleName}&bundleVersion=${localBundleVersion}${packageName ? `&packageName=${encodeURIComponent(packageName)}` : ''}`
|
||||||
)
|
)
|
||||||
|
|
||||||
if (data.bundleVersion <= currentBundleVersion) {
|
if (data.bundleVersion <= localBundleVersion) {
|
||||||
return { hasUpdate: false }
|
return { hasUpdate: false }
|
||||||
}
|
}
|
||||||
if (packageName && data.packageMatched === false) {
|
if (packageName && data.packageMatched === false) {
|
||||||
@ -62,7 +63,9 @@ export class UpdateSDK {
|
|||||||
static async downloadRnBundle(
|
static async downloadRnBundle(
|
||||||
context: common.UIAbilityContext,
|
context: common.UIAbilityContext,
|
||||||
downloadUrl: string,
|
downloadUrl: string,
|
||||||
destFilename: string
|
destFilename: string,
|
||||||
|
bundleName?: string,
|
||||||
|
bundleInfo?: RnBundleInfo,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const destPath = context.cacheDir + '/' + destFilename
|
const destPath = context.cacheDir + '/' + destFilename
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
@ -75,9 +78,36 @@ export class UpdateSDK {
|
|||||||
task.on('fail', (error: number) => reject(new Error(`Download failed: ${error}`)))
|
task.on('fail', (error: number) => reject(new Error(`Download failed: ${error}`)))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
if (bundleName && bundleInfo) {
|
||||||
|
const installedInfo: InstalledRnBundleInfo = {
|
||||||
|
bundleVersion: bundleInfo.bundleVersion,
|
||||||
|
packageName: bundleInfo.packageName,
|
||||||
|
minCommonVersion: bundleInfo.minCommonVersion,
|
||||||
|
installedAt: Date.now(),
|
||||||
|
}
|
||||||
|
await SDKContext.setRnBundleInfo(bundleName, installedInfo)
|
||||||
|
}
|
||||||
if (SDKContext.getConfig().debug) {
|
if (SDKContext.getConfig().debug) {
|
||||||
console.log('[UpdateSDK] RN bundle downloaded to', destPath)
|
console.log('[UpdateSDK] RN bundle downloaded to', destPath)
|
||||||
}
|
}
|
||||||
return destPath
|
return destPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async rememberRnBundleInfo(
|
||||||
|
bundleName: string,
|
||||||
|
bundleVersion: number,
|
||||||
|
packageName?: string,
|
||||||
|
minCommonVersion?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
await SDKContext.setRnBundleInfo(bundleName, {
|
||||||
|
bundleVersion,
|
||||||
|
packageName,
|
||||||
|
minCommonVersion,
|
||||||
|
installedAt: Date.now(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getInstalledRnBundleInfo(bundleName: string): Promise<InstalledRnBundleInfo | null> {
|
||||||
|
return SDKContext.getRnBundleInfo(bundleName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户