diff --git a/README.md b/README.md index c54060c..9cf3d44 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ val service = RetrofitFactory.create(MyApiService::class.java) - 会话置顶/免打扰/已读/草稿/删除同步服务端 - IM 连接状态提示 - SDK 登录态恢复后自动重连 -- IM token 自动续签,过期前会静默刷新并重连 +- IM 登录态更新后自动重连,不在 SDK 侧做静默续签 - 群聊支持 `@userId` 提及,并写入 `mentionedUserIds` - 关系链支持好友申请、接受/拒绝、黑名单 - 支持图片 / 视频 / 音频 / 文件消息,文件通过独立文件服务上传后再发 IM diff --git a/TEST_REPORT.md b/TEST_REPORT.md index cf55eb9..53b0150 100644 --- a/TEST_REPORT.md +++ b/TEST_REPORT.md @@ -41,7 +41,7 @@ | 字段 | 内容 | |------|------| | **测试目的** | 验证 UserSig 鉴权模式下的登录与登出 | -| **测试步骤** | 1. 调用 `XuqmSDK.login(userId, userSig, nickname, avatar, userSigExpiresAt)`
2. 观察 `ImSDK.onSdkLogin` 是否自动触发 WebSocket 连接
3. 监听 `ImEventListener.onConnected()`
4. 调用 `XuqmSDK.logout()`
5. 确认 `ImSDK.onSdkLogout` 断开 WebSocket 并清空 Token | +| **测试步骤** | 1. 调用 `XuqmSDK.login(userId, userSig)`
2. 观察 `ImSDK.onSdkLogin` 是否自动触发 WebSocket 连接
3. 监听 `ImEventListener.onConnected()`
4. 调用 `XuqmSDK.logout()`
5. 确认 `ImSDK.onSdkLogout` 断开 WebSocket 并清空 Token | | **预期结果** | 1. 登录返回 `XuqmLoginSession`
2. WebSocket 建立 101 连接并 STOMP CONNECTED
3. `onConnected()` 回调触发
4. 登出后 `connectionState` 变为 `Disconnected`
5. `TokenStore` 被清空 | | **实际结果** | 通过 | | **通过状态** | ✅ | @@ -108,13 +108,13 @@ --- -### TC-08 UserSig 续签测试(新增) +### TC-08 UserSig 登录态更新测试(新增) | 字段 | 内容 | |------|------| -| **测试目的** | 验证 UserSig 即将过期时的静默续签回调机制 | -| **测试步骤** | 1. 登录时传入 `userSigExpiresAt`(如当前时间 + 6 分钟)
2. 设置 `XuqmSDK.setUserSigRefreshListener { ... }`
3. 等待 1 分钟后观察 `UserSigRefresher` 检查逻辑
4. 在回调中获取新 UserSig 并重新调用 `XuqmSDK.login()`
5. 验证旧定时器被停止,新定时器启动 | -| **预期结果** | 1. `UserSigRefresher.start(expiryTimeMs)` 启动
2. 到期前 5 分钟触发 `onUserSigRefreshRequired()`
3. 回调在主线程执行
4. 重新登录后 WebSocket 使用新 Token 连接
5. 无内存泄漏,定时器正确替换 | +| **测试目的** | 验证业务侧重新签发登录态后,SDK 能覆盖旧会话并重连 | +| **测试步骤** | 1. 登录后保持 WebSocket 连接
2. 业务服务端重新签发新的 UserSig
3. 重新调用 `XuqmSDK.login()`
4. 观察 `ImSDK.onSdkLogin` 与 `ImEventListener.onConnected()`
5. 确认旧会话被替换 | +| **预期结果** | 1. 新登录态生效
2. WebSocket 使用新登录态重连
3. 不存在 SDK 侧定时续签逻辑
4. 旧会话被覆盖
5. 无内存泄漏 | | **实际结果** | 待测试 | | **通过状态** | ⬜ | @@ -143,5 +143,5 @@ | TC-05 | 会话列表/置顶/静音测试 | ⬜ 待测试 | | TC-06 | Push 设备注册测试 | ⬜ 待测试 | | TC-07 | 版本更新检查测试 | ⬜ 待测试 | -| TC-08 | UserSig 续签测试 | ⬜ 待测试 | +| TC-08 | UserSig 登录态更新测试 | ⬜ 待测试 | | TC-09 | 多厂商 Push 检测测试 | ⬜ 待测试 | diff --git a/sample-app/src/main/java/com/xuqm/sdk/sample/data/api/DemoApi.kt b/sample-app/src/main/java/com/xuqm/sdk/sample/data/api/DemoApi.kt index ef19960..6d66280 100644 --- a/sample-app/src/main/java/com/xuqm/sdk/sample/data/api/DemoApi.kt +++ b/sample-app/src/main/java/com/xuqm/sdk/sample/data/api/DemoApi.kt @@ -6,7 +6,6 @@ import okhttp3.logging.HttpLoggingInterceptor import com.xuqm.sdk.sample.BuildConfig import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory -import com.google.gson.annotations.SerializedName import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST @@ -38,19 +37,10 @@ data class AuthProfile( data class AuthResult( val demoToken: String, - val demoTokenExpiresAt: Long? = null, - @SerializedName(value = "imToken", alternate = ["userSig"]) - val userSig: String? = null, - val imTokenExpiresAt: Long? = null, + val imToken: String, val profile: AuthProfile, ) -data class ImRefreshResult( - @SerializedName(value = "imToken", alternate = ["userSig"]) - val userSig: String, - val imTokenExpiresAt: Long? = null, -) - data class UserData( val userId: String, val nickname: String, @@ -74,9 +64,6 @@ interface DemoApi { @POST("api/demo/auth/register") suspend fun register(@Body request: RegisterRequest): DemoResponse - @POST("api/demo/auth/refresh-im") - suspend fun refreshImToken(@Query("appId") appId: String = DEMO_APP_ID): DemoResponse - @POST("api/demo/auth/reset-password") suspend fun resetPassword(@Body request: ResetPasswordRequest): DemoResponse diff --git a/sample-app/src/main/java/com/xuqm/sdk/sample/data/repo/AuthRepository.kt b/sample-app/src/main/java/com/xuqm/sdk/sample/data/repo/AuthRepository.kt index 75580fb..9fea73c 100644 --- a/sample-app/src/main/java/com/xuqm/sdk/sample/data/repo/AuthRepository.kt +++ b/sample-app/src/main/java/com/xuqm/sdk/sample/data/repo/AuthRepository.kt @@ -5,7 +5,6 @@ import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKeys import com.xuqm.sdk.XuqmSDK import com.xuqm.sdk.sample.data.api.AuthResult -import com.xuqm.sdk.sample.data.api.ImRefreshResult import com.xuqm.sdk.sample.data.api.ChangePasswordRequest import com.xuqm.sdk.sample.data.api.DEMO_APP_ID import com.xuqm.sdk.sample.data.api.DemoApi @@ -55,22 +54,19 @@ class AuthRepository(context: Context) { val demoToken = getDemoToken().orEmpty() val userId = getCurrentUserId().orEmpty() val userSig = getCurrentUserSig().orEmpty() - val demoTokenExpiresAt = prefs.getLong("demo_token_expires_at", 0L) return demoToken.isNotBlank() && userId.isNotBlank() && - userSig.isNotBlank() && - demoTokenExpiresAt > System.currentTimeMillis() + userSig.isNotBlank() } private fun saveSession(result: AuthResult) { val profile = result.profile prefs.edit() .putString("demo_token", result.demoToken) - .putLong("demo_token_expires_at", result.demoTokenExpiresAt ?: 0L) .putString("user_id", profile.userId) .putString("nickname", profile.nickname) .putString("avatar", profile.avatar) - .putString("user_sig", result.userSig) + .putString("user_sig", result.imToken) .apply() } @@ -80,7 +76,7 @@ class AuthRepository(context: Context) { val res = api.login(LoginRequest(DEMO_APP_ID, userId, password)) val data = requireNotNull(res.data) { res.message ?: "Login failed" } saveSession(data) - val userSig = requireNotNull(data.userSig?.takeIf { it.isNotBlank() }) { + val userSig = requireNotNull(data.imToken.takeIf { it.isNotBlank() }) { "Login succeeded but IM credential is missing" } XuqmSDK.login( @@ -98,7 +94,7 @@ class AuthRepository(context: Context) { val res = api.register(RegisterRequest(DEMO_APP_ID, userId, password, nickname)) val data = requireNotNull(res.data) { res.message ?: "Register failed" } saveSession(data) - val userSig = requireNotNull(data.userSig?.takeIf { it.isNotBlank() }) { + val userSig = requireNotNull(data.imToken.takeIf { it.isNotBlank() }) { "Register succeeded but IM credential is missing" } XuqmSDK.login(