feat(im): 添加即时通讯SDK核心功能

- 实现IM API接口定义,包括消息、群组、好友、黑名单等功能
- 定义IM消息相关数据模型,包含聊天类型、消息类型、用户资料等
- 实现ImSDK单例类,提供登录、消息发送、群组管理、好友管理等核心功能
- 添加WebSocket连接管理,支持自动重连机制
- 实现历史消息查询、群组操作、用户资料管理等API调用
- 添加会话状态管理,支持置顶、静音、草稿等功能
- 集成文件上传结果,支持多媒体消息发送
- 实现连接状态监听和事件回调机制
这个提交包含在:
XuqmGroup 2026-04-28 21:05:06 +08:00
父节点 0425c988ae
当前提交 17168dcf4e
共有 6 个文件被更改,包括 68 次插入33 次删除

查看文件

@ -17,6 +17,8 @@ import com.xuqm.sdk.im.model.ConversationData
import com.xuqm.sdk.im.model.GroupJoinRequest import com.xuqm.sdk.im.model.GroupJoinRequest
import com.xuqm.sdk.im.model.ImGroup import com.xuqm.sdk.im.model.ImGroup
import com.xuqm.sdk.im.model.ImMessage import com.xuqm.sdk.im.model.ImMessage
import com.xuqm.sdk.im.model.PageResult
import com.xuqm.sdk.im.model.UserProfile
import com.xuqm.sdk.im.model.BlacklistEntry import com.xuqm.sdk.im.model.BlacklistEntry
import com.xuqm.sdk.im.model.FriendRequest import com.xuqm.sdk.im.model.FriendRequest
import com.xuqm.sdk.file.FileUploadResult import com.xuqm.sdk.file.FileUploadResult
@ -447,6 +449,34 @@ object ImSDK {
suspend fun listPublicGroups(keyword: String? = null): List<ImGroup> = suspend fun listPublicGroups(keyword: String? = null): List<ImGroup> =
withContext(Dispatchers.IO) { api.listPublicGroups(XuqmSDK.appId, keyword).data ?: emptyList() } withContext(Dispatchers.IO) { api.listPublicGroups(XuqmSDK.appId, keyword).data ?: emptyList() }
suspend fun searchUsers(keyword: String, size: Int = 20): List<UserProfile> =
withContext(Dispatchers.IO) { api.searchUsers(XuqmSDK.appId, keyword, size).data ?: emptyList() }
suspend fun searchGroups(keyword: String, size: Int = 20): List<ImGroup> =
withContext(Dispatchers.IO) { api.searchGroups(XuqmSDK.appId, keyword, size).data ?: emptyList() }
suspend fun searchMessages(
keyword: String? = null,
chatType: String? = null,
msgType: String? = null,
startTime: LocalDateTime? = null,
endTime: LocalDateTime? = null,
page: Int = 0,
size: Int = 20,
): PageResult<ImMessage> =
withContext(Dispatchers.IO) {
api.searchMessages(
XuqmSDK.appId,
keyword,
chatType,
msgType,
startTime?.toString(),
endTime?.toString(),
page,
size,
).data ?: PageResult()
}
suspend fun updateGroupInfo(groupId: String, name: String? = null, announcement: String? = null) = suspend fun updateGroupInfo(groupId: String, name: String? = null, announcement: String? = null) =
withContext(Dispatchers.IO) { api.updateGroupInfo(groupId, UpdateGroupRequest(name, announcement)) } withContext(Dispatchers.IO) { api.updateGroupInfo(groupId, UpdateGroupRequest(name, announcement)) }

查看文件

@ -83,6 +83,32 @@ interface ImApi {
@Query("keyword") keyword: String? = null, @Query("keyword") keyword: String? = null,
): ApiResponse<List<ImGroup>> ): ApiResponse<List<ImGroup>>
@GET("api/im/admin/users/search")
suspend fun searchUsers(
@Query("appId") appId: String,
@Query("keyword") keyword: String,
@Query("size") size: Int = 20,
): ApiResponse<List<UserProfile>>
@GET("api/im/admin/groups/search")
suspend fun searchGroups(
@Query("appId") appId: String,
@Query("keyword") keyword: String,
@Query("size") size: Int = 20,
): ApiResponse<List<ImGroup>>
@GET("api/im/admin/messages/search")
suspend fun searchMessages(
@Query("appId") appId: String,
@Query("keyword") keyword: String? = null,
@Query("chatType") chatType: String? = null,
@Query("msgType") msgType: String? = null,
@Query("startTime") startTime: String? = null,
@Query("endTime") endTime: String? = null,
@Query("page") page: Int = 0,
@Query("size") size: Int = 20,
): ApiResponse<PageResult<ImMessage>>
@POST("api/im/groups") @POST("api/im/groups")
suspend fun createGroup( suspend fun createGroup(
@Query("appId") appId: String, @Query("appId") appId: String,

查看文件

@ -2,6 +2,18 @@ package com.xuqm.sdk.im.model
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
data class PageResult<T>(
val content: List<T> = emptyList(),
val totalElements: Long = 0,
val totalPages: Int = 0,
val size: Int = 0,
val number: Int = 0,
val numberOfElements: Int = 0,
val first: Boolean = false,
val last: Boolean = false,
val empty: Boolean = true,
)
data class ImMessage( data class ImMessage(
val id: String, val id: String,
val appId: String, val appId: String,

查看文件

@ -9,7 +9,6 @@ import com.xuqm.sdk.file.FileSDK
import com.xuqm.sdk.core.ServiceEndpointRegistry import com.xuqm.sdk.core.ServiceEndpointRegistry
import com.xuqm.sdk.network.ApiClient import com.xuqm.sdk.network.ApiClient
import com.xuqm.sdk.update.api.UpdateApi import com.xuqm.sdk.update.api.UpdateApi
import com.xuqm.sdk.update.model.RnUpdateInfo
import com.xuqm.sdk.update.model.UpdateInfo import com.xuqm.sdk.update.model.UpdateInfo
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -22,10 +21,6 @@ object UpdateSDK {
private fun normalizeDownloadUrl(rawUrl: String?): String? { private fun normalizeDownloadUrl(rawUrl: String?): String? {
if (rawUrl.isNullOrBlank()) return rawUrl if (rawUrl.isNullOrBlank()) return rawUrl
if (rawUrl.contains("/api/v1/updates/api/v1/rn/files/")) {
return rawUrl.replace("/api/v1/updates/api/v1/rn/files/", "/api/v1/rn/files/")
}
return runCatching { return runCatching {
val uri = Uri.parse(rawUrl) val uri = Uri.parse(rawUrl)
if (uri.path?.startsWith("/files/apk/") == true) { if (uri.path?.startsWith("/files/apk/") == true) {
@ -65,14 +60,4 @@ object UpdateSDK {
} }
context.startActivity(intent) context.startActivity(intent)
} }
suspend fun checkRnUpdate(moduleId: String, currentVersion: String): RnUpdateInfo? =
withContext(Dispatchers.IO) {
XuqmSDK.requireInit()
runCatching {
api.checkRnUpdate(XuqmSDK.appId, moduleId, "ANDROID", currentVersion).data?.let {
it.copy(downloadUrl = normalizeDownloadUrl(it.downloadUrl) ?: it.downloadUrl)
}
}.getOrNull()
}
} }

查看文件

@ -1,7 +1,6 @@
package com.xuqm.sdk.update.api package com.xuqm.sdk.update.api
import com.xuqm.sdk.update.model.UpdateInfo import com.xuqm.sdk.update.model.UpdateInfo
import com.xuqm.sdk.update.model.RnUpdateInfo
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.Query import retrofit2.http.Query
@ -14,12 +13,4 @@ interface UpdateApi {
@Query("platform") platform: String, @Query("platform") platform: String,
@Query("currentVersionCode") currentVersionCode: Int, @Query("currentVersionCode") currentVersionCode: Int,
): ApiResponse<UpdateInfo> ): ApiResponse<UpdateInfo>
@GET("api/v1/rn/update/check")
suspend fun checkRnUpdate(
@Query("appId") appId: String,
@Query("moduleId") moduleId: String,
@Query("platform") platform: String,
@Query("currentVersion") currentVersion: String,
): ApiResponse<RnUpdateInfo>
} }

查看文件

@ -10,12 +10,3 @@ data class UpdateInfo(
val appStoreUrl: String = "", val appStoreUrl: String = "",
val marketUrl: String = "", val marketUrl: String = "",
) )
data class RnUpdateInfo(
val needsUpdate: Boolean,
val latestVersion: String = "",
val downloadUrl: String = "",
val md5: String = "",
val minCommonVersion: String = "0.0.0",
val note: String = "",
)