Initial Commit
This commit is contained in:
commit
1d71a02738
237 changed files with 64293 additions and 0 deletions
300
utils/chat-message-handler.js
Normal file
300
utils/chat-message-handler.js
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
/**
|
||||
* 聊天消息处理器
|
||||
* 专门处理WebSocket接收到的聊天相关消息
|
||||
*/
|
||||
class ChatMessageHandler {
|
||||
constructor() {
|
||||
this.messageListeners = new Map();
|
||||
this.statusListeners = new Map();
|
||||
this.conversationListeners = new Map();
|
||||
}
|
||||
|
||||
// 处理WebSocket消息
|
||||
handleMessage(message) {
|
||||
console.log('🔄 聊天消息处理器收到消息:', message.type);
|
||||
|
||||
switch (message.type) {
|
||||
case 'new_message':
|
||||
this.handleNewMessage(message.data);
|
||||
break;
|
||||
case 'message_status_update':
|
||||
this.handleMessageStatusUpdate(message.data);
|
||||
break;
|
||||
case 'message_recalled':
|
||||
this.handleMessageRecalled(message.data);
|
||||
break;
|
||||
case 'conversation_update':
|
||||
this.handleConversationUpdate(message.data);
|
||||
break;
|
||||
case 'typing_status':
|
||||
this.handleTypingStatus(message.data);
|
||||
break;
|
||||
case 'read_receipt':
|
||||
this.handleReadReceipt(message.data);
|
||||
break;
|
||||
default:
|
||||
console.log('未处理的聊天消息类型:', message.type);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理新消息
|
||||
handleNewMessage(messageData) {
|
||||
console.log('📨 收到新消息:', messageData);
|
||||
|
||||
// 格式化消息数据
|
||||
const formattedMessage = this.formatMessage(messageData);
|
||||
|
||||
// 通知所有监听器
|
||||
this.notifyMessageListeners('new_message', formattedMessage);
|
||||
|
||||
// 更新会话信息
|
||||
this.updateConversationLastMessage(formattedMessage);
|
||||
}
|
||||
|
||||
// 处理消息状态更新
|
||||
handleMessageStatusUpdate(statusData) {
|
||||
console.log('📊 消息状态更新:', statusData);
|
||||
|
||||
this.notifyStatusListeners('status_update', statusData);
|
||||
}
|
||||
|
||||
// 处理消息撤回
|
||||
handleMessageRecalled(recallData) {
|
||||
console.log('↩️ 消息被撤回:', recallData);
|
||||
|
||||
this.notifyMessageListeners('message_recalled', recallData);
|
||||
}
|
||||
|
||||
// 处理会话更新
|
||||
handleConversationUpdate(conversationData) {
|
||||
console.log('💬 会话更新:', conversationData);
|
||||
|
||||
this.notifyConversationListeners('conversation_update', conversationData);
|
||||
}
|
||||
|
||||
// 处理输入状态
|
||||
handleTypingStatus(typingData) {
|
||||
console.log('⌨️ 输入状态:', typingData);
|
||||
|
||||
this.notifyMessageListeners('typing_status', typingData);
|
||||
}
|
||||
|
||||
// 处理已读回执
|
||||
handleReadReceipt(readData) {
|
||||
console.log('✅ 已读回执:', readData);
|
||||
|
||||
this.notifyStatusListeners('read_receipt', readData);
|
||||
}
|
||||
|
||||
// 格式化消息数据
|
||||
formatMessage(messageData) {
|
||||
return {
|
||||
messageId: messageData.messageId,
|
||||
senderId: messageData.senderId,
|
||||
receiverId: messageData.receiverId,
|
||||
conversationId: messageData.conversationId || this.generateConversationId(messageData.senderId, messageData.receiverId),
|
||||
chatType: messageData.chatType || 0,
|
||||
msgType: messageData.msgType || 1,
|
||||
content: messageData.content || '',
|
||||
status: messageData.status || 1,
|
||||
sendTime: messageData.sendTime || new Date().toISOString(),
|
||||
senderName: messageData.senderName || '',
|
||||
senderAvatar: messageData.senderAvatar || '',
|
||||
replyTo: messageData.replyTo || '',
|
||||
atUsers: messageData.atUsers || [],
|
||||
extra: messageData.extra || '',
|
||||
// 客户端扩展字段
|
||||
isOwn: false, // 需要在使用时设置
|
||||
displayTime: this.formatDisplayTime(messageData.sendTime),
|
||||
contentType: this.getContentType(messageData.msgType),
|
||||
parsedContent: this.parseContent(messageData.content, messageData.msgType)
|
||||
};
|
||||
}
|
||||
|
||||
// 生成会话ID
|
||||
generateConversationId(senderId, receiverId) {
|
||||
// 单聊会话ID生成规则:较小的ID在前
|
||||
const ids = [senderId, receiverId].sort();
|
||||
return `conv_${ids[0]}_${ids[1]}`;
|
||||
}
|
||||
|
||||
// 格式化显示时间
|
||||
formatDisplayTime(timeString) {
|
||||
if (!timeString) return '';
|
||||
|
||||
const messageTime = new Date(timeString);
|
||||
const now = new Date();
|
||||
const diffMs = now - messageTime;
|
||||
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
||||
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
||||
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffMinutes < 1) {
|
||||
return '刚刚';
|
||||
} else if (diffMinutes < 60) {
|
||||
return `${diffMinutes}分钟前`;
|
||||
} else if (diffHours < 24) {
|
||||
return messageTime.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
||||
} else if (diffDays < 7) {
|
||||
const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||||
return weekdays[messageTime.getDay()];
|
||||
} else {
|
||||
return messageTime.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' });
|
||||
}
|
||||
}
|
||||
|
||||
// 获取内容类型
|
||||
getContentType(msgType) {
|
||||
const typeMap = {
|
||||
1: 'text',
|
||||
2: 'image',
|
||||
3: 'voice',
|
||||
4: 'video',
|
||||
5: 'file',
|
||||
6: 'emoji',
|
||||
7: 'location',
|
||||
8: 'system',
|
||||
9: 'recall',
|
||||
10: 'redpacket'
|
||||
};
|
||||
return typeMap[msgType] || 'text';
|
||||
}
|
||||
|
||||
// 解析消息内容
|
||||
parseContent(content, msgType) {
|
||||
try {
|
||||
switch (msgType) {
|
||||
case 2: // 图片
|
||||
return { url: content, type: 'image' };
|
||||
case 3: // 语音
|
||||
const voiceData = JSON.parse(content);
|
||||
return { url: voiceData.url, duration: voiceData.duration, type: 'voice' };
|
||||
case 4: // 视频
|
||||
const videoData = JSON.parse(content);
|
||||
return { url: videoData.url, duration: videoData.duration, thumbnail: videoData.thumbnail, type: 'video' };
|
||||
case 5: // 文件
|
||||
const fileData = JSON.parse(content);
|
||||
return { url: fileData.url, name: fileData.name, size: fileData.size, type: 'file' };
|
||||
case 7: // 位置
|
||||
const locationData = JSON.parse(content);
|
||||
return { latitude: locationData.latitude, longitude: locationData.longitude, address: locationData.address, type: 'location' };
|
||||
default:
|
||||
return { text: content, type: 'text' };
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('解析消息内容失败:', error);
|
||||
return { text: content, type: 'text' };
|
||||
}
|
||||
}
|
||||
|
||||
// 更新会话最后消息
|
||||
updateConversationLastMessage(message) {
|
||||
const conversationUpdate = {
|
||||
conversationId: message.conversationId,
|
||||
lastMessage: {
|
||||
content: this.getDisplayContent(message),
|
||||
sendTime: message.sendTime,
|
||||
senderId: message.senderId,
|
||||
senderName: message.senderName
|
||||
},
|
||||
unreadCount: message.isOwn ? 0 : 1 // 如果是自己发的消息,未读数为0
|
||||
};
|
||||
|
||||
this.notifyConversationListeners('last_message_update', conversationUpdate);
|
||||
}
|
||||
|
||||
// 获取显示内容
|
||||
getDisplayContent(message) {
|
||||
switch (message.msgType) {
|
||||
case 1: return message.content;
|
||||
case 2: return '[图片]';
|
||||
case 3: return '[语音]';
|
||||
case 4: return '[视频]';
|
||||
case 5: return '[文件]';
|
||||
case 6: return '[表情]';
|
||||
case 7: return '[位置]';
|
||||
case 8: return message.content;
|
||||
case 9: return '撤回了一条消息';
|
||||
case 10: return '[红包]';
|
||||
default: return message.content;
|
||||
}
|
||||
}
|
||||
|
||||
// 注册消息监听器
|
||||
onMessage(event, listener) {
|
||||
if (!this.messageListeners.has(event)) {
|
||||
this.messageListeners.set(event, []);
|
||||
}
|
||||
this.messageListeners.get(event).push(listener);
|
||||
}
|
||||
|
||||
// 注册状态监听器
|
||||
onStatus(event, listener) {
|
||||
if (!this.statusListeners.has(event)) {
|
||||
this.statusListeners.set(event, []);
|
||||
}
|
||||
this.statusListeners.get(event).push(listener);
|
||||
}
|
||||
|
||||
// 注册会话监听器
|
||||
onConversation(event, listener) {
|
||||
if (!this.conversationListeners.has(event)) {
|
||||
this.conversationListeners.set(event, []);
|
||||
}
|
||||
this.conversationListeners.get(event).push(listener);
|
||||
}
|
||||
|
||||
// 通知消息监听器
|
||||
notifyMessageListeners(event, data) {
|
||||
const listeners = this.messageListeners.get(event);
|
||||
if (listeners) {
|
||||
listeners.forEach(listener => {
|
||||
try {
|
||||
listener(data);
|
||||
} catch (error) {
|
||||
console.error(`消息监听器错误 [${event}]:`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 通知状态监听器
|
||||
notifyStatusListeners(event, data) {
|
||||
const listeners = this.statusListeners.get(event);
|
||||
if (listeners) {
|
||||
listeners.forEach(listener => {
|
||||
try {
|
||||
listener(data);
|
||||
} catch (error) {
|
||||
console.error(`状态监听器错误 [${event}]:`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 通知会话监听器
|
||||
notifyConversationListeners(event, data) {
|
||||
const listeners = this.conversationListeners.get(event);
|
||||
if (listeners) {
|
||||
listeners.forEach(listener => {
|
||||
try {
|
||||
listener(data);
|
||||
} catch (error) {
|
||||
console.error(`会话监听器错误 [${event}]:`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 清理监听器
|
||||
removeAllListeners() {
|
||||
this.messageListeners.clear();
|
||||
this.statusListeners.clear();
|
||||
this.conversationListeners.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
const chatMessageHandler = new ChatMessageHandler();
|
||||
|
||||
module.exports = chatMessageHandler;
|
||||
Loading…
Add table
Add a link
Reference in a new issue