Migrate frontend realtime messaging to SSE.
Switch service integrations and screen/store consumers from websocket events to SSE, and ignore generated dist-web artifacts. Made-with: Cursor
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
import { ConversationResponse, MessageResponse, MessageSegment, UserDTO } from '../types/dto';
|
||||
import { messageService } from '../services/messageService';
|
||||
import {
|
||||
websocketService,
|
||||
sseService,
|
||||
WSChatMessage,
|
||||
WSGroupChatMessage,
|
||||
WSReadMessage,
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
WSGroupTypingMessage,
|
||||
WSGroupNoticeMessage,
|
||||
GroupNoticeType,
|
||||
} from '../services/websocketService';
|
||||
} from '../services/sseService';
|
||||
import {
|
||||
saveMessage,
|
||||
saveMessagesBatch,
|
||||
@@ -39,7 +39,7 @@ import {
|
||||
CachedMessage,
|
||||
getUserCache,
|
||||
saveUserCache,
|
||||
deleteMessage as deleteMessageFromDb,
|
||||
updateMessageStatus,
|
||||
deleteConversation as deleteConversationFromDb,
|
||||
} from '../services/database';
|
||||
import { api } from '../services/api';
|
||||
@@ -326,47 +326,47 @@ class MessageManager {
|
||||
|
||||
|
||||
// 监听私聊消息
|
||||
websocketService.on('chat', (message: WSChatMessage) => {
|
||||
sseService.on('chat', (message: WSChatMessage) => {
|
||||
this.handleNewMessage(message);
|
||||
});
|
||||
|
||||
// 监听群聊消息
|
||||
websocketService.on('group_message', (message: WSGroupChatMessage) => {
|
||||
sseService.on('group_message', (message: WSGroupChatMessage) => {
|
||||
this.handleNewMessage(message);
|
||||
});
|
||||
|
||||
// 监听私聊已读回执
|
||||
websocketService.on('read', (message: WSReadMessage) => {
|
||||
sseService.on('read', (message: WSReadMessage) => {
|
||||
this.handleReadReceipt(message);
|
||||
});
|
||||
|
||||
// 监听群聊已读回执
|
||||
websocketService.on('group_read', (message: WSGroupReadMessage) => {
|
||||
sseService.on('group_read', (message: WSGroupReadMessage) => {
|
||||
this.handleGroupReadReceipt(message);
|
||||
});
|
||||
|
||||
// 监听私聊消息撤回
|
||||
websocketService.on('recall', (message: WSRecallMessage) => {
|
||||
sseService.on('recall', (message: WSRecallMessage) => {
|
||||
this.handleRecallMessage(message);
|
||||
});
|
||||
|
||||
// 监听群聊消息撤回
|
||||
websocketService.on('group_recall', (message: WSGroupRecallMessage) => {
|
||||
sseService.on('group_recall', (message: WSGroupRecallMessage) => {
|
||||
this.handleGroupRecallMessage(message);
|
||||
});
|
||||
|
||||
// 监听群聊输入状态
|
||||
websocketService.on('group_typing', (message: WSGroupTypingMessage) => {
|
||||
sseService.on('group_typing', (message: WSGroupTypingMessage) => {
|
||||
this.handleGroupTyping(message);
|
||||
});
|
||||
|
||||
// 监听群通知
|
||||
websocketService.on('group_notice', (message: WSGroupNoticeMessage) => {
|
||||
sseService.on('group_notice', (message: WSGroupNoticeMessage) => {
|
||||
this.handleGroupNotice(message);
|
||||
});
|
||||
|
||||
// 监听连接状态
|
||||
websocketService.onConnect(() => {
|
||||
sseService.onConnect(() => {
|
||||
this.state.isWebSocketConnected = true;
|
||||
this.notifySubscribers({
|
||||
type: 'connection_changed',
|
||||
@@ -389,7 +389,7 @@ class MessageManager {
|
||||
}
|
||||
});
|
||||
|
||||
websocketService.onDisconnect(() => {
|
||||
sseService.onDisconnect(() => {
|
||||
this.state.isWebSocketConnected = false;
|
||||
this.notifySubscribers({
|
||||
type: 'connection_changed',
|
||||
@@ -761,36 +761,93 @@ class MessageManager {
|
||||
private handleGroupReadReceipt(message: WSGroupReadMessage): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将指定消息标记为已撤回(保留占位,不删除)
|
||||
*/
|
||||
private markMessageAsRecalled(conversationId: string, messageId: string): void {
|
||||
const normalizedConversationId = this.normalizeConversationId(conversationId);
|
||||
const messages = this.state.messagesMap.get(normalizedConversationId);
|
||||
if (!messages) {
|
||||
return;
|
||||
}
|
||||
|
||||
let changed = false;
|
||||
const updatedMessages: MessageResponse[] = messages.map((m): MessageResponse => {
|
||||
if (String(m.id) !== String(messageId) || m.status === 'recalled') {
|
||||
return m;
|
||||
}
|
||||
changed = true;
|
||||
return {
|
||||
...m,
|
||||
status: 'recalled' as MessageResponse['status'],
|
||||
segments: [],
|
||||
};
|
||||
});
|
||||
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.messagesMap.set(normalizedConversationId, updatedMessages);
|
||||
this.notifySubscribers({
|
||||
type: 'messages_updated',
|
||||
payload: { conversationId: normalizedConversationId, messages: updatedMessages },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果撤回的是会话最后一条消息,同步会话列表中的 last_message 状态
|
||||
*/
|
||||
private syncConversationLastMessageOnRecall(conversationId: string, messageId: string): void {
|
||||
const normalizedConversationId = this.normalizeConversationId(conversationId);
|
||||
const conversation = this.state.conversations.get(normalizedConversationId);
|
||||
if (!conversation?.last_message) {
|
||||
return;
|
||||
}
|
||||
if (String(conversation.last_message.id) !== String(messageId)) {
|
||||
return;
|
||||
}
|
||||
if (conversation.last_message.status === 'recalled') {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedConversation: ConversationResponse = {
|
||||
...conversation,
|
||||
last_message: {
|
||||
...conversation.last_message,
|
||||
status: 'recalled',
|
||||
},
|
||||
};
|
||||
this.state.conversations.set(normalizedConversationId, updatedConversation);
|
||||
this.updateConversationList();
|
||||
this.notifySubscribers({
|
||||
type: 'conversations_updated',
|
||||
payload: { conversations: this.state.conversationList },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理私聊消息撤回
|
||||
*/
|
||||
private handleRecallMessage(message: WSRecallMessage): void {
|
||||
const { conversation_id, message_id } = message;
|
||||
const normalizedConversationId = this.normalizeConversationId(conversation_id);
|
||||
|
||||
// 从消息列表中移除被撤回的消息
|
||||
const messages = this.state.messagesMap.get(conversation_id);
|
||||
if (messages) {
|
||||
const updatedMessages = messages.filter(m => m.id !== message_id);
|
||||
if (updatedMessages.length !== messages.length) {
|
||||
this.state.messagesMap.set(conversation_id, updatedMessages);
|
||||
this.notifySubscribers({
|
||||
type: 'messages_updated',
|
||||
payload: { conversationId: conversation_id, messages: updatedMessages },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
this.markMessageAsRecalled(normalizedConversationId, message_id);
|
||||
this.syncConversationLastMessageOnRecall(normalizedConversationId, message_id);
|
||||
|
||||
// 通知订阅者消息被撤回
|
||||
this.notifySubscribers({
|
||||
type: 'message_recalled',
|
||||
payload: { conversationId: conversation_id, messageId: message_id },
|
||||
payload: { conversationId: normalizedConversationId, messageId: message_id },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// 从本地数据库删除
|
||||
deleteMessageFromDb(message_id).catch(error => {
|
||||
console.error('[MessageManager] 删除本地消息失败:', error);
|
||||
// 同步本地数据库状态,避免冷启动后撤回占位丢失
|
||||
updateMessageStatus(message_id, 'recalled', true).catch(error => {
|
||||
console.error('[MessageManager] 更新本地消息撤回状态失败:', error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -799,31 +856,21 @@ class MessageManager {
|
||||
*/
|
||||
private handleGroupRecallMessage(message: WSGroupRecallMessage): void {
|
||||
const { conversation_id, message_id } = message;
|
||||
const normalizedConversationId = this.normalizeConversationId(conversation_id);
|
||||
|
||||
// 从消息列表中移除被撤回的消息
|
||||
const messages = this.state.messagesMap.get(conversation_id);
|
||||
if (messages) {
|
||||
const updatedMessages = messages.filter(m => m.id !== message_id);
|
||||
if (updatedMessages.length !== messages.length) {
|
||||
this.state.messagesMap.set(conversation_id, updatedMessages);
|
||||
this.notifySubscribers({
|
||||
type: 'messages_updated',
|
||||
payload: { conversationId: conversation_id, messages: updatedMessages },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
this.markMessageAsRecalled(normalizedConversationId, message_id);
|
||||
this.syncConversationLastMessageOnRecall(normalizedConversationId, message_id);
|
||||
|
||||
// 通知订阅者消息被撤回
|
||||
this.notifySubscribers({
|
||||
type: 'message_recalled',
|
||||
payload: { conversationId: conversation_id, messageId: message_id, isGroup: true },
|
||||
payload: { conversationId: normalizedConversationId, messageId: message_id, isGroup: true },
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
||||
// 从本地数据库删除
|
||||
deleteMessageFromDb(message_id).catch(error => {
|
||||
console.error('[MessageManager] 删除本地消息失败:', error);
|
||||
// 同步本地数据库状态,避免冷启动后撤回占位丢失
|
||||
updateMessageStatus(message_id, 'recalled', true).catch(error => {
|
||||
console.error('[MessageManager] 更新本地消息撤回状态失败:', error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user