All checks were successful
continuous-integration/drone/push Build is passing
324 lines
8.3 KiB
JavaScript
324 lines
8.3 KiB
JavaScript
/**
|
||
* 腾讯 IM SDK 封装
|
||
* 负责初始化、登录、收发消息
|
||
*/
|
||
import TencentCloudChat from '@tencentcloud/chat'
|
||
import request from './request'
|
||
|
||
let chat = null
|
||
let isReady = false
|
||
let onMessageCallback = null
|
||
|
||
/**
|
||
* 获取 IM 实例(单例)
|
||
*/
|
||
export function getChatInstance() {
|
||
return chat
|
||
}
|
||
|
||
/**
|
||
* 初始化并登录 IM
|
||
* @returns {Promise<{sdkAppId, userId}>}
|
||
*/
|
||
export async function initIM() {
|
||
// 从后端获取 UserSig
|
||
const res = await request({ url: '/api/im/usersig' })
|
||
const { sdkAppId, userId, userSig } = res
|
||
|
||
// 创建 SDK 实例
|
||
if (!chat) {
|
||
chat = TencentCloudChat.create({ SDKAppID: sdkAppId })
|
||
chat.setLogLevel(1) // Release 级别日志
|
||
|
||
// 监听 SDK 就绪
|
||
chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
|
||
console.log('[IM] SDK 就绪')
|
||
isReady = true
|
||
})
|
||
|
||
// 监听新消息
|
||
chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, (event) => {
|
||
const msgList = event.data
|
||
if (onMessageCallback) {
|
||
onMessageCallback(msgList)
|
||
}
|
||
})
|
||
|
||
// 监听被踢下线
|
||
chat.on(TencentCloudChat.EVENT.KICKED_OUT, () => {
|
||
console.log('[IM] 被踢下线')
|
||
isReady = false
|
||
})
|
||
}
|
||
|
||
// 登录
|
||
await chat.login({ userID: userId, userSig })
|
||
console.log('[IM] 登录成功:', userId)
|
||
|
||
// 同步用户资料(昵称+头像)到 IM
|
||
try {
|
||
const userInfo = JSON.parse(uni.getStorageSync('userInfo') || '{}')
|
||
if (userInfo.nickname || userInfo.avatarUrl) {
|
||
const profileData = {}
|
||
if (userInfo.nickname) profileData.nick = userInfo.nickname
|
||
if (userInfo.avatarUrl) profileData.avatar = userInfo.avatarUrl
|
||
await chat.updateMyProfile(profileData)
|
||
console.log('[IM] 用户资料已同步')
|
||
}
|
||
} catch (e) {
|
||
console.warn('[IM] 同步用户资料失败:', e)
|
||
}
|
||
|
||
return { sdkAppId, userId }
|
||
}
|
||
|
||
/**
|
||
* 登出 IM
|
||
*/
|
||
export async function logoutIM() {
|
||
if (chat) {
|
||
await chat.logout()
|
||
isReady = false
|
||
console.log('[IM] 已登出')
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 设置新消息回调
|
||
* @param {Function} callback - 接收消息列表的回调
|
||
*/
|
||
export function onNewMessage(callback) {
|
||
onMessageCallback = callback
|
||
}
|
||
|
||
/**
|
||
* 移除新消息回调
|
||
*/
|
||
export function offNewMessage() {
|
||
onMessageCallback = null
|
||
}
|
||
|
||
/**
|
||
* 获取会话 ID(C2C 单聊)
|
||
* @param {string} targetUserId - 对方用户 ID(如 user_123)
|
||
*/
|
||
export function getConversationId(targetUserId) {
|
||
return `C2C${targetUserId}`
|
||
}
|
||
|
||
/**
|
||
* 获取会话列表(用于消息页展示聊天记录)
|
||
* @returns {Promise<Array>} 会话列表
|
||
*/
|
||
export async function getConversationList() {
|
||
if (!chat || !isReady) return []
|
||
try {
|
||
const res = await chat.getConversationList()
|
||
const list = res.data.conversationList || []
|
||
// 只返回 C2C 单聊会话
|
||
return list
|
||
.filter(c => c.type === TencentCloudChat.TYPES.CONV_C2C)
|
||
.map(c => {
|
||
const lastMsg = c.lastMessage
|
||
let lastMessageText = ''
|
||
if (lastMsg) {
|
||
if (lastMsg.type === TencentCloudChat.TYPES.MSG_TEXT) {
|
||
lastMessageText = lastMsg.payload?.text || ''
|
||
} else if (lastMsg.type === TencentCloudChat.TYPES.MSG_IMAGE) {
|
||
lastMessageText = '[图片]'
|
||
} else if (lastMsg.type === TencentCloudChat.TYPES.MSG_CUSTOM) {
|
||
lastMessageText = lastMsg.payload?.description || '[自定义消息]'
|
||
} else {
|
||
lastMessageText = '[消息]'
|
||
}
|
||
}
|
||
return {
|
||
conversationID: c.conversationID,
|
||
targetUserId: c.userProfile?.userID || c.conversationID.replace('C2C', ''),
|
||
nickname: c.userProfile?.nick || c.conversationID.replace('C2C', ''),
|
||
avatarUrl: c.userProfile?.avatar || '',
|
||
lastMessage: lastMessageText,
|
||
lastMessageTime: lastMsg ? lastMsg.lastTime * 1000 : 0,
|
||
unreadCount: c.unreadCount || 0
|
||
}
|
||
})
|
||
.sort((a, b) => b.lastMessageTime - a.lastMessageTime)
|
||
} catch (e) {
|
||
console.error('[IM] 获取会话列表失败:', e)
|
||
return []
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 拉取历史消息
|
||
* @param {string} targetUserId - 对方用户 ID
|
||
* @param {string} [nextReqMessageID] - 分页标记
|
||
* @returns {Promise<{messageList, isCompleted, nextReqMessageID}>}
|
||
*/
|
||
export async function getMessageList(targetUserId, nextReqMessageID) {
|
||
const conversationID = getConversationId(targetUserId)
|
||
const res = await chat.getMessageList({
|
||
conversationID,
|
||
nextReqMessageID,
|
||
count: 20
|
||
})
|
||
return {
|
||
messageList: res.data.messageList || [],
|
||
isCompleted: res.data.isCompleted,
|
||
nextReqMessageID: res.data.nextReqMessageID
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 发送文本消息
|
||
* @param {string} targetUserId - 对方用户 ID
|
||
* @param {string} text - 文本内容
|
||
* @param {string|number} [orderId] - 关联订单ID
|
||
*/
|
||
export async function sendTextMessage(targetUserId, text, orderId) {
|
||
const message = chat.createTextMessage({
|
||
to: targetUserId,
|
||
conversationType: TencentCloudChat.TYPES.CONV_C2C,
|
||
payload: { text }
|
||
})
|
||
if (orderId) message.cloudCustomData = JSON.stringify({ orderId: String(orderId) })
|
||
const res = await chat.sendMessage(message)
|
||
return res.data.message
|
||
}
|
||
|
||
/**
|
||
* 发送图片消息
|
||
* @param {string} targetUserId - 对方用户 ID
|
||
* @param {string} filePath - 本地图片路径
|
||
* @param {string|number} [orderId] - 关联订单ID
|
||
*/
|
||
export async function sendImageMessage(targetUserId, filePath, orderId) {
|
||
const message = chat.createImageMessage({
|
||
to: targetUserId,
|
||
conversationType: TencentCloudChat.TYPES.CONV_C2C,
|
||
payload: { file: { tempFilePaths: [filePath] } }
|
||
})
|
||
if (orderId) message.cloudCustomData = JSON.stringify({ orderId: String(orderId) })
|
||
const res = await chat.sendMessage(message)
|
||
return res.data.message
|
||
}
|
||
|
||
/**
|
||
* 发送自定义消息(改价申请等)
|
||
* @param {string} targetUserId - 对方用户 ID
|
||
* @param {Object} customData - 自定义数据
|
||
* @param {string|number} [orderId] - 关联订单ID
|
||
*/
|
||
export async function sendCustomMessage(targetUserId, customData, orderId) {
|
||
const message = chat.createCustomMessage({
|
||
to: targetUserId,
|
||
conversationType: TencentCloudChat.TYPES.CONV_C2C,
|
||
payload: {
|
||
data: JSON.stringify(customData),
|
||
description: customData.description || '',
|
||
extension: customData.extension || ''
|
||
}
|
||
})
|
||
if (orderId) message.cloudCustomData = JSON.stringify({ orderId: String(orderId) })
|
||
const res = await chat.sendMessage(message)
|
||
return res.data.message
|
||
}
|
||
|
||
/**
|
||
* 将 IM 消息转换为聊天页展示格式
|
||
* @param {Object} msg - IM SDK 消息对象
|
||
* @param {string} currentImUserId - 当前用户 IM ID
|
||
* @returns {Object} 聊天页消息格式
|
||
*/
|
||
export function formatIMMessage(msg, currentImUserId) {
|
||
const isSelf = msg.from === currentImUserId
|
||
const avatar = msg.avatar || '/static/logo.png'
|
||
|
||
// 提取消息关联的订单ID
|
||
let msgOrderId = null
|
||
if (msg.cloudCustomData) {
|
||
try {
|
||
const cd = JSON.parse(msg.cloudCustomData)
|
||
msgOrderId = cd.orderId || null
|
||
} catch (e) {}
|
||
}
|
||
|
||
// 文本消息
|
||
if (msg.type === TencentCloudChat.TYPES.MSG_TEXT) {
|
||
return {
|
||
id: msg.ID,
|
||
type: 'text',
|
||
content: msg.payload.text,
|
||
isSelf,
|
||
avatar,
|
||
time: msg.time * 1000,
|
||
orderId: msgOrderId
|
||
}
|
||
}
|
||
|
||
// 图片消息
|
||
if (msg.type === TencentCloudChat.TYPES.MSG_IMAGE) {
|
||
const imageInfo = msg.payload.imageInfoArray?.[1] || msg.payload.imageInfoArray?.[0]
|
||
return {
|
||
id: msg.ID,
|
||
type: 'image',
|
||
content: imageInfo?.url || '',
|
||
isSelf,
|
||
avatar,
|
||
time: msg.time * 1000,
|
||
orderId: msgOrderId
|
||
}
|
||
}
|
||
|
||
// 自定义消息(改价、订单状态等)
|
||
if (msg.type === TencentCloudChat.TYPES.MSG_CUSTOM) {
|
||
try {
|
||
const data = JSON.parse(msg.payload.data)
|
||
if (data.bizType === 'price-change') {
|
||
return {
|
||
id: msg.ID,
|
||
type: 'price-change',
|
||
isSelf,
|
||
priceChangeId: data.priceChangeId,
|
||
changeTypeLabel: data.changeTypeLabel,
|
||
originalPrice: data.originalPrice,
|
||
newPrice: data.newPrice,
|
||
difference: data.difference,
|
||
status: data.status,
|
||
time: msg.time * 1000,
|
||
orderId: msgOrderId
|
||
}
|
||
}
|
||
if (data.bizType === 'order-status') {
|
||
return {
|
||
id: msg.ID,
|
||
type: 'system',
|
||
content: data.description || '[订单状态变更]',
|
||
time: msg.time * 1000,
|
||
orderId: msgOrderId
|
||
}
|
||
}
|
||
} catch (e) {}
|
||
return {
|
||
id: msg.ID,
|
||
type: 'text',
|
||
content: msg.payload.description || '[自定义消息]',
|
||
isSelf,
|
||
avatar,
|
||
time: msg.time * 1000,
|
||
orderId: msgOrderId
|
||
}
|
||
}
|
||
|
||
// 其他类型
|
||
return {
|
||
id: msg.ID,
|
||
type: 'text',
|
||
content: '[暂不支持的消息类型]',
|
||
isSelf,
|
||
avatar,
|
||
time: msg.time * 1000,
|
||
orderId: msgOrderId
|
||
}
|
||
}
|