feat(im): 添加即时消息SDK核心功能实现
- 实现了聊天消息发送功能,支持文本、图片、视频、音频、文件等多种消息类型 - 集成了文件上传下载功能,支持多媒体文件的传输和管理 - 添加了群组管理功能,包括创建群组、成员管理、权限控制等操作 - 实现了好友系统,支持好友添加、删除、分组等功能 - 集成了黑名单管理,提供用户屏蔽和解除屏蔽功能 - 添加了会话管理功能,支持对话列表、未读消息统计等 - 实现了历史消息查询和搜索功能 - 添加了实时连接状态管理和自动重连机制
这个提交包含在:
父节点
6e42862512
当前提交
f423a2acb2
@ -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) {
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户