Files
xk-doctor-wx/utils/chat/chatRoomManager.js
2026-03-06 13:59:22 +08:00

425 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 聊天室管理器
* 封装聊天室相关操作,包括进入房间、离开房间、发送消息、接收消息、管理未读数等
*/
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();