refactor(WebSocket): migrate from SSE to WebSocket for real-time messaging

- Replaced SSEClient with WSClient for handling real-time messaging.
- Updated ProcessMessageUseCase to initialize WebSocket listeners.
- Refactored messageService to prioritize WebSocket for sending messages, with fallback to HTTP.
- Removed sseService and adjusted imports across the application to utilize wsService.
- Enhanced message handling and connection management for improved performance and reliability.
This commit is contained in:
lafay
2026-03-26 22:04:46 +08:00
parent 4b89b50006
commit ba99900624
16 changed files with 986 additions and 659 deletions

View File

@@ -14,7 +14,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage';
import { create } from 'zustand';
import { User } from '../types';
import { authService, resolveAuthApiError, LoginRequest, RegisterRequest } from '../services';
import { sseService } from '../services/sseService';
import { wsService } from '../services/wsService';
import {
initDatabase,
closeDatabase,
@@ -95,7 +95,7 @@ function resolveLoginError(error: any): string {
// ── 启动 SSE 实时服务 ──
async function startRealtime(): Promise<void> {
try {
await sseService.start();
await wsService.start();
} catch (error) {
console.error('[AuthStore] 启动 SSE 服务失败:', error);
}
@@ -212,7 +212,7 @@ export const useAuthStore = create<AuthState>((set) => ({
// 1. 通知服务端Token 清理在 authService 内部完成)
await authService.logout();
// 2. 停止 SSE
sseService.stop();
wsService.stop();
// 3. 清除 DB 中的用户缓存DB 此时一定已初始化)
await clearCurrentUserCache().catch(() => {});
// 4. 关闭数据库连接

View File

@@ -4,7 +4,7 @@
*/
import { messageStateManager, MessageStateManager } from './MessageStateManager';
import { sseMessageHandler, SSEMessageHandler } from './SSEMessageHandler';
import { wsMessageHandler, WSMessageHandler } from './WSMessageHandler';
import { messageSyncService, MessageSyncService } from './MessageSyncService';
import { readReceiptManager, ReadReceiptManager } from './ReadReceiptManager';
import { messageRepository } from '../../data/repositories/MessageRepository';
@@ -20,23 +20,23 @@ export type {
export class MessageManager {
private stateManager: MessageStateManager;
private sseHandler: SSEMessageHandler;
private wsHandler: WSMessageHandler;
private syncService: MessageSyncService;
private readManager: ReadReceiptManager;
private initialized: boolean = false;
private authUnsubscribe: (() => void) | null = null;
private sseUnsubscribe: (() => void) | null = null;
private wsUnsubscribe: (() => void) | null = null;
private currentUserId: string | null = null;
constructor() {
this.stateManager = messageStateManager;
this.sseHandler = sseMessageHandler;
this.wsHandler = wsMessageHandler;
this.syncService = messageSyncService;
this.readManager = readReceiptManager;
this.readManager.setStateManager(this.stateManager);
this.setupSSEHandlers();
this.setupWSHandlers();
this.initAuthListener();
}
@@ -58,8 +58,8 @@ export class MessageManager {
});
}
private setupSSEHandlers(): void {
this.sseUnsubscribe = this.sseHandler.subscribe((event) => {
private setupWSHandlers(): void {
this.wsUnsubscribe = this.wsHandler.subscribe((event) => {
switch (event.type) {
case 'chat_message':
case 'group_message':
@@ -89,7 +89,7 @@ export class MessageManager {
try {
console.log('[MessageManager] Initializing...');
this.sseHandler.connect();
this.wsHandler.connect();
const conversations = await this.syncService.syncConversations();
this.stateManager.setConversations(conversations);
@@ -224,14 +224,14 @@ export class MessageManager {
}
isConnected(): boolean {
return this.sseHandler.isConnected();
return this.wsHandler.isConnected();
}
reset(): void {
this.initialized = false;
this.stateManager.reset();
this.readManager.reset();
this.sseHandler.disconnect();
this.wsHandler.disconnect();
}
destroy(): void {
@@ -239,9 +239,9 @@ export class MessageManager {
this.authUnsubscribe();
this.authUnsubscribe = null;
}
if (this.sseUnsubscribe) {
this.sseUnsubscribe();
this.sseUnsubscribe = null;
if (this.wsUnsubscribe) {
this.wsUnsubscribe();
this.wsUnsubscribe = null;
}
this.reset();
}

View File

@@ -1,12 +1,12 @@
/**
* SSE
* SSE
* WS
* WS
*/
import { sseService, WSChatMessage, WSGroupChatMessage, WSReadMessage, WSGroupReadMessage, WSRecallMessage, WSGroupRecallMessage, WSGroupTypingMessage, WSGroupNoticeMessage, GroupNoticeType, WSMessage } from '../../services/sseService';
import { wsService, WSChatMessage, WSGroupChatMessage, WSReadMessage, WSGroupReadMessage, WSRecallMessage, WSGroupRecallMessage, WSGroupTypingMessage, WSGroupNoticeMessage, GroupNoticeType, WSMessage } from '../../services/wsService';
import type { Message, GroupNotice } from '../../core/entities/Message';
export type SSEEventType =
export type WSEventType =
| 'chat_message'
| 'group_message'
| 'read_receipt'
@@ -16,58 +16,58 @@ export type SSEEventType =
| 'typing'
| 'group_notice';
export interface SSEEvent {
type: SSEEventType;
export interface WSEvent {
type: WSEventType;
payload: any;
raw: any;
}
export type SSEEventHandler = (event: SSEEvent) => void;
export type WSEventHandler = (event: WSEvent) => void;
export class SSEMessageHandler {
export class WSMessageHandler {
private unsubscribeFns: Array<() => void> = [];
private handlers: Set<SSEEventHandler> = new Set();
private handlers: Set<WSEventHandler> = new Set();
connect(): void {
if (this.unsubscribeFns.length > 0) return;
// 监听私聊消息
const unsubChat = sseService.on('chat', (message) => {
const unsubChat = wsService.on('chat', (message) => {
this.emit('chat_message', this.parseChatMessage(message), message);
});
// 监听群聊消息
const unsubGroupMessage = sseService.on('group_message', (message) => {
const unsubGroupMessage = wsService.on('group_message', (message) => {
this.emit('group_message', this.parseGroupMessage(message), message);
});
// 监听私聊已读回执
const unsubRead = sseService.on('read', (message) => {
const unsubRead = wsService.on('read', (message) => {
this.emit('read_receipt', message, message);
});
// 监听群聊已读回执
const unsubGroupRead = sseService.on('group_read', (message) => {
const unsubGroupRead = wsService.on('group_read', (message) => {
this.emit('group_read_receipt', message, message);
});
// 监听私聊消息撤回
const unsubRecall = sseService.on('recall', (message) => {
const unsubRecall = wsService.on('recall', (message) => {
this.emit('message_recalled', message, message);
});
// 监听群聊消息撤回
const unsubGroupRecall = sseService.on('group_recall', (message) => {
const unsubGroupRecall = wsService.on('group_recall', (message) => {
this.emit('group_message_recalled', message, message);
});
// 监听群聊输入状态
const unsubGroupTyping = sseService.on('group_typing', (message) => {
const unsubGroupTyping = wsService.on('group_typing', (message) => {
this.emit('typing', message, message);
});
// 监听群通知
const unsubGroupNotice = sseService.on('group_notice', (message) => {
const unsubGroupNotice = wsService.on('group_notice', (message) => {
this.emit('group_notice', this.parseGroupNotice(message), message);
});
@@ -89,18 +89,18 @@ export class SSEMessageHandler {
this.handlers.clear();
}
subscribe(handler: SSEEventHandler): () => void {
subscribe(handler: WSEventHandler): () => void {
this.handlers.add(handler);
return () => this.handlers.delete(handler);
}
private emit(type: SSEEventType, payload: any, raw: any): void {
const event: SSEEvent = { type, payload, raw };
private emit(type: WSEventType, payload: any, raw: any): void {
const event: WSEvent = { type, payload, raw };
this.handlers.forEach(handler => {
try {
handler(event);
} catch (error) {
console.error('[SSEMessageHandler] Handler error:', error);
console.error('[WSMessageHandler] Handler error:', error);
}
});
}
@@ -141,8 +141,8 @@ export class SSEMessageHandler {
}
isConnected(): boolean {
return sseService.isConnected();
return wsService.isConnected();
}
}
export const sseMessageHandler = new SSEMessageHandler();
export const wsMessageHandler = new WSMessageHandler();

View File

@@ -12,8 +12,8 @@ export type { MessageState, MessageEvent, MessageSubscriber, MessageEventType }
// 导出同步服务
export { messageSyncService, MessageSyncService } from './MessageSyncService';
// 导出SSE处理器
export { sseMessageHandler, SSEMessageHandler } from './SSEMessageHandler';
// 导出WS处理器
export { wsMessageHandler, WSMessageHandler } from './WSMessageHandler';
// 导出已读管理器
export { readReceiptManager, ReadReceiptManager } from './ReadReceiptManager';

View File

@@ -18,7 +18,7 @@
import { ConversationResponse, MessageResponse, MessageSegment, UserDTO } from '../types/dto';
import { messageService } from '../services/messageService';
import {
sseService,
wsService,
WSChatMessage,
WSGroupChatMessage,
WSReadMessage,
@@ -28,7 +28,7 @@ import {
WSGroupTypingMessage,
WSGroupNoticeMessage,
GroupNoticeType,
} from '../services/sseService';
} from '../services/wsService';
import {
saveMessage,
saveMessagesBatch,
@@ -94,7 +94,7 @@ interface MessageManagerState {
systemUnreadCount: number;
// 连接状态
isSSEConnected: boolean;
isWSConnected: boolean;
// 当前活动会话ID用户正在查看的会话
currentConversationId: string | null;
@@ -210,7 +210,7 @@ class MessageManager {
messagesMap: new Map(),
totalUnreadCount: 0,
systemUnreadCount: 0,
isSSEConnected: false,
isWSConnected: false,
currentConversationId: null,
isLoadingConversations: false,
loadingMessagesSet: new Set(),
@@ -582,7 +582,7 @@ class MessageManager {
// 监听私聊消息
sseService.on('chat', (message: WSChatMessage) => {
wsService.on('chat', (message: WSChatMessage) => {
if (!this.state.isInitialized || this.isBootstrapping) {
this.bufferedChatMessages.push(message);
return;
@@ -591,7 +591,7 @@ class MessageManager {
});
// 监听群聊消息
sseService.on('group_message', (message: WSGroupChatMessage) => {
wsService.on('group_message', (message: WSGroupChatMessage) => {
if (!this.state.isInitialized || this.isBootstrapping) {
this.bufferedChatMessages.push(message);
return;
@@ -600,7 +600,7 @@ class MessageManager {
});
// 监听私聊已读回执
sseService.on('read', (message: WSReadMessage) => {
wsService.on('read', (message: WSReadMessage) => {
if (!this.state.isInitialized || this.isBootstrapping) {
this.bufferedReadReceipts.push(message);
return;
@@ -609,7 +609,7 @@ class MessageManager {
});
// 监听群聊已读回执
sseService.on('group_read', (message: WSGroupReadMessage) => {
wsService.on('group_read', (message: WSGroupReadMessage) => {
if (!this.state.isInitialized || this.isBootstrapping) {
this.bufferedReadReceipts.push(message);
return;
@@ -618,28 +618,28 @@ class MessageManager {
});
// 监听私聊消息撤回
sseService.on('recall', (message: WSRecallMessage) => {
wsService.on('recall', (message: WSRecallMessage) => {
this.handleRecallMessage(message);
});
// 监听群聊消息撤回
sseService.on('group_recall', (message: WSGroupRecallMessage) => {
wsService.on('group_recall', (message: WSGroupRecallMessage) => {
this.handleGroupRecallMessage(message);
});
// 监听群聊输入状态
sseService.on('group_typing', (message: WSGroupTypingMessage) => {
wsService.on('group_typing', (message: WSGroupTypingMessage) => {
this.handleGroupTyping(message);
});
// 监听群通知
sseService.on('group_notice', (message: WSGroupNoticeMessage) => {
wsService.on('group_notice', (message: WSGroupNoticeMessage) => {
this.handleGroupNotice(message);
});
// 监听连接状态
sseService.onConnect(() => {
this.state.isSSEConnected = true;
wsService.onConnect(() => {
this.state.isWSConnected = true;
this.notifySubscribers({
type: 'connection_changed',
payload: { connected: true },
@@ -668,8 +668,8 @@ class MessageManager {
}
});
sseService.onDisconnect(() => {
this.state.isSSEConnected = false;
wsService.onDisconnect(() => {
this.state.isWSConnected = false;
this.notifySubscribers({
type: 'connection_changed',
payload: { connected: false },
@@ -2375,7 +2375,7 @@ class MessageManager {
* 获取SSE连接状态
*/
isConnected(): boolean {
return this.state.isSSEConnected;
return this.state.isWSConnected;
}
/**
@@ -2486,7 +2486,7 @@ class MessageManager {
subscriber({
type: 'connection_changed',
payload: { connected: this.state.isSSEConnected },
payload: { connected: this.state.isWSConnected },
timestamp: Date.now(),
});