All checks were successful
continuous-integration/drone/push Build is passing
253 lines
6.8 KiB
JavaScript
253 lines
6.8 KiB
JavaScript
/**
|
|
* 腾讯 IM SDK 封装(群聊模式)
|
|
* 每个订单一个群,消息按订单隔离
|
|
*/
|
|
import TencentCloudChat from '@tencentcloud/chat'
|
|
import TIMUploadPlugin from 'tim-upload-plugin'
|
|
import request from './request'
|
|
|
|
let chat = null
|
|
let isReady = false
|
|
let onMessageCallback = null
|
|
|
|
/** 获取 IM 实例 */
|
|
export function getChatInstance() {
|
|
return chat
|
|
}
|
|
|
|
/** 初始化并登录 IM */
|
|
export async function initIM() {
|
|
const res = await request({ url: '/api/im/usersig' })
|
|
const { sdkAppId, userId, userSig } = res
|
|
|
|
if (!chat) {
|
|
chat = TencentCloudChat.create({ SDKAppID: sdkAppId })
|
|
chat.registerPlugin({ 'tim-upload-plugin': TIMUploadPlugin })
|
|
chat.setLogLevel(1)
|
|
|
|
chat.on(TencentCloudChat.EVENT.SDK_READY, () => {
|
|
console.log('[IM] SDK 就绪')
|
|
isReady = true
|
|
// SDK就绪后同步用户头像和昵称到腾讯 IM
|
|
try {
|
|
const userInfo = uni.getStorageSync('userInfo')
|
|
if (userInfo) {
|
|
const parsed = typeof userInfo === 'string' ? JSON.parse(userInfo) : userInfo
|
|
chat.updateMyProfile({
|
|
nick: parsed.nickname || '',
|
|
avatar: parsed.avatarUrl || ''
|
|
})
|
|
}
|
|
} catch (e) {
|
|
console.warn('[IM] 同步资料失败:', e)
|
|
}
|
|
})
|
|
|
|
chat.on(TencentCloudChat.EVENT.MESSAGE_RECEIVED, (event) => {
|
|
if (onMessageCallback) onMessageCallback(event.data)
|
|
})
|
|
|
|
chat.on(TencentCloudChat.EVENT.KICKED_OUT, () => {
|
|
console.log('[IM] 被踢下线')
|
|
isReady = false
|
|
})
|
|
}
|
|
|
|
await chat.login({ userID: userId, userSig })
|
|
console.log('[IM] 登录成功:', userId)
|
|
return { sdkAppId, userId }
|
|
}
|
|
|
|
/** 登出 IM */
|
|
export async function logoutIM() {
|
|
if (chat) {
|
|
await chat.logout()
|
|
isReady = false
|
|
}
|
|
}
|
|
|
|
/** 设置新消息回调 */
|
|
export function onNewMessage(callback) {
|
|
onMessageCallback = callback
|
|
}
|
|
|
|
/** 移除新消息回调 */
|
|
export function offNewMessage() {
|
|
onMessageCallback = null
|
|
}
|
|
|
|
/** 获取群会话 ID */
|
|
export function getGroupConversationId(groupId) {
|
|
return `GROUP${groupId}`
|
|
}
|
|
|
|
/** 获取会话列表(群聊模式) */
|
|
export async function getConversationList() {
|
|
if (!chat || !isReady) return []
|
|
try {
|
|
const res = await chat.getConversationList()
|
|
const list = res.data.conversationList || []
|
|
return list
|
|
.filter(c => c.type === TencentCloudChat.TYPES.CONV_GROUP)
|
|
.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,
|
|
groupId: c.groupProfile?.groupID || c.conversationID.replace('GROUP', ''),
|
|
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 []
|
|
}
|
|
}
|
|
|
|
/** 拉取群历史消息 */
|
|
export async function getMessageList(groupId, nextReqMessageID) {
|
|
const conversationID = getGroupConversationId(groupId)
|
|
const res = await chat.getMessageList({
|
|
conversationID,
|
|
nextReqMessageID,
|
|
count: 20
|
|
})
|
|
return {
|
|
messageList: res.data.messageList || [],
|
|
isCompleted: res.data.isCompleted,
|
|
nextReqMessageID: res.data.nextReqMessageID
|
|
}
|
|
}
|
|
|
|
/** 标记群会话已读 */
|
|
export async function setMessageRead(groupId) {
|
|
if (!chat || !isReady) return
|
|
try {
|
|
const conversationID = getGroupConversationId(groupId)
|
|
await chat.setMessageRead({ conversationID })
|
|
} catch (e) {
|
|
console.error('[IM] 标记已读失败:', e)
|
|
}
|
|
}
|
|
|
|
/** 发送群文本消息 */
|
|
export async function sendTextMessage(groupId, text) {
|
|
const message = chat.createTextMessage({
|
|
to: groupId,
|
|
conversationType: TencentCloudChat.TYPES.CONV_GROUP,
|
|
payload: { text }
|
|
})
|
|
const res = await chat.sendMessage(message)
|
|
return res.data.message
|
|
}
|
|
|
|
/** 发送群图片消息 */
|
|
export async function sendImageMessage(groupId, fileRes) {
|
|
const message = chat.createImageMessage({
|
|
to: groupId,
|
|
conversationType: TencentCloudChat.TYPES.CONV_GROUP,
|
|
payload: { file: fileRes }
|
|
})
|
|
const res = await chat.sendMessage(message)
|
|
return res.data.message
|
|
}
|
|
|
|
/** 发送群自定义消息 */
|
|
export async function sendCustomMessage(groupId, customData) {
|
|
const message = chat.createCustomMessage({
|
|
to: groupId,
|
|
conversationType: TencentCloudChat.TYPES.CONV_GROUP,
|
|
payload: {
|
|
data: JSON.stringify(customData),
|
|
description: customData.description || '',
|
|
extension: customData.extension || ''
|
|
}
|
|
})
|
|
const res = await chat.sendMessage(message)
|
|
return res.data.message
|
|
}
|
|
|
|
/** 将 IM 消息转换为聊天页展示格式 */
|
|
export function formatIMMessage(msg, currentImUserId, avatarMap = {}) {
|
|
const isSelf = msg.from === currentImUserId
|
|
const avatar = avatarMap[msg.from] || msg.avatar || '/static/logo.png'
|
|
|
|
// 文本消息
|
|
if (msg.type === TencentCloudChat.TYPES.MSG_TEXT) {
|
|
return {
|
|
id: msg.ID, type: 'text', content: msg.payload.text,
|
|
isSelf, avatar, time: msg.time * 1000
|
|
}
|
|
}
|
|
|
|
// 图片消息
|
|
if (msg.type === TencentCloudChat.TYPES.MSG_IMAGE) {
|
|
const imgArr = msg.payload.imageInfoArray || []
|
|
const displayUrl = imgArr[1]?.url || imgArr[0]?.url || ''
|
|
const originUrl = imgArr[0]?.url || imgArr[1]?.url || ''
|
|
return {
|
|
id: msg.ID, type: 'image', content: displayUrl, originUrl,
|
|
isSelf, avatar, time: msg.time * 1000
|
|
}
|
|
}
|
|
|
|
// 自定义消息
|
|
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
|
|
}
|
|
}
|
|
if (data.bizType === 'price-change-response') {
|
|
return {
|
|
id: msg.ID, type: 'price-change-response',
|
|
priceChangeId: data.priceChangeId,
|
|
action: data.action, status: data.status,
|
|
changeTypeLabel: data.changeTypeLabel,
|
|
isSelf, time: msg.time * 1000
|
|
}
|
|
}
|
|
if (data.bizType === 'order-status') {
|
|
return {
|
|
id: msg.ID, type: 'system',
|
|
content: data.description || '[订单状态变更]',
|
|
time: msg.time * 1000
|
|
}
|
|
}
|
|
} catch (e) {}
|
|
return {
|
|
id: msg.ID, type: 'text',
|
|
content: msg.payload.description || '[消息]',
|
|
isSelf, avatar, time: msg.time * 1000
|
|
}
|
|
}
|
|
|
|
return {
|
|
id: msg.ID, type: 'text', content: '[暂不支持的消息类型]',
|
|
isSelf, avatar, time: msg.time * 1000
|
|
}
|
|
}
|