feat(bugcollect): 使用 deviceId 作为 userId 兜底,保障影响设备数统计

- XuqmSDK 新增 deviceId 属性:首次生成 UUID 存入 SharedPreferences,后续不变
- BugCollect 上报事件时 userId 改为 getUserId() ?: deviceId,未登录也能统计影响设备数
- BugCollectInitProvider 崩溃拦截同步使用 deviceId 兜底

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
这个提交包含在:
XuqmGroup 2026-06-18 10:47:38 +08:00
父节点 77fef3a38b
当前提交 aca0d009a8
共有 3 个文件被更改,包括 25 次插入9 次删除

查看文件

@ -45,7 +45,7 @@ object BugCollect {
if (!isReady()) return if (!isReady()) return
queue().push(LogEvent( queue().push(LogEvent(
name = name, properties = properties, name = name, properties = properties,
appKey = XuqmSDK.appKey, userId = XuqmSDK.getUserId(), appKey = XuqmSDK.appKey, userId = XuqmSDK.getUserId() ?: XuqmSDK.deviceId,
platform = "android", release = appVersion(), environment = environment, platform = "android", release = appVersion(), environment = environment,
sdk = IssueEvent.SdkInfo(SDK_NAME, SDK_VERSION), sdk = IssueEvent.SdkInfo(SDK_NAME, SDK_VERSION),
)) ))
@ -73,8 +73,8 @@ object BugCollect {
breadcrumbs = crumbs, breadcrumbs = crumbs,
release = appVersion(), release = appVersion(),
environment = environment, environment = environment,
userId = XuqmSDK.getUserId(), userId = XuqmSDK.getUserId() ?: XuqmSDK.deviceId,
user = IssueEvent.UserInfo(id = XuqmSDK.getUserId()), user = IssueEvent.UserInfo(id = XuqmSDK.getUserId() ?: XuqmSDK.deviceId),
device = buildDeviceInfo(), device = buildDeviceInfo(),
tags = tags, tags = tags,
)) ))
@ -100,8 +100,8 @@ object BugCollect {
breadcrumbs = crumbs, breadcrumbs = crumbs,
release = appVersion(), release = appVersion(),
environment = environment, environment = environment,
userId = XuqmSDK.getUserId(), userId = XuqmSDK.getUserId() ?: XuqmSDK.deviceId,
user = IssueEvent.UserInfo(id = XuqmSDK.getUserId()), user = IssueEvent.UserInfo(id = XuqmSDK.getUserId() ?: XuqmSDK.deviceId),
device = buildDeviceInfo(), device = buildDeviceInfo(),
tags = tags, tags = tags,
)) ))
@ -118,8 +118,8 @@ object BugCollect {
exception = IssueEvent.ExceptionInfo(type = "Warning", value = message), exception = IssueEvent.ExceptionInfo(type = "Warning", value = message),
release = appVersion(), release = appVersion(),
environment = environment, environment = environment,
userId = XuqmSDK.getUserId(), userId = XuqmSDK.getUserId() ?: XuqmSDK.deviceId,
user = IssueEvent.UserInfo(id = XuqmSDK.getUserId()), user = IssueEvent.UserInfo(id = XuqmSDK.getUserId() ?: XuqmSDK.deviceId),
tags = tags, tags = tags,
)) ))
} }
@ -141,7 +141,7 @@ object BugCollect {
CrashCapture.start( CrashCapture.start(
appContext = XuqmSDK.appContext, appContext = XuqmSDK.appContext,
appKey = XuqmSDK.appKey, appKey = XuqmSDK.appKey,
getUserId = { XuqmSDK.getUserId() }, getUserId = { XuqmSDK.getUserId() ?: XuqmSDK.deviceId },
) )
queue().uploadPendingCrashes() queue().uploadPendingCrashes()
} }

查看文件

@ -40,7 +40,7 @@ internal class BugCollectInitProvider : ContentProvider() {
CrashCapture.start( CrashCapture.start(
appContext = ctx, appContext = ctx,
appKey = XuqmSDK.appKey, appKey = XuqmSDK.appKey,
getUserId = { XuqmSDK.getUserId() }, getUserId = { XuqmSDK.getUserId() ?: XuqmSDK.deviceId },
) )
// ── Phase 2远端配置就绪后上传 ───────────────────────────────────────── // ── Phase 2远端配置就绪后上传 ─────────────────────────────────────────

查看文件

@ -43,6 +43,11 @@ object XuqmSDK {
/** 初始化时使用的平台地址(同步可用,远端配置到达前即有值)。 */ /** 初始化时使用的平台地址(同步可用,远端配置到达前即有值)。 */
val platformUrl: String get() = resolvedPlatformUrl val platformUrl: String get() = resolvedPlatformUrl
/** 持久设备 IDUUID,首次生成后永不变更。未上报 userId 的场景可用此 ID 追踪影响设备数。 */
@Volatile var deviceId: String = ""
private set
@Volatile private var loginSession: XuqmLoginSession? = null @Volatile private var loginSession: XuqmLoginSession? = null
@Volatile private var userInfoValue: XuqmUserInfo? = null @Volatile private var userInfoValue: XuqmUserInfo? = null
@Volatile var platformConfig: SdkPlatformConfig? = null @Volatile var platformConfig: SdkPlatformConfig? = null
@ -121,6 +126,7 @@ object XuqmSDK {
config = SDKConfig(appKey, logLevel) config = SDKConfig(appKey, logLevel)
appContext = applicationContext appContext = applicationContext
tokenStore = TokenStore(applicationContext) tokenStore = TokenStore(applicationContext)
deviceId = resolveDeviceId(applicationContext)
ApiClient.init(config, tokenStore) ApiClient.init(config, tokenStore)
initializedAppKey = appKey initializedAppKey = appKey
initialized = true initialized = true
@ -217,6 +223,16 @@ object XuqmSDK {
) )
} }
private fun resolveDeviceId(context: Context): String {
val prefs = context.getSharedPreferences("xuqm_sdk_core", android.content.Context.MODE_PRIVATE)
val key = "device_id"
val stored = prefs.getString(key, null)
if (!stored.isNullOrBlank()) return stored
val generated = java.util.UUID.randomUUID().toString()
prefs.edit().putString(key, generated).apply()
return generated
}
private fun platformInitException(platformUrl: String, appKey: String, cause: Throwable): Throwable { private fun platformInitException(platformUrl: String, appKey: String, cause: Throwable): Throwable {
val kind = if (platformUrl == DEFAULT_PLATFORM_URL) "公有平台" else "私有化平台 $platformUrl" val kind = if (platformUrl == DEFAULT_PLATFORM_URL) "公有平台" else "私有化平台 $platformUrl"
return IllegalStateException( return IllegalStateException(