/** * 聊天室管理器 * 封装聊天室相关操作,包括进入房间、离开房间、发送消息、接收消息、管理未读数等 */ import { chatConfig } from '@/config/chat.js'; import { sendWebSocketMessage, isWebSocketConnected } from '@/utils/ws/initWebSocket.js'; import { getChatRegisterInfoApi, getMessagesByRoomIdApi } from '@/request/api/im.js'; /** * 聊天室管理器类 */ class ChatRoomManager { constructor() { // 当前房间ID this.currentRoomId = null; // 最后一条消息ID(用于分页加载) this.lastMessageId = 0; // 未读数(按房间ID存储) this.unreadCounts = {}; // 消息缓存(按房间ID存储) this.roomMessages = {}; // 消息处理器列表 this.messageHandlers = []; } /** * 进入聊天室 * @param {string} roomId - 房间ID * 职责:设置当前房间,重置未读数,保存到本地存储 */ enterRoom(roomId) { if (!roomId) { console.warn('房间ID不能为空'); return; } this.currentRoomId = roomId; uni.setStorageSync('currentRoomId', roomId); this.resetUnreadCount(roomId); console.log('进入聊天室:', roomId); } /** * 离开聊天室 * 职责:清空当前房间,移除本地存储 */ leaveRoom() { this.currentRoomId = null; uni.removeStorageSync('currentRoomId'); console.log('离开聊天室'); } /** * 获取当前房间ID * @returns {string|null} 当前房间ID */ getCurrentRoomId() { return this.currentRoomId || uni.getStorageSync('currentRoomId'); } /** * 增加未读数 * @param {string} roomId - 房间ID * 职责:如果房间不是当前房间,则增加未读数并通知 */ increaseUnreadCount(roomId) { if (!roomId) return; // 如果是当前房间,不增加未读数 if (this.getCurrentRoomId() === roomId) { return; } this.unreadCounts[roomId] = (this.unreadCounts[roomId] || 0) + 1; this.notifyUnreadChange(); } /** * 重置未读数 * @param {string} roomId - 房间ID * 职责:将指定房间的未读数重置为0并通知 */ resetUnreadCount(roomId) { if (this.unreadCounts[roomId]) { this.unreadCounts[roomId] = 0; this.notifyUnreadChange(); } } /** * 获取未读数 * @param {string} roomId - 房间ID * @returns {number} 未读数 */ getUnreadCount(roomId) { return this.unreadCounts[roomId] || 0; } /** * 获取所有未读数总和 * @returns {number} 总未读数 */ getAllUnreadCount() { return Object.values(this.unreadCounts).reduce((sum, count) => sum + count, 0); } /** * 格式化未读数显示 * @param {string} roomId - 房间ID * @returns {string} 格式化后的未读数 */ getFormattedUnreadCount(roomId) { const count = this.getUnreadCount(roomId); return chatConfig.formatUnreadCount(count); } /** * 通知未读数变化 * 职责:触发未读数更新事件 */ notifyUnreadChange() { uni.$emit('unread-message-update', { total: this.getAllUnreadCount(), byRoom: { ...this.unreadCounts } }); } /** * 添加消息到房间 * @param {string} roomId - 房间ID * @param {Object} message - 消息对象 * 职责:将消息添加到指定房间的消息列表 */ addMessageToRoom(roomId, message) { if (!roomId || !message) return; if (!this.roomMessages[roomId]) { this.roomMessages[roomId] = []; } // 检查消息是否已存在(避免重复) const existingIndex = this.roomMessages[roomId].findIndex(m => m.id === message.id); if (existingIndex !== -1) { this.roomMessages[roomId][existingIndex] = message; } else { this.roomMessages[roomId].push(message); } } /** * 获取房间消息列表 * @param {string} roomId - 房间ID * @returns {Array} 消息列表 */ getRoomMessages(roomId) { return this.roomMessages[roomId] || []; } /** * 加载房间消息 * @param {string} roomId - 房间ID * @param {number} lastMessageId - 最后一条消息ID(可选,用于分页) * @returns {Promise} 消息列表 */ async loadRoomMessages(roomId, lastMessageId = 0) { if (!roomId) { console.warn('房间ID不能为空'); return []; } try { const res = await getMessagesByRoomIdApi({ room_id: roomId, last_message_id: lastMessageId || 0 }); if (res && res.data && res.data.result) { const messages = this.processMessages(res.data.result.list || []); this.lastMessageId = res.data.result.last_message_id || 0; // 如果是首次加载,替换消息列表;否则追加到前面 if (lastMessageId === 0) { this.roomMessages[roomId] = messages; } else { this.roomMessages[roomId] = [...messages, ...(this.roomMessages[roomId] || [])]; } uni.$emit('load-room-message', lastMessageId === 0 ? 1 : 0); return messages; } return []; } catch (error) { console.error('加载房间消息失败:', error); return []; } } /** * 处理消息数据 * @param {Array|Object} messages - 消息数据(数组或单个消息) * @returns {Array|Object} 处理后的消息 */ processMessages(messages) { if (!Array.isArray(messages)) { return this.processSingleMessage(messages); } return messages.map((item) => { const result = { id: item.id, sender_user_id: item.sender_user_id, receiver_user_id: item.receiver_user_id, message_type: item.message_type, message_content: item.message_content, duration: item.duration, room_id: item.room_id, created_at: item.created_at || item.send_time, created_at_text: item.created_at_text || this.formatTime(item.send_time || item.created_at), timestamp: item.send_time || item.created_at }; // 如果是挂号消息(type=10),异步加载详细信息 if (item.message_type === 10) { this.loadRegisterInfo(result); } return result; }); } /** * 处理单个消息 * @param {Object} message - 消息对象 * @returns {Object} 处理后的消息 */ processSingleMessage(message) { return { id: message.id, sender_user_id: message.sender_user_id, receiver_user_id: message.receiver_user_id, message_type: message.message_type, message_content: message.message_content, duration: message.duration, room_id: message.room_id, created_at: message.created_at || message.send_time, created_at_text: message.created_at_text || this.formatTime(message.send_time || message.created_at), timestamp: message.send_time || message.created_at }; } /** * 格式化时间 * @param {number} timestamp - 时间戳(秒) * @returns {string} 格式化后的时间(HH:mm) */ formatTime(timestamp) { if (!timestamp) return ''; const date = new Date(timestamp * 1000); const hours = date.getHours().toString().padStart(2, '0'); const minutes = date.getMinutes().toString().padStart(2, '0'); return `${hours}:${minutes}`; } /** * 加载挂号信息(异步) * @param {Object} message - 消息对象 * 职责:为挂号消息加载详细信息 */ loadRegisterInfo(message) { const parsedContent = this.parseMessageContent(message.message_content); if (parsedContent._loaded || !parsedContent.id) return; getChatRegisterInfoApi({ id: parsedContent.id, message_id: message.id || '' }).then((res) => { if (res && res.data && res.data.result) { const resultWithFlag = { ...res.data.result, _loaded: true }; const roomId = message.room_id; const roomMessages = this.roomMessages[roomId]; if (roomMessages) { const index = roomMessages.findIndex(m => m.id === message.id); if (index !== -1) { roomMessages[index].message_content = JSON.stringify(resultWithFlag); uni.$emit('message-updated', { roomId, messageId: message.id }); } } } }).catch(error => { console.error('加载挂号信息失败:', error); }); } /** * 解析消息内容 * @param {string|Object} content - 消息内容 * @returns {Object|string} 解析后的内容 */ parseMessageContent(content) { try { return typeof content === 'object' ? content : JSON.parse(content); } catch (e) { return content; } } /** * 发送消息 * @param {Object} messageData - 消息数据 * @param {string} messageData.room_id - 房间ID * @param {string} messageData.sender_user_id - 发送者ID * @param {string} messageData.receiver_user_id - 接收者ID * @param {number|string} messageData.message_type - 消息类型 * @param {string} messageData.message_content - 消息内容 * @param {number} messageData.duration - 语音时长(可选) * @returns {boolean} 是否发送成功 */ sendMessage(messageData) { if (!isWebSocketConnected()) { console.error('WebSocket未连接,无法发送消息'); return false; } // 确保消息类型是数字 let messageType = messageData.message_type; if (typeof messageType === 'string') { messageType = chatConfig.getMessageType(messageType); } const message = { request_type: 'send_message', room_id: messageData.room_id, sender_user_id: messageData.sender_user_id, receiver_user_id: messageData.receiver_user_id, message_type: messageType, message_content: messageData.message_content || '', duration: messageData.duration || 0 }; return sendWebSocketMessage(message); } /** * 注册消息处理器 * @param {Function} handler - 消息处理函数 * 职责:注册消息处理器,当收到WebSocket消息时调用 */ onMessage(handler) { if (typeof handler !== 'function') { console.warn('消息处理器必须是函数'); return; } this.messageHandlers.push(handler); } /** * 移除消息处理器 * @param {Function} handler - 消息处理函数 */ offMessage(handler) { const index = this.messageHandlers.indexOf(handler); if (index !== -1) { this.messageHandlers.splice(index, 1); } } /** * 处理收到的WebSocket消息 * @param {Object} data - WebSocket消息数据 * 职责:处理收到的消息,更新未读数,调用注册的处理器 */ handleWebSocketMessage(data) { // 只处理聊天消息 if (data.request_type !== 'receive_message' && !data.room_id) { return; } const roomId = data.room_id; if (!roomId) return; // 处理消息 const message = this.processSingleMessage(data); // 添加到房间消息列表 this.addMessageToRoom(roomId, message); // 如果不是当前房间,增加未读数 if (this.getCurrentRoomId() !== roomId) { this.increaseUnreadCount(roomId); } // 调用所有注册的处理器 this.messageHandlers.forEach(handler => { try { handler(message, roomId); } catch (error) { console.error('消息处理器执行失败:', error); } }); } /** * 清空房间消息缓存 * @param {string} roomId - 房间ID(可选,不传则清空所有) */ clearRoomCache(roomId = null) { if (roomId) { delete this.roomMessages[roomId]; } else { this.roomMessages = {}; } } } // 导出单例 export const chatRoomManager = new ChatRoomManager();