campus-errand/miniapp/utils/im.js
18631081161 681d2b5fe8
All checks were successful
continuous-integration/drone/push Build is passing
提现
2026-04-02 16:55:18 +08:00

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
}
}