From cfd0382ba23063588f96f95beacdba6bd14f63c6 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Sat, 2 May 2026 12:30:31 +0800 Subject: [PATCH] =?UTF-8?q?docs(project):=20=E6=9B=B4=E6=96=B0=E9=9C=80?= =?UTF-8?q?=E6=B1=82=E4=B8=8E=E5=BC=80=E5=8F=91=E8=BF=9B=E5=BA=A6=E5=AF=B9?= =?UTF-8?q?=E6=AF=94=E6=8A=A5=E5=91=8A=E5=B9=B6=E5=AE=8C=E5=96=84Android?= =?UTF-8?q?=20SDK=E6=8E=A5=E5=8F=A3=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加了完整的XuqmGroup平台需求与开发进度对比报告 - 实现了Android SDK的ImApi接口定义,涵盖群组、好友、黑名单等完整功能 - 定义了IM消息、会话、群组、用户资料等核心数据模型 - 实现了Android SDK的ImSDK核心功能类,包括连接管理和消息处理 --- sdk-im/src/main/java/com/xuqm/sdk/im/ImSDK.kt | 57 ++++++++++- .../main/java/com/xuqm/sdk/im/api/ImApi.kt | 94 +++++++++++++++++-- .../java/com/xuqm/sdk/im/model/ImMessage.kt | 24 +++++ 3 files changed, 165 insertions(+), 10 deletions(-) diff --git a/sdk-im/src/main/java/com/xuqm/sdk/im/ImSDK.kt b/sdk-im/src/main/java/com/xuqm/sdk/im/ImSDK.kt index 4e2312a..ff6cf2f 100644 --- a/sdk-im/src/main/java/com/xuqm/sdk/im/ImSDK.kt +++ b/sdk-im/src/main/java/com/xuqm/sdk/im/ImSDK.kt @@ -4,18 +4,22 @@ import com.xuqm.sdk.XuqmLoginSession import com.xuqm.sdk.XuqmSDK import com.xuqm.sdk.core.ServiceEndpointRegistry import com.xuqm.sdk.im.api.AddMemberRequest +import com.xuqm.sdk.im.api.AttributeKeysRequest import com.xuqm.sdk.im.api.CreateGroupRequest +import com.xuqm.sdk.im.api.GroupReadReceiptRequest import com.xuqm.sdk.im.api.ImApi import com.xuqm.sdk.im.model.EditMessageRequest import com.xuqm.sdk.im.api.MuteGroupMemberRequest -import com.xuqm.sdk.im.api.SetMutedRequest -import com.xuqm.sdk.im.api.SetPinnedRequest import com.xuqm.sdk.im.api.SetGroupRoleRequest +import com.xuqm.sdk.im.api.TransferOwnerRequest import com.xuqm.sdk.im.api.UpdateGroupRequest import com.xuqm.sdk.im.listener.ImEventListener +import com.xuqm.sdk.im.model.BlacklistCheckResult +import com.xuqm.sdk.im.model.ConversationGroupItem import com.xuqm.sdk.im.model.ImConnectionState import com.xuqm.sdk.im.model.ConversationData import com.xuqm.sdk.im.model.GroupJoinRequest +import com.xuqm.sdk.im.model.GroupReadReceiptSummary import com.xuqm.sdk.im.model.ImGroup import com.xuqm.sdk.im.model.ImMessage import com.xuqm.sdk.im.model.PageResult @@ -534,6 +538,15 @@ object ImSDK { suspend fun muteGroupMember(groupId: String, userId: String, minutes: Long) = withContext(Dispatchers.IO) { api.muteGroupMember(groupId, MuteGroupMemberRequest(userId, minutes)) } + suspend fun transferGroupOwner(groupId: String, newOwnerId: String): ImGroup? = + withContext(Dispatchers.IO) { api.transferGroupOwner(groupId, TransferOwnerRequest(newOwnerId)).data } + + suspend fun updateGroupAttributes(groupId: String, attributes: Map): ImGroup? = + withContext(Dispatchers.IO) { api.updateGroupAttributes(groupId, attributes).data } + + suspend fun removeGroupAttributes(groupId: String, keys: List): ImGroup? = + withContext(Dispatchers.IO) { api.removeGroupAttributes(groupId, AttributeKeysRequest(keys)).data } + suspend fun dismissGroup(groupId: String) = withContext(Dispatchers.IO) { api.dismissGroup(groupId) } @@ -555,9 +568,21 @@ object ImSDK { suspend fun addFriend(friendId: String) = withContext(Dispatchers.IO) { api.addFriend(XuqmSDK.appKey, friendId) } + suspend fun removeAllFriends() = + withContext(Dispatchers.IO) { api.removeAllFriends(XuqmSDK.appKey) } + suspend fun removeFriend(friendId: String) = withContext(Dispatchers.IO) { api.removeFriend(friendId, XuqmSDK.appKey) } + suspend fun setFriendGroup(friendId: String, groupName: String? = null) = + withContext(Dispatchers.IO) { api.setFriendGroup(friendId, XuqmSDK.appKey, groupName) } + + suspend fun listFriendGroups(): List = + withContext(Dispatchers.IO) { api.listFriendGroups(XuqmSDK.appKey).data ?: emptyList() } + + suspend fun listFriendsByGroup(groupName: String): List = + withContext(Dispatchers.IO) { api.listFriendsByGroup(groupName, XuqmSDK.appKey).data ?: emptyList() } + suspend fun listFriendRequests(direction: String = "incoming"): List = withContext(Dispatchers.IO) { api.listFriendRequests(XuqmSDK.appKey, direction).data ?: emptyList() } @@ -579,6 +604,9 @@ object ImSDK { suspend fun removeFromBlacklist(blockedUserId: String) = withContext(Dispatchers.IO) { api.removeFromBlacklist(XuqmSDK.appKey, blockedUserId) } + suspend fun checkBlacklist(targetUserId: String): BlacklistCheckResult? = + withContext(Dispatchers.IO) { api.checkBlacklist(XuqmSDK.appKey, targetUserId).data } + suspend fun getProfile(userId: String) = withContext(Dispatchers.IO) { api.getProfile(userId, XuqmSDK.appKey).data } @@ -601,14 +629,30 @@ object ImSDK { suspend fun setConversationPinned(targetId: String, chatType: String, pinned: Boolean) = withContext(Dispatchers.IO) { - api.setConversationPinned(targetId, chatType, SetPinnedRequest(pinned)) + api.setConversationPinned(targetId, XuqmSDK.appKey, chatType, pinned) } suspend fun setConversationMuted(targetId: String, chatType: String, muted: Boolean) = withContext(Dispatchers.IO) { - api.setConversationMuted(targetId, chatType, SetMutedRequest(muted)) + api.setConversationMuted(targetId, XuqmSDK.appKey, chatType, muted) } + suspend fun setConversationHidden(targetId: String, chatType: String, hidden: Boolean) = + withContext(Dispatchers.IO) { + api.setConversationHidden(targetId, XuqmSDK.appKey, chatType, hidden) + } + + suspend fun setConversationGroup(targetId: String, chatType: String, groupName: String? = null) = + withContext(Dispatchers.IO) { + api.setConversationGroup(targetId, XuqmSDK.appKey, chatType, groupName) + } + + suspend fun listConversationGroups(): List = + withContext(Dispatchers.IO) { api.listConversationGroups(XuqmSDK.appKey).data ?: emptyList() } + + suspend fun listConversationGroupItems(groupName: String): List = + withContext(Dispatchers.IO) { api.listConversationGroupItems(groupName, XuqmSDK.appKey).data ?: emptyList() } + suspend fun markRead(targetId: String, chatType: String = "SINGLE") = withContext(Dispatchers.IO) { api.markRead(targetId, XuqmSDK.appKey, chatType) } @@ -618,6 +662,11 @@ object ImSDK { suspend fun deleteConversation(targetId: String, chatType: String) = withContext(Dispatchers.IO) { api.deleteConversation(targetId, XuqmSDK.appKey, chatType) } + suspend fun adminGroupReadReceipts(groupId: String, messageIds: List): List = + withContext(Dispatchers.IO) { + api.adminGroupReadReceipts(groupId, XuqmSDK.appKey, GroupReadReceiptRequest(messageIds)).data ?: emptyList() + } + fun addListener(listener: ImEventListener) { Log.d(TAG, "addListener listener=${listener.javaClass.name}") listeners.add(listener) diff --git a/sdk-im/src/main/java/com/xuqm/sdk/im/api/ImApi.kt b/sdk-im/src/main/java/com/xuqm/sdk/im/api/ImApi.kt index f994f78..c06d2ff 100644 --- a/sdk-im/src/main/java/com/xuqm/sdk/im/api/ImApi.kt +++ b/sdk-im/src/main/java/com/xuqm/sdk/im/api/ImApi.kt @@ -1,9 +1,12 @@ package com.xuqm.sdk.im.api import com.xuqm.sdk.im.model.ConversationData +import com.xuqm.sdk.im.model.BlacklistCheckResult import com.xuqm.sdk.im.model.BlacklistEntry +import com.xuqm.sdk.im.model.ConversationGroupItem import com.xuqm.sdk.im.model.EditMessageRequest import com.xuqm.sdk.im.model.FriendRequest +import com.xuqm.sdk.im.model.GroupReadReceiptSummary import com.xuqm.sdk.im.model.ImGroup import com.xuqm.sdk.im.model.GroupJoinRequest import com.xuqm.sdk.im.model.ImMessage @@ -39,14 +42,16 @@ data class UpdateGroupRequest(val name: String? = null, val announcement: String data class AddMemberRequest(val userId: String) -data class SetPinnedRequest(val pinned: Boolean) - -data class SetMutedRequest(val muted: Boolean) - data class SetGroupRoleRequest(val userId: String, val role: String) data class MuteGroupMemberRequest(val userId: String, val minutes: Long) +data class TransferOwnerRequest(val newOwnerId: String) + +data class AttributeKeysRequest(val keys: List) + +data class GroupReadReceiptRequest(val messageIds: List) + interface ImApi { @POST("api/im/auth/login") @@ -167,6 +172,24 @@ interface ImApi { @Body request: MuteGroupMemberRequest, ): ApiResponse + @POST("api/im/groups/{groupId}/owner") + suspend fun transferGroupOwner( + @Path("groupId") groupId: String, + @Body request: TransferOwnerRequest, + ): ApiResponse + + @PUT("api/im/groups/{groupId}/attributes") + suspend fun updateGroupAttributes( + @Path("groupId") groupId: String, + @Body attributes: Map, + ): ApiResponse + + @POST("api/im/groups/{groupId}/attributes/delete") + suspend fun removeGroupAttributes( + @Path("groupId") groupId: String, + @Body request: AttributeKeysRequest, + ): ApiResponse + @DELETE("api/im/groups/{groupId}") suspend fun dismissGroup(@Path("groupId") groupId: String): ApiResponse @@ -206,12 +229,31 @@ interface ImApi { @Query("friendId") friendId: String, ): ApiResponse + @DELETE("api/im/friends") + suspend fun removeAllFriends(@Query("appId") appId: String): ApiResponse + @DELETE("api/im/friends/{friendId}") suspend fun removeFriend( @Path("friendId") friendId: String, @Query("appId") appId: String, ): ApiResponse + @PUT("api/im/friends/{friendId}/group") + suspend fun setFriendGroup( + @Path("friendId") friendId: String, + @Query("appId") appId: String, + @Query("groupName") groupName: String? = null, + ): ApiResponse + + @GET("api/im/friends/groups") + suspend fun listFriendGroups(@Query("appId") appId: String): ApiResponse> + + @GET("api/im/friends/groups/{groupName}") + suspend fun listFriendsByGroup( + @Path("groupName") groupName: String, + @Query("appId") appId: String, + ): ApiResponse> + @GET("api/im/friend-requests") suspend fun listFriendRequests( @Query("appId") appId: String, @@ -252,6 +294,12 @@ interface ImApi { @Query("blockedUserId") blockedUserId: String, ): ApiResponse + @GET("api/im/blacklist/check") + suspend fun checkBlacklist( + @Query("appId") appId: String, + @Query("targetUserId") targetUserId: String, + ): ApiResponse + @GET("api/im/accounts/{userId}") suspend fun getProfile( @Path("userId") userId: String, @@ -273,17 +321,44 @@ interface ImApi { @PUT("api/im/conversations/{targetId}/pinned") suspend fun setConversationPinned( @Path("targetId") targetId: String, + @Query("appId") appId: String, @Query("chatType") chatType: String, - @Body request: SetPinnedRequest, + @Query("pinned") pinned: Boolean, ): ApiResponse @PUT("api/im/conversations/{targetId}/muted") suspend fun setConversationMuted( @Path("targetId") targetId: String, + @Query("appId") appId: String, @Query("chatType") chatType: String, - @Body request: SetMutedRequest, + @Query("muted") muted: Boolean, ): ApiResponse + @PUT("api/im/conversations/{targetId}/hidden") + suspend fun setConversationHidden( + @Path("targetId") targetId: String, + @Query("appId") appId: String, + @Query("chatType") chatType: String, + @Query("hidden") hidden: Boolean, + ): ApiResponse + + @PUT("api/im/conversations/{targetId}/group") + suspend fun setConversationGroup( + @Path("targetId") targetId: String, + @Query("appId") appId: String, + @Query("chatType") chatType: String, + @Query("groupName") groupName: String? = null, + ): ApiResponse + + @GET("api/im/conversation-groups") + suspend fun listConversationGroups(@Query("appId") appId: String): ApiResponse> + + @GET("api/im/conversation-groups/{groupName}") + suspend fun listConversationGroupItems( + @Path("groupName") groupName: String, + @Query("appId") appId: String, + ): ApiResponse> + @PUT("api/im/conversations/{targetId}/read") suspend fun markRead( @Path("targetId") targetId: String, @@ -318,4 +393,11 @@ interface ImApi { @Query("appId") appId: String, @Query("chatType") chatType: String, ): ApiResponse + + @POST("api/im/admin/groups/{groupId}/read-receipts") + suspend fun adminGroupReadReceipts( + @Path("groupId") groupId: String, + @Query("appId") appId: String, + @Body request: GroupReadReceiptRequest, + ): ApiResponse> } diff --git a/sdk-im/src/main/java/com/xuqm/sdk/im/model/ImMessage.kt b/sdk-im/src/main/java/com/xuqm/sdk/im/model/ImMessage.kt index aabaa60..52775a7 100644 --- a/sdk-im/src/main/java/com/xuqm/sdk/im/model/ImMessage.kt +++ b/sdk-im/src/main/java/com/xuqm/sdk/im/model/ImMessage.kt @@ -38,6 +38,7 @@ data class ImMessage( data class ConversationData( val targetId: String, val chatType: String, + val conversationGroup: String? = null, val lastMsgContent: String? = null, val lastMsgType: String? = null, val lastMsgTime: Long = 0, @@ -54,9 +55,17 @@ data class ImGroup( val memberIds: String, val adminIds: String, val announcement: String? = null, + val memberInfo: String? = null, + val extAttributes: String? = null, val createdAt: Long, ) +data class ConversationGroupItem( + val targetId: String, + val chatType: String, + val groupName: String, +) + data class UserProfile( val userId: String, val nickname: String, @@ -93,6 +102,21 @@ data class BlacklistEntry( val createdAt: Long, ) +data class BlacklistCheckResult( + val targetUserId: String, + val blockedByMe: Boolean, + val blockedByTarget: Boolean, + val eitherBlocked: Boolean, +) + +data class GroupReadReceiptSummary( + val messageId: String, + val groupId: String, + val memberCount: Int, + val readCount: Int, + val unreadCount: Int, +) + enum class ChatType { SINGLE, GROUP } enum class MsgType {