docs: 添加 SDK API 重设计、安全设计规范和测试进度跟踪文档
- 新增 SDK API 重设计规范文档,统一各端 SDK 初始化、登录、消息接口 - 新增安全设计规范文档,涵盖密码安全、AppSecret 验证、令牌存储等安全要点 - 新增 Bug 跟踪记录文档,记录已修复问题和开放问题 - 新增测试进度跟踪文档,记录各模块测试覆盖情况和验证结果
这个提交包含在:
父节点
72a03a65d6
当前提交
20fc57ac97
@ -148,7 +148,7 @@ val service = RetrofitFactory.create(MyApiService::class.java)
|
|||||||
- 会话置顶/免打扰/已读/草稿/删除同步服务端
|
- 会话置顶/免打扰/已读/草稿/删除同步服务端
|
||||||
- IM 连接状态提示
|
- IM 连接状态提示
|
||||||
- SDK 登录态恢复后自动重连
|
- SDK 登录态恢复后自动重连
|
||||||
- IM 登录态更新后自动重连,不在 SDK 侧做静默续签
|
- 重复调用登录会覆盖当前 IM 会话并自动重连,SDK 侧不做生命周期检测或维护
|
||||||
- 群聊支持 `@userId` 提及,并写入 `mentionedUserIds`
|
- 群聊支持 `@userId` 提及,并写入 `mentionedUserIds`
|
||||||
- 关系链支持好友申请、接受/拒绝、黑名单
|
- 关系链支持好友申请、接受/拒绝、黑名单
|
||||||
- 支持图片 / 视频 / 音频 / 文件消息,文件通过独立文件服务上传后再发 IM
|
- 支持图片 / 视频 / 音频 / 文件消息,文件通过独立文件服务上传后再发 IM
|
||||||
|
|||||||
@ -108,13 +108,13 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### TC-08 UserSig 登录态更新测试(新增)
|
### TC-08 UserSig 匹配重登测试(新增)
|
||||||
|
|
||||||
| 字段 | 内容 |
|
| 字段 | 内容 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| **测试目的** | 验证业务侧重新签发登录态后,SDK 能覆盖旧会话并重连 |
|
| **测试目的** | 验证 `userId + userSig` 匹配时,SDK 能覆盖当前会话并重连 |
|
||||||
| **测试步骤** | 1. 登录后保持 WebSocket 连接 <br> 2. 业务服务端重新签发新的 UserSig <br> 3. 重新调用 `XuqmSDK.login()` <br> 4. 观察 `ImSDK.onSdkLogin` 与 `ImEventListener.onConnected()` <br> 5. 确认旧会话被替换 |
|
| **测试步骤** | 1. 登录后保持 WebSocket 连接 <br> 2. 使用同一 `userId` 与匹配的 UserSig 重新调用 `XuqmSDK.login()` <br> 3. 观察 `ImSDK.onSdkLogin` 与 `ImEventListener.onConnected()` <br> 4. 确认当前会话被替换 |
|
||||||
| **预期结果** | 1. 新登录态生效 <br> 2. WebSocket 使用新登录态重连 <br> 3. 不存在 SDK 侧定时续签逻辑 <br> 4. 旧会话被覆盖 <br> 5. 无内存泄漏 |
|
| **预期结果** | 1. 匹配的 UserSig 生效 <br> 2. WebSocket 使用当前登录态重连 <br> 3. SDK 侧无生命周期检测或维护机制 <br> 4. 当前会话被覆盖 <br> 5. 无内存泄漏 |
|
||||||
| **实际结果** | 待测试 |
|
| **实际结果** | 待测试 |
|
||||||
| **通过状态** | ⬜ |
|
| **通过状态** | ⬜ |
|
||||||
|
|
||||||
@ -143,5 +143,5 @@
|
|||||||
| TC-05 | 会话列表/置顶/静音测试 | ⬜ 待测试 |
|
| TC-05 | 会话列表/置顶/静音测试 | ⬜ 待测试 |
|
||||||
| TC-06 | Push 设备注册测试 | ⬜ 待测试 |
|
| TC-06 | Push 设备注册测试 | ⬜ 待测试 |
|
||||||
| TC-07 | 版本更新检查测试 | ⬜ 待测试 |
|
| TC-07 | 版本更新检查测试 | ⬜ 待测试 |
|
||||||
| TC-08 | UserSig 登录态更新测试 | ⬜ 待测试 |
|
| TC-08 | UserSig 匹配重登测试 | ⬜ 待测试 |
|
||||||
| TC-09 | 多厂商 Push 检测测试 | ⬜ 待测试 |
|
| TC-09 | 多厂商 Push 检测测试 | ⬜ 待测试 |
|
||||||
|
|||||||
@ -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 // 每分钟检查一次
|
|
||||||
}
|
|
||||||
}
|
|
||||||
正在加载...
在新工单中引用
屏蔽一个用户