Files
xk-doctor-wx/utils/chat/chatRoomManager.js

425 lines
12 KiB
JavaScript
Raw Normal View History

2026-03-06 13:59:22 +08:00
/**
* 聊天室管理器
* 封装聊天室相关操作,包括进入房间、离开房间、发送消息、接收消息、管理未读数等
*/
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<Array>} 消息列表
*/
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();