feat(sdk): 新增 autoInitialize(),放入 license 文件即可完成全量 SDK 初始化

- LicenseFile 新增 serverUrl 字段(私有化部署时由后端写入)
- LicenseSDK 新增 readLicenseFileData() 供 sdk-core 反射调用
- XuqmSDK.autoInitialize(context) 从 assets/xuqm/license.xuqm 读取
  appKey 和 serverUrl,一行完成公/私有化 SDK 初始化

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-05-21 15:25:22 +08:00
父节点 02407dfe16
当前提交 84eeecb4cc
共有 3 个文件被更改,包括 38 次插入0 次删除

查看文件

@ -27,6 +27,24 @@ object XuqmSDK {
@Volatile
private var loginSession: XuqmLoginSession? = null
/**
* Initializes the SDK automatically from the license file embedded in assets/xuqm/.
* Place the license.xuqm file downloaded from the tenant platform into your app's
* src/main/assets/xuqm/ directory no hardcoded appKey or serverUrl needed.
*
* For private deployments the license file contains the server URL and all service
* endpoints are configured automatically. For public deployments the default endpoints
* (dev.xuqinmin.com) are used.
*/
fun autoInitialize(context: Context, logLevel: LogLevel = LogLevel.WARN) {
val (appKey, serverUrl) = readLicenseFileData(context)
?: throw IllegalStateException(
"No license file found in assets/xuqm/. " +
"Download license.xuqm from the tenant platform and place it in src/main/assets/xuqm/."
)
initialize(context, appKey, serverUrl, logLevel)
}
fun initialize(
context: Context,
appKey: String,
@ -52,6 +70,14 @@ object XuqmSDK {
serverUrl?.takeIf { it.isNotBlank() }?.let { configurePrivateServer(context, appKey, it) }
}
@Suppress("UNCHECKED_CAST")
private fun readLicenseFileData(context: Context): Pair<String, String?>? = runCatching {
val clazz = Class.forName("com.xuqm.sdk.license.LicenseSDK")
val instance = clazz.getField("INSTANCE").get(null)
val method = clazz.getMethod("readLicenseFileData", Context::class.java)
method.invoke(instance, context.applicationContext) as? Pair<String, String?>
}.getOrNull()
private fun configurePrivateServer(context: Context, appKey: String, serverUrl: String) {
val base = serverUrl.trimEnd('/') + "/"
val wsBase = serverUrl.trimEnd('/')

查看文件

@ -186,6 +186,16 @@ object LicenseSDK {
return store.deviceId
}
/**
* Returns (appKey, serverUrl) from the embedded license file, or null if no file is present.
* Called by XuqmSDK.autoInitialize() via reflection sdk-core cannot depend on sdk-license directly.
*/
fun readLicenseFileData(context: Context): Pair<String, String?>? {
val licenseFile = LicenseFileReader.read(context) ?: return null
val appKey = licenseFile.appKey.takeIf { it.isNotBlank() } ?: return null
return appKey to licenseFile.serverUrl?.takeIf { it.isNotBlank() }
}
/**
* Clear all license data (token, device ID, status).
*/

查看文件

@ -11,6 +11,8 @@ data class LicenseFile(
@SerializedName(value = "appName", alternate = ["app_name"]) val appName: String? = null,
@SerializedName(value = "companyName", alternate = ["company_name"]) val companyName: String? = null,
@SerializedName(value = "baseUrl", alternate = ["base_url"]) val baseUrl: String? = null,
// Set only for private deployments; used by XuqmSDK.autoInitialize() to configure all service endpoints.
@SerializedName(value = "serverUrl", alternate = ["server_url"]) val serverUrl: String? = null,
@SerializedName(value = "issuedAt", alternate = ["issued_at"]) val issuedAt: String? = null,
@SerializedName(value = "expiresAt", alternate = ["expires_at"]) val expiresAt: String? = null,
)