docs(project): 更新需求与开发进度对比报告并完善Android SDK接口定义

- 添加了完整的XuqmGroup平台需求与开发进度对比报告
- 实现了Android SDK的ImApi接口定义,涵盖群组、好友、黑名单等完整功能
- 定义了IM消息、会话、群组、用户资料等核心数据模型
- 实现了Android SDK的ImSDK核心功能类,包括连接管理和消息处理
这个提交包含在:
XuqmGroup 2026-05-02 12:30:32 +08:00
父节点 3c7ae4d766
当前提交 293f9d6a96
共有 3 个文件被更改,包括 174 次插入0 次删除

查看文件

@ -4,9 +4,12 @@ import { ImDatabase } from './db/ImDatabase'
import type { MessageSearchParams } from './db/ImDatabase'
import type {
BlacklistEntry,
BlacklistCheckResult,
ChatType,
ConversationData,
ConversationGroupItem,
FriendRequest,
GroupReadReceiptSummary,
GroupJoinRequest,
ImEventListener,
ImGroup,
@ -103,6 +106,7 @@ function normalizeConversation(item: {
unreadCount: number
isMuted: boolean
isPinned: boolean
conversationGroup?: string | null
}): ConversationData {
return {
targetId: item.targetId,
@ -113,6 +117,7 @@ function normalizeConversation(item: {
unreadCount: item.unreadCount,
isMuted: item.isMuted,
isPinned: item.isPinned,
conversationGroup: item.conversationGroup ?? null,
}
}
@ -172,6 +177,15 @@ function setConversationPinnedMemory(targetId: string, chatType: ChatType, pinne
emitConversationMemory()
}
function setConversationGroupMemory(targetId: string, chatType: ChatType, groupName?: string | null): void {
conversationMemory = conversationMemory.map(item =>
item.targetId === targetId && item.chatType === chatType
? { ...item, conversationGroup: groupName ?? null }
: item,
)
emitConversationMemory()
}
function deleteConversationMemory(targetId: string, chatType: ChatType): void {
conversationMemory = conversationMemory.filter(
item => !(item.targetId === targetId && item.chatType === chatType),
@ -779,6 +793,27 @@ export const ImSDK = {
})
},
async transferGroupOwner(groupId: string, newOwnerId: string): Promise<ImGroup> {
return apiRequest<ImGroup>(`/api/im/groups/${encodeURIComponent(groupId)}/owner`, {
method: 'POST',
body: { newOwnerId },
})
},
async updateGroupAttributes(groupId: string, attributes: Record<string, unknown>): Promise<ImGroup> {
return apiRequest<ImGroup>(`/api/im/groups/${encodeURIComponent(groupId)}/attributes`, {
method: 'PUT',
body: attributes,
})
},
async removeGroupAttributes(groupId: string, keys: string[]): Promise<ImGroup> {
return apiRequest<ImGroup>(`/api/im/groups/${encodeURIComponent(groupId)}/attributes/delete`, {
method: 'POST',
body: { keys },
})
},
async dismissGroup(groupId: string): Promise<void> {
await apiRequest(`/api/im/groups/${encodeURIComponent(groupId)}`, { method: 'DELETE' })
},
@ -851,6 +886,42 @@ export const ImSDK = {
})
},
async removeAllFriends(): Promise<void> {
const config = getConfig()
await apiRequest('/api/im/friends', {
method: 'DELETE',
params: { appId: config.appId },
})
},
async setFriendGroup(friendId: string, groupName?: string): Promise<void> {
const config = getConfig()
await apiRequest(`/api/im/friends/${encodeURIComponent(friendId)}/group`, {
method: 'PUT',
params: {
appId: config.appId,
...(groupName ? { groupName } : {}),
},
})
},
async listFriendGroups(): Promise<string[]> {
const config = getConfig()
const res = await apiRequest<string[] | { content?: string[] }>('/api/im/friends/groups', {
params: { appId: config.appId },
})
return Array.isArray(res) ? res : (res.content ?? [])
},
async listFriendsByGroup(groupName: string): Promise<string[]> {
const config = getConfig()
const res = await apiRequest<string[] | { content?: string[] }>(
`/api/im/friends/groups/${encodeURIComponent(groupName)}`,
{ params: { appId: config.appId } },
)
return Array.isArray(res) ? res : (res.content ?? [])
},
async listFriendRequests(direction: 'incoming' | 'outgoing' = 'incoming'): Promise<FriendRequest[]> {
const config = getConfig()
const res = await apiRequest<FriendRequest[] | { content?: FriendRequest[] }>('/api/im/friend-requests', {
@ -914,6 +985,13 @@ export const ImSDK = {
})
},
async checkBlacklist(targetUserId: string): Promise<BlacklistCheckResult> {
const config = getConfig()
return apiRequest<BlacklistCheckResult>('/api/im/blacklist/check', {
params: { appId: config.appId, targetUserId },
})
},
async getProfile(userId: string): Promise<UserProfile> {
const config = getConfig()
return apiRequest<UserProfile>(`/api/im/accounts/${encodeURIComponent(userId)}`, {
@ -977,6 +1055,7 @@ export const ImSDK = {
unreadCount: model.unreadCount,
isMuted: model.isMuted,
isPinned: model.isPinned,
conversationGroup: null,
}))
conversationMemory = conversations
emitConversationMemory()
@ -1013,6 +1092,7 @@ export const ImSDK = {
unreadCount: c.unreadCount,
isMuted: c.isMuted,
isPinned: c.isPinned,
conversationGroup: null,
}))
callback(data)
})
@ -1074,6 +1154,64 @@ export const ImSDK = {
}
},
async setConversationHidden(targetId: string, chatType: ChatType, hidden: boolean): Promise<void> {
const config = getConfig()
await apiRequest(`/api/im/conversations/${encodeURIComponent(targetId)}/hidden`, {
method: 'PUT',
params: {
appId: config.appId,
chatType,
hidden: String(hidden),
},
})
if (hidden) {
deleteConversationMemory(targetId, chatType)
}
},
async setConversationGroup(targetId: string, chatType: ChatType, groupName?: string): Promise<void> {
const config = getConfig()
await apiRequest(`/api/im/conversations/${encodeURIComponent(targetId)}/group`, {
method: 'PUT',
params: {
appId: config.appId,
chatType,
...(groupName ? { groupName } : {}),
},
})
setConversationGroupMemory(targetId, chatType, groupName ?? null)
},
async listConversationGroups(): Promise<string[]> {
const config = getConfig()
const res = await apiRequest<string[] | { content?: string[] }>('/api/im/conversation-groups', {
params: { appId: config.appId },
})
return Array.isArray(res) ? res : (res.content ?? [])
},
async listConversationGroupItems(groupName: string): Promise<ConversationGroupItem[]> {
const config = getConfig()
const res = await apiRequest<ConversationGroupItem[] | { content?: ConversationGroupItem[] }>(
`/api/im/conversation-groups/${encodeURIComponent(groupName)}`,
{ params: { appId: config.appId } },
)
return Array.isArray(res) ? res : (res.content ?? [])
},
async adminGroupReadReceipts(groupId: string, messageIds: string[]): Promise<GroupReadReceiptSummary[]> {
const config = getConfig()
const res = await apiRequest<GroupReadReceiptSummary[] | { content?: GroupReadReceiptSummary[] }>(
`/api/im/admin/groups/${encodeURIComponent(groupId)}/read-receipts`,
{
method: 'POST',
params: { appId: config.appId },
body: { messageIds },
},
)
return Array.isArray(res) ? res : (res.content ?? [])
},
async getDraft(targetId: string, chatType: ChatType): Promise<string> {
const config = getConfig()
if (ImDatabase.isInitialized()) {

查看文件

@ -5,10 +5,19 @@ import { ImSDK as _ImSDK } from './ImSDK'
export const listFriends = (): Promise<string[]> => _ImSDK.listFriends()
export const addFriend = (friendId: string): Promise<void> => _ImSDK.addFriend(friendId)
export const removeFriend = (friendId: string): Promise<void> => _ImSDK.removeFriend(friendId)
export const removeAllFriends = (): Promise<void> => _ImSDK.removeAllFriends()
export const setFriendGroup = (friendId: string, groupName?: string): Promise<void> => _ImSDK.setFriendGroup(friendId, groupName)
export const listFriendGroups = (): ReturnType<typeof _ImSDK.listFriendGroups> => _ImSDK.listFriendGroups()
export const listFriendsByGroup = (groupName: string): ReturnType<typeof _ImSDK.listFriendsByGroup> => _ImSDK.listFriendsByGroup(groupName)
export const checkBlacklist = (targetUserId: string): ReturnType<typeof _ImSDK.checkBlacklist> => _ImSDK.checkBlacklist(targetUserId)
export const searchUsers = (keyword: string, size?: number): ReturnType<typeof _ImSDK.searchUsers> => _ImSDK.searchUsers(keyword, size)
export const searchGroups = (keyword: string, size?: number): ReturnType<typeof _ImSDK.searchGroups> => _ImSDK.searchGroups(keyword, size)
export const searchMessages = (params: Parameters<typeof _ImSDK.searchMessages>[0]): ReturnType<typeof _ImSDK.searchMessages> => _ImSDK.searchMessages(params)
export const editMessage = (messageId: string, content: string): ReturnType<typeof _ImSDK.editMessage> => _ImSDK.editMessage(messageId, content)
export const setConversationHidden = (targetId: string, chatType: Parameters<typeof _ImSDK.setConversationHidden>[1], hidden: boolean): ReturnType<typeof _ImSDK.setConversationHidden> => _ImSDK.setConversationHidden(targetId, chatType, hidden)
export const setConversationGroup = (targetId: string, chatType: Parameters<typeof _ImSDK.setConversationGroup>[1], groupName?: string): ReturnType<typeof _ImSDK.setConversationGroup> => _ImSDK.setConversationGroup(targetId, chatType, groupName)
export const listConversationGroups = (): ReturnType<typeof _ImSDK.listConversationGroups> => _ImSDK.listConversationGroups()
export const listConversationGroupItems = (groupName: string): ReturnType<typeof _ImSDK.listConversationGroupItems> => _ImSDK.listConversationGroupItems(groupName)
export const locateHistoryPage = (toId: string, messageId: string, pageSize?: number, maxPages?: number): ReturnType<typeof _ImSDK.locateHistoryPage> => _ImSDK.locateHistoryPage(toId, messageId, pageSize, maxPages)
export const locateGroupHistoryPage = (groupId: string, messageId: string, pageSize?: number, maxPages?: number): ReturnType<typeof _ImSDK.locateGroupHistoryPage> => _ImSDK.locateGroupHistoryPage(groupId, messageId, pageSize, maxPages)
export { ImClient } from './ImClient'
@ -18,11 +27,14 @@ export type {
ImMessage, ImGroup, ChatType, MsgType, MsgStatus,
ImEventListener, SendMessageParams,
ConversationData,
ConversationGroupItem,
HistoryQuery,
PageResult,
FriendRequest,
GroupJoinRequest,
BlacklistEntry,
BlacklistCheckResult,
GroupReadReceiptSummary,
UserProfile,
} from './types'
export { uploadFile } from './upload'

查看文件

@ -65,6 +65,8 @@ export interface ImGroup {
memberIds: string
adminIds: string
announcement?: string | null
memberInfo?: string | null
extAttributes?: string | null
createdAt: number
}
@ -77,6 +79,13 @@ export interface ConversationData {
unreadCount: number
isMuted: boolean
isPinned: boolean
conversationGroup?: string | null
}
export interface ConversationGroupItem {
targetId: string
chatType: ChatType
groupName: string
}
export interface HistoryQuery {
@ -130,6 +139,21 @@ export interface BlacklistEntry {
createdAt: number
}
export interface BlacklistCheckResult {
targetUserId: string
blockedByMe: boolean
blockedByTarget: boolean
eitherBlocked: boolean
}
export interface GroupReadReceiptSummary {
messageId: string
groupId: string
memberCount: number
readCount: number
unreadCount: number
}
export interface UserProfile {
id?: string
appId?: string