feat(im): 添加即时消息SDK核心功能实现

- 实现了聊天消息发送功能,支持文本、图片、视频、音频、文件等多种消息类型
- 集成了文件上传下载功能,支持多媒体文件的传输和管理
- 添加了群组管理功能,包括创建群组、成员管理、权限控制等操作
- 实现了好友系统,支持好友添加、删除、分组等功能
- 集成了黑名单管理,提供用户屏蔽和解除屏蔽功能
- 添加了会话管理功能,支持对话列表、未读消息统计等
- 实现了历史消息查询和搜索功能
- 添加了实时连接状态管理和自动重连机制
这个提交包含在:
XuqmGroup 2026-05-03 00:11:05 +08:00
父节点 6e42862512
当前提交 f423a2acb2
共有 2 个文件被更改,包括 79 次插入2 次删除

查看文件

@ -1,6 +1,5 @@
export interface SDKConfig {
appKey: string
appSecret: string
debug: boolean
}

查看文件

@ -1,7 +1,8 @@
import webSocket from '@ohos.net.webSocket'
import http from '@ohos.net.http'
import { HttpClient } from '../core/HttpClient'
import { SDKContext } from '../core/SDKContext'
import { DEFAULT_IM_WS_URL } from '../core/Endpoints'
import { DEFAULT_API_BASE_URL, DEFAULT_IM_WS_URL } from '../core/Endpoints'
import type {
ChatType,
ConversationData,
@ -23,6 +24,7 @@ export interface ImEventDelegate {
onRead?(msg: ImMessage): void
onRevoke?(data: RevokeData): void
onError?(message: string): void
onConversationsChange?(conversations: ConversationData[]): void
}
export interface RevokeData {
@ -155,8 +157,10 @@ export class ImClient {
this.delegate?.onRevoke?.({ msgId: message.id, operatorId: message.fromId })
}
this.delegate?.onMessage?.(message)
this.notifyConversations()
} else if (frame.type === 'REVOKE') {
this.delegate?.onRevoke?.(frame.payload as RevokeData)
this.notifyConversations()
}
} catch {
// ignore malformed frames
@ -705,6 +709,80 @@ export class ImClient {
return parts.join('&')
}
async sendImageMessage(toId: string, chatType: ChatType, filePath: string, width?: number, height?: number): Promise<ImMessage> {
const uploadResult = await this.uploadFile(filePath)
const url = (uploadResult as Record<string, string>)['url'] ?? ''
const thumbnailUrl = (uploadResult as Record<string, string>)['thumbnailUrl']
const contentObj: Record<string, unknown> = { url }
if (width !== undefined) contentObj.width = width
if (height !== undefined) contentObj.height = height
if (thumbnailUrl) contentObj.thumbnailUrl = thumbnailUrl
return this.send({ toId, chatType, msgType: 'IMAGE', content: JSON.stringify(contentObj) })
}
async sendVideoMessage(toId: string, chatType: ChatType, filePath: string, width?: number, height?: number, duration?: number): Promise<ImMessage> {
const uploadResult = await this.uploadFile(filePath)
const url = (uploadResult as Record<string, string>)['url'] ?? ''
const thumbnailUrl = (uploadResult as Record<string, string>)['thumbnailUrl']
const contentObj: Record<string, unknown> = { url }
if (width !== undefined) contentObj.width = width
if (height !== undefined) contentObj.height = height
if (duration !== undefined) contentObj.duration = duration
if (thumbnailUrl) contentObj.thumbnailUrl = thumbnailUrl
return this.send({ toId, chatType, msgType: 'VIDEO', content: JSON.stringify(contentObj) })
}
async sendFileMessage(toId: string, chatType: ChatType, filePath: string): Promise<ImMessage> {
const uploadResult = await this.uploadFile(filePath)
const url = (uploadResult as Record<string, string>)['url'] ?? ''
const name = (uploadResult as Record<string, string>)['originalName'] ?? ''
const size = (uploadResult as Record<string, number>)['size'] ?? 0
const contentObj: Record<string, unknown> = { url, name, size }
return this.send({ toId, chatType, msgType: 'FILE', content: JSON.stringify(contentObj) })
}
async sendAudioMessage(toId: string, chatType: ChatType, filePath: string, duration?: number): Promise<ImMessage> {
const uploadResult = await this.uploadFile(filePath)
const url = (uploadResult as Record<string, string>)['url'] ?? ''
const contentObj: Record<string, unknown> = { url }
if (duration !== undefined) contentObj.duration = duration
return this.send({ toId, chatType, msgType: 'AUDIO', content: JSON.stringify(contentObj) })
}
private async uploadFile(filePath: string): Promise<Object> {
const token = SDKContext.getToken()
const url = DEFAULT_API_BASE_URL.replace(/\/$/, '') + '/api/file/upload'
const client = http.createHttp()
try {
const header: Record<string, string> = {}
if (token) header.Authorization = 'Bearer ' + token
const multipartForm = new http.MultipartForm()
multipartForm.addFile('file', filePath, filePath.substring(filePath.lastIndexOf('/') + 1))
const options: http.HttpRequestOptions = {
method: http.RequestMethod.POST,
header,
extraData: multipartForm,
connectTimeout: 60000,
readTimeout: 60000,
}
const res = await client.request(url, options)
const json = JSON.parse(res.result as string) as { code: number; message: string; data: Object }
if (json.code !== 200) throw new Error(json.message)
return json.data
} finally {
client.destroy()
}
}
private async notifyConversations(): Promise<void> {
try {
const conversations = await this.listConversations()
this.delegate?.onConversationsChange?.(conversations)
} catch {
// ignore
}
}
disconnect(): void {
this.destroyed = true
if (this.reconnectTimer !== null) {