feat: 简化登录模型,移除 nickname/avatar/expiresAt

- XuqmSDK.login() 只保留 userId + userSig
- 移除 UserSigRefresher 续签逻辑
- XuqmLoginSession 移除 nickname/avatar/expiresAt
- ImSDK.login() 同步简化
- AuthRepository 适配新登录方式,移除过期检查与定时刷新
- README 示例同步更新
这个提交包含在:
XuqmGroup 2026-05-01 22:18:47 +08:00
父节点 595a91ae3d
当前提交 553427d44e
共有 6 个文件被更改,包括 7 次插入139 次删除

查看文件

@ -68,11 +68,10 @@ val userSig = api.getUserSig(userId)
XuqmSDK.login( XuqmSDK.login(
userId = userId, userId = userId,
userSig = userSig, userSig = userSig,
nickname = profile.nickname,
avatar = profile.avatar,
) )
// 如果工程里集成了 sdk-im,SDK 会自动完成 IM 登录 // 如果工程里集成了 sdk-im / sdk-push / sdk-update,
// SDK 会自动完成对应模块的登录与初始化
``` ```
### 3. 切换联调环境 ### 3. 切换联调环境

查看文件

@ -30,9 +30,6 @@ import java.util.concurrent.atomic.AtomicBoolean
class AuthRepository(context: Context) { class AuthRepository(context: Context) {
private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private var refreshJob: Job? = null
private val refreshing = AtomicBoolean(false)
private val prefs = EncryptedSharedPreferences.create( private val prefs = EncryptedSharedPreferences.create(
"xuqm_demo_auth", "xuqm_demo_auth",
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC), MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
@ -52,21 +49,17 @@ class AuthRepository(context: Context) {
fun getCurrentNickname(): String? = prefs.getString("nickname", null) fun getCurrentNickname(): String? = prefs.getString("nickname", null)
fun getCurrentAvatar(): String? = prefs.getString("avatar", null) fun getCurrentAvatar(): String? = prefs.getString("avatar", null)
fun getCurrentUserSig(): String? = prefs.getString("user_sig", null) fun getCurrentUserSig(): String? = prefs.getString("user_sig", null)
fun getCurrentUserSigExpiresAt(): Long = prefs.getLong("user_sig_expires_at", 0L)
fun isLoggedIn(): Boolean = hasUsableSession() fun isLoggedIn(): Boolean = hasUsableSession()
private fun hasUsableSession(): Boolean { private fun hasUsableSession(): Boolean {
val now = System.currentTimeMillis()
val demoToken = getDemoToken().orEmpty() val demoToken = getDemoToken().orEmpty()
val userId = getCurrentUserId().orEmpty() val userId = getCurrentUserId().orEmpty()
val userSig = getCurrentUserSig().orEmpty() val userSig = getCurrentUserSig().orEmpty()
val demoTokenExpiresAt = prefs.getLong("demo_token_expires_at", 0L) val demoTokenExpiresAt = prefs.getLong("demo_token_expires_at", 0L)
val userSigExpiresAt = getCurrentUserSigExpiresAt()
return demoToken.isNotBlank() && return demoToken.isNotBlank() &&
userId.isNotBlank() && userId.isNotBlank() &&
userSig.isNotBlank() && userSig.isNotBlank() &&
demoTokenExpiresAt > now && demoTokenExpiresAt > System.currentTimeMillis()
userSigExpiresAt > now
} }
private fun saveSession(result: AuthResult) { private fun saveSession(result: AuthResult) {
@ -78,27 +71,7 @@ class AuthRepository(context: Context) {
.putString("nickname", profile.nickname) .putString("nickname", profile.nickname)
.putString("avatar", profile.avatar) .putString("avatar", profile.avatar)
.putString("user_sig", result.userSig) .putString("user_sig", result.userSig)
.putLong("user_sig_expires_at", result.imTokenExpiresAt ?: 0L)
.apply() .apply()
scheduleImTokenRefresh(result.imTokenExpiresAt)
}
private fun saveImCredential(result: ImRefreshResult) {
prefs.edit()
.putString("user_sig", result.userSig)
.putLong("user_sig_expires_at", result.imTokenExpiresAt ?: 0L)
.apply()
}
private fun scheduleImTokenRefresh(expiresAt: Long?) {
refreshJob?.cancel()
val tokenExpiresAt = expiresAt ?: 0L
if (tokenExpiresAt <= 0L) return
val delayMs = (tokenExpiresAt - System.currentTimeMillis() - REFRESH_GRACE_MS).coerceAtLeast(0L)
refreshJob = appScope.launch {
delay(delayMs)
refreshImToken()
}
} }
suspend fun login(userId: String, password: String): Result<UserData> = suspend fun login(userId: String, password: String): Result<UserData> =
@ -113,8 +86,6 @@ class AuthRepository(context: Context) {
XuqmSDK.login( XuqmSDK.login(
userId = data.profile.userId, userId = data.profile.userId,
userSig = userSig, userSig = userSig,
nickname = data.profile.nickname,
avatar = data.profile.avatar,
) )
UserData(data.profile.userId, data.profile.nickname, data.profile.avatar, data.profile.gender) UserData(data.profile.userId, data.profile.nickname, data.profile.avatar, data.profile.gender)
} }
@ -133,8 +104,6 @@ class AuthRepository(context: Context) {
XuqmSDK.login( XuqmSDK.login(
userId = data.profile.userId, userId = data.profile.userId,
userSig = userSig, userSig = userSig,
nickname = data.profile.nickname,
avatar = data.profile.avatar,
) )
UserData(data.profile.userId, data.profile.nickname, data.profile.avatar, data.profile.gender) UserData(data.profile.userId, data.profile.nickname, data.profile.avatar, data.profile.gender)
} }
@ -177,86 +146,22 @@ class AuthRepository(context: Context) {
val userSig = getCurrentUserSig() val userSig = getCurrentUserSig()
val demoToken = getDemoToken() val demoToken = getDemoToken()
if (userId.isNullOrBlank() || userSig.isNullOrBlank() || demoToken.isNullOrBlank()) { if (userId.isNullOrBlank() || userSig.isNullOrBlank() || demoToken.isNullOrBlank()) {
clearSession() logout()
throw IllegalStateException("No cached session") throw IllegalStateException("No cached session")
} }
val refreshed = if (shouldRefreshImToken()) {
refreshImTokenInternal()
} else {
null
}
if (shouldRefreshImToken() && refreshed == null) {
clearSession()
throw IllegalStateException("Cached session expired")
}
XuqmSDK.login( XuqmSDK.login(
userId = userId, userId = userId,
userSig = refreshed?.userSig ?: userSig, userSig = userSig,
nickname = getCurrentNickname(),
avatar = getCurrentAvatar(),
) )
if (refreshed != null) { Unit
saveImCredential(refreshed)
scheduleImTokenRefresh(refreshed.imTokenExpiresAt)
} else {
scheduleImTokenRefresh(getCurrentUserSigExpiresAt().takeIf { it > 0L })
}
} }
} }
fun logout() { fun logout() {
refreshJob?.cancel()
refreshJob = null
XuqmSDK.logout() XuqmSDK.logout()
prefs.edit().clear().apply() prefs.edit().clear().apply()
} }
private suspend fun refreshImToken() {
if (!refreshing.compareAndSet(false, true)) return
try {
withContext(Dispatchers.IO) {
val refreshed = refreshImTokenInternal() ?: run {
clearSession()
return@withContext
}
saveImCredential(refreshed)
XuqmSDK.login(
userId = getCurrentUserId() ?: return@withContext,
userSig = refreshed.userSig,
nickname = getCurrentNickname(),
avatar = getCurrentAvatar(),
)
scheduleImTokenRefresh(refreshed.imTokenExpiresAt)
}
} finally {
refreshing.set(false)
}
}
private fun clearSession() {
refreshJob?.cancel()
refreshJob = null
XuqmSDK.logout()
prefs.edit().clear().apply()
}
private suspend fun refreshImTokenInternal(): ImRefreshResult? {
val appId = DEMO_APP_ID
return runCatching {
val response = api.refreshImToken(appId)
requireNotNull(response.data) { response.message ?: "Refresh IM token failed" }
}.getOrNull()
}
private fun shouldRefreshImToken(): Boolean {
val expiresAt = getCurrentUserSigExpiresAt()
return expiresAt <= 0L || expiresAt - System.currentTimeMillis() <= REFRESH_GRACE_MS
}
companion object {
private const val REFRESH_GRACE_MS = 5 * 60 * 1000L
}
private fun <T> Result<T>.mapFailureMessage(transform: (Throwable) -> String): Result<T> = private fun <T> Result<T>.mapFailureMessage(transform: (Throwable) -> String): Result<T> =
fold( fold(
onSuccess = { Result.success(it) }, onSuccess = { Result.success(it) },

查看文件

@ -4,7 +4,4 @@ data class XuqmLoginSession(
val appKey: String, val appKey: String,
val userId: String, val userId: String,
val userSig: String, val userSig: String,
val nickname: String? = null,
val avatar: String? = null,
val expiresAt: Long? = null,
) )

查看文件

@ -2,7 +2,6 @@ package com.xuqm.sdk
import android.content.Context import android.content.Context
import com.xuqm.sdk.auth.TokenStore import com.xuqm.sdk.auth.TokenStore
import com.xuqm.sdk.auth.UserSigRefresher
import com.xuqm.sdk.core.LogLevel import com.xuqm.sdk.core.LogLevel
import com.xuqm.sdk.core.ServiceEndpointRegistry import com.xuqm.sdk.core.ServiceEndpointRegistry
import com.xuqm.sdk.core.ServiceEndpoints import com.xuqm.sdk.core.ServiceEndpoints
@ -26,8 +25,6 @@ object XuqmSDK {
@Volatile @Volatile
private var loginSession: XuqmLoginSession? = null private var loginSession: XuqmLoginSession? = null
private val userSigRefresher = UserSigRefresher()
fun initialize( fun initialize(
context: Context, context: Context,
appKey: String, appKey: String,
@ -63,31 +60,18 @@ object XuqmSDK {
val currentLoginSession: XuqmLoginSession? val currentLoginSession: XuqmLoginSession?
get() = loginSession get() = loginSession
fun setUserSigRefreshListener(listener: UserSigRefresher.UserSigRefreshListener?) {
userSigRefresher.setRefreshListener(listener)
}
suspend fun login( suspend fun login(
userId: String, userId: String,
userSig: String, userSig: String,
nickname: String? = null,
avatar: String? = null,
userSigExpiresAt: Long? = null,
): XuqmLoginSession = withContext(Dispatchers.IO) { ): XuqmLoginSession = withContext(Dispatchers.IO) {
requireInit() requireInit()
val session = XuqmLoginSession( val session = XuqmLoginSession(
appKey = appKey, appKey = appKey,
userId = userId, userId = userId,
userSig = userSig, userSig = userSig,
nickname = nickname,
avatar = avatar,
expiresAt = userSigExpiresAt,
) )
loginSession = session loginSession = session
tokenStore.saveToken(userSig) tokenStore.saveToken(userSig)
userSigExpiresAt?.let {
userSigRefresher.start(it)
}
notifyOptionalModules("onSdkLogin", session) notifyOptionalModules("onSdkLogin", session)
session session
} }
@ -96,7 +80,6 @@ object XuqmSDK {
val session = loginSession val session = loginSession
loginSession = null loginSession = null
tokenStore.clear() tokenStore.clear()
userSigRefresher.stop()
if (session != null) { if (session != null) {
notifyOptionalModulesSync("onSdkLogout") notifyOptionalModulesSync("onSdkLogout")
} }

查看文件

@ -85,28 +85,12 @@ object ImSDK {
XuqmSDK.currentLoginSession?.let { onSdkLogin(it) } XuqmSDK.currentLoginSession?.let { onSdkLogin(it) }
} }
suspend fun login( suspend fun login(userId: String, userSig: String) = withContext(Dispatchers.IO) {
userId: String,
userSig: String,
nickname: String? = null,
avatar: String? = null,
) = withContext(Dispatchers.IO) {
XuqmSDK.requireInit() XuqmSDK.requireInit()
currentUserId = userId currentUserId = userId
connectWithToken(userSig) connectWithToken(userSig)
} }
suspend fun loginWithUserSig(userId: String, userSig: String) =
withContext(Dispatchers.IO) {
XuqmSDK.requireInit()
currentUserId = userId
connectWithToken(userSig)
}
@Deprecated("Use loginWithUserSig(userId, userSig) instead.")
suspend fun loginWithToken(userId: String, token: String) =
loginWithUserSig(userId, token)
fun sendMessage( fun sendMessage(
toId: String, toId: String,
chatType: String, chatType: String,