From 20fc57ac972ae95c4604f24388fb145c6650993a Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Sat, 2 May 2026 11:45:43 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20SDK=20API=20?= =?UTF-8?q?=E9=87=8D=E8=AE=BE=E8=AE=A1=E3=80=81=E5=AE=89=E5=85=A8=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=E8=A7=84=E8=8C=83=E5=92=8C=E6=B5=8B=E8=AF=95=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E8=B7=9F=E8=B8=AA=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 SDK API 重设计规范文档,统一各端 SDK 初始化、登录、消息接口 - 新增安全设计规范文档,涵盖密码安全、AppSecret 验证、令牌存储等安全要点 - 新增 Bug 跟踪记录文档,记录已修复问题和开放问题 - 新增测试进度跟踪文档,记录各模块测试覆盖情况和验证结果 --- README.md | 2 +- TEST_REPORT.md | 10 +- .../com/xuqm/sdk/auth/UserSigRefresher.kt | 93 ------------------- 3 files changed, 6 insertions(+), 99 deletions(-) delete mode 100644 sdk-core/src/main/java/com/xuqm/sdk/auth/UserSigRefresher.kt diff --git a/README.md b/README.md index 9cf3d44..6eeb74f 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ val service = RetrofitFactory.create(MyApiService::class.java) - 会话置顶/免打扰/已读/草稿/删除同步服务端 - IM 连接状态提示 - SDK 登录态恢复后自动重连 -- IM 登录态更新后自动重连,不在 SDK 侧做静默续签 +- 重复调用登录会覆盖当前 IM 会话并自动重连,SDK 侧不做生命周期检测或维护 - 群聊支持 `@userId` 提及,并写入 `mentionedUserIds` - 关系链支持好友申请、接受/拒绝、黑名单 - 支持图片 / 视频 / 音频 / 文件消息,文件通过独立文件服务上传后再发 IM diff --git a/TEST_REPORT.md b/TEST_REPORT.md index 53b0150..ba526c3 100644 --- a/TEST_REPORT.md +++ b/TEST_REPORT.md @@ -108,13 +108,13 @@ --- -### TC-08 UserSig 登录态更新测试(新增) +### TC-08 UserSig 匹配重登测试(新增) | 字段 | 内容 | |------|------| -| **测试目的** | 验证业务侧重新签发登录态后,SDK 能覆盖旧会话并重连 | -| **测试步骤** | 1. 登录后保持 WebSocket 连接
2. 业务服务端重新签发新的 UserSig
3. 重新调用 `XuqmSDK.login()`
4. 观察 `ImSDK.onSdkLogin` 与 `ImEventListener.onConnected()`
5. 确认旧会话被替换 | -| **预期结果** | 1. 新登录态生效
2. WebSocket 使用新登录态重连
3. 不存在 SDK 侧定时续签逻辑
4. 旧会话被覆盖
5. 无内存泄漏 | +| **测试目的** | 验证 `userId + userSig` 匹配时,SDK 能覆盖当前会话并重连 | +| **测试步骤** | 1. 登录后保持 WebSocket 连接
2. 使用同一 `userId` 与匹配的 UserSig 重新调用 `XuqmSDK.login()`
3. 观察 `ImSDK.onSdkLogin` 与 `ImEventListener.onConnected()`
4. 确认当前会话被替换 | +| **预期结果** | 1. 匹配的 UserSig 生效
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/sdk-core/src/main/java/com/xuqm/sdk/auth/UserSigRefresher.kt b/sdk-core/src/main/java/com/xuqm/sdk/auth/UserSigRefresher.kt deleted file mode 100644 index 6b26171..0000000 --- a/sdk-core/src/main/java/com/xuqm/sdk/auth/UserSigRefresher.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.xuqm.sdk.auth - -import android.os.Handler -import android.os.Looper -import android.util.Log -import java.util.concurrent.atomic.AtomicBoolean - -/** - * UserSig 静默续签定时器 - * - * 在 [login] 时启动,在 [logout] 时停止。 - * 当检测到 UserSig 距离过期不足 5 分钟时,触发 [UserSigRefreshListener.onUserSigRefreshRequired] 回调, - * 业务层应在回调中异步获取新的 userSig,然后调用 [com.xuqm.sdk.XuqmSDK.login] 重新登录以刷新定时器。 - */ -class UserSigRefresher { - - private val mainHandler = Handler(Looper.getMainLooper()) - private val isRunning = AtomicBoolean(false) - private var refreshListener: UserSigRefreshListener? = null - private var currentExpiryTimeMs: Long = 0L - - /** - * UserSig 续签监听器 - */ - fun interface UserSigRefreshListener { - /** - * 当 UserSig 即将过期时触发(到期前 5 分钟)。 - * 业务层应在此回调中异步获取新的 userSig,然后调用 XuqmSDK.login() 重新登录。 - */ - fun onUserSigRefreshRequired() - } - - /** - * 设置续签监听器 - */ - fun setRefreshListener(listener: UserSigRefreshListener?) { - this.refreshListener = listener - } - - /** - * 启动续签检测定时器 - * - * @param expiryTimeMs UserSig 过期时间戳(毫秒) - */ - fun start(expiryTimeMs: Long) { - stop() - currentExpiryTimeMs = expiryTimeMs - isRunning.set(true) - scheduleCheck() - } - - /** - * 停止续签检测定时器 - */ - fun stop() { - isRunning.set(false) - mainHandler.removeCallbacksAndMessages(null) - } - - private fun scheduleCheck() { - if (!isRunning.get()) return - - val now = System.currentTimeMillis() - val timeUntilExpiry = currentExpiryTimeMs - now - val timeUntilRefresh = timeUntilExpiry - REFRESH_THRESHOLD_MS - - if (timeUntilRefresh <= 0) { - notifyRefreshRequired() - if (isRunning.get()) { - mainHandler.postDelayed({ scheduleCheck() }, CHECK_INTERVAL_MS) - } - } else { - mainHandler.postDelayed( - { if (isRunning.get()) scheduleCheck() }, - timeUntilRefresh.coerceAtMost(CHECK_INTERVAL_MS), - ) - } - } - - private fun notifyRefreshRequired() { - runCatching { - refreshListener?.onUserSigRefreshRequired() - }.onFailure { error -> - Log.w(TAG, "UserSig refresh callback failed: ${error.message}") - } - } - - companion object { - private const val TAG = "UserSigRefresher" - private const val REFRESH_THRESHOLD_MS = 5 * 60 * 1000L // 到期前 5 分钟 - private const val CHECK_INTERVAL_MS = 60 * 1000L // 每分钟检查一次 - } -}