From fefd8d191b1b2826da9e4c6c47c1837e28b966de Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Wed, 29 Apr 2026 00:39:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(im-sdk):=20=E6=B7=BB=E5=8A=A0=20Flutter=20?= =?UTF-8?q?IM=20SDK=20=E5=92=8C=20Go=20=E6=9C=8D=E5=8A=A1=E7=AB=AF=20SDK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了完整的 Flutter IM SDK,支持消息发送、历史记录、会话管理、好友功能和群组操作 - 添加了 Go 服务端 SDK 骨架,包含登录、消息发送、撤回、编辑等核心功能 - 实现了认证和授权机制,支持 token 管理和请求签名验证 - 添加了会话列表、好友申请、群组管理和消息搜索功能 - 提供了用户资料管理、头像上传和账户导入导出功能 - 实现了消息撤回、编辑和已读状态管理功能 - 添加了群组加入申请、审批和成员管理功能 - 提供了消息历史分页查询和定位功能 - 实现了会话置顶、免打扰和草稿保存功能 - 添加了用户和群组搜索功能,支持关键词检索 --- src/im/api.ts | 8 ++++++++ src/im/useIm.ts | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/im/api.ts b/src/im/api.ts index de938e8..602feb6 100644 --- a/src/im/api.ts +++ b/src/im/api.ts @@ -187,6 +187,14 @@ export function getGroupInfo(groupId: string): Promise { return http.get(`/api/im/groups/${encodeURIComponent(groupId)}`, appQuery()) } +export function listGroupMembers(groupId: string): Promise { + return http.get(`/api/im/groups/${encodeURIComponent(groupId)}/members`, appQuery()) +} + +export function searchGroupMembers(groupId: string, keyword: string, size = 20): Promise { + return http.get(`/api/im/groups/${encodeURIComponent(groupId)}/members/search`, appQuery({ keyword, size })) +} + export function sendFriendRequest(toUserId: string, remark?: string): Promise { return http.post( '/api/im/friend-requests', diff --git a/src/im/useIm.ts b/src/im/useIm.ts index 2862d53..512602a 100644 --- a/src/im/useIm.ts +++ b/src/im/useIm.ts @@ -52,6 +52,20 @@ export function useIm() { messages.value = [...messages.value, message] } + function markMessageRevoked(msgId: string) { + const index = messages.value.findIndex((item) => item.id === msgId) + if (index < 0) return + const next = [...messages.value] + next[index] = { + ...next[index], + status: 'REVOKED', + msgType: 'REVOKED', + revoked: true, + content: '', + } + messages.value = next + } + async function refreshConversations() { const items = await listConversations() conversations.value = [...items].sort((a, b) => { @@ -60,6 +74,14 @@ export function useIm() { }) } + function markConversationRead(targetId: string, chatType: ChatType = 'SINGLE') { + conversations.value = conversations.value.map((item) => + item.targetId === targetId && item.chatType === chatType + ? { ...item, unreadCount: 0 } + : item, + ) + } + async function loadHistory(toId: string, query: HistoryQuery = {}): Promise> { return fetchHistory(toId, query) } @@ -91,6 +113,10 @@ export function useIm() { upsertMessage(msg) void refreshConversations().catch(() => {}) }) + im.on('revoke', ({ msgId }) => { + markMessageRevoked(msgId) + void refreshConversations().catch(() => {}) + }) im.on('error', (e) => { error.value = e }) im.connect() client.value = im @@ -105,11 +131,19 @@ export function useIm() { } function revoke(msgId: string) { - return revokeMessage(msgId) + return revokeMessage(msgId).then((message) => { + upsertMessage(message) + void refreshConversations().catch(() => {}) + return message + }) } function edit(msgId: string, content: string) { - return editMessage(msgId, content) + return editMessage(msgId, content).then((message) => { + upsertMessage(message) + void refreshConversations().catch(() => {}) + return message + }) } function disconnect() { @@ -119,6 +153,8 @@ export function useIm() { } function setConversationRead(targetId: string, chatType: ChatType = 'SINGLE') { + markConversationRead(targetId, chatType) + void refreshConversations().catch(() => {}) return markRead(targetId, chatType) }