2025-12-03 12:56:57 +08:00
|
|
|
|
import type { ChatMessage } from '@/types/api'
|
2025-12-08 09:07:56 +08:00
|
|
|
|
import type { MomentNotifPayload } from '@/types/moment'
|
2025-12-03 12:56:57 +08:00
|
|
|
|
|
|
|
|
|
|
export interface WebSocketMessage {
|
|
|
|
|
|
request_type?: string
|
|
|
|
|
|
clientId?: string
|
2025-12-08 09:07:56 +08:00
|
|
|
|
data?: ChatMessage | MomentNotifPayload
|
2025-12-03 12:56:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export type MessageHandler = (message: ChatMessage) => void
|
|
|
|
|
|
export type SignalHandler = (message: ChatMessage) => void
|
2025-12-08 09:07:56 +08:00
|
|
|
|
export type MomentNotifHandler = (payload: MomentNotifPayload) => void
|
2025-12-03 12:56:57 +08:00
|
|
|
|
|
|
|
|
|
|
class WebSocketManager {
|
|
|
|
|
|
private ws: WebSocket | null = null
|
|
|
|
|
|
private clientId: string | null = null
|
|
|
|
|
|
private userId: string | null = null
|
|
|
|
|
|
private messageHandlers: MessageHandler[] = []
|
|
|
|
|
|
private signalHandlers: SignalHandler[] = []
|
2025-12-08 09:07:56 +08:00
|
|
|
|
private momentNotifHandlers: MomentNotifHandler[] = []
|
2025-12-03 12:56:57 +08:00
|
|
|
|
private reconnectAttempts = 0
|
|
|
|
|
|
private maxReconnectAttempts = 5
|
|
|
|
|
|
private reconnectDelay = 3000
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 连接WebSocket
|
|
|
|
|
|
*/
|
|
|
|
|
|
connect(userId: string): Promise<void> {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
|
|
|
|
resolve()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.userId = userId
|
|
|
|
|
|
const wsUrl = `ws://localhost:12080/ws?user_id=${userId}`
|
2025-12-06 23:02:49 +08:00
|
|
|
|
|
2025-12-03 12:56:57 +08:00
|
|
|
|
try {
|
|
|
|
|
|
this.ws = new WebSocket(wsUrl)
|
|
|
|
|
|
|
|
|
|
|
|
this.ws.onopen = () => {
|
2025-12-06 23:02:49 +08:00
|
|
|
|
console.log('✅ WebSocket connected')
|
2025-12-03 12:56:57 +08:00
|
|
|
|
this.reconnectAttempts = 0
|
|
|
|
|
|
resolve()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.ws.onmessage = (event) => {
|
|
|
|
|
|
try {
|
2025-12-03 15:33:48 +08:00
|
|
|
|
// 处理可能的多行JSON或消息分割
|
|
|
|
|
|
const data = event.data.toString().trim()
|
|
|
|
|
|
const lines = data.split('\n').filter(line => line.trim())
|
2025-12-06 23:02:49 +08:00
|
|
|
|
|
2025-12-03 15:33:48 +08:00
|
|
|
|
for (const line of lines) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const payload: WebSocketMessage = JSON.parse(line)
|
2025-12-06 23:02:49 +08:00
|
|
|
|
|
2025-12-03 15:33:48 +08:00
|
|
|
|
// 接收clientId
|
|
|
|
|
|
if (payload.clientId) {
|
|
|
|
|
|
this.clientId = payload.clientId
|
|
|
|
|
|
}
|
2025-12-06 23:02:49 +08:00
|
|
|
|
|
2025-12-03 15:33:48 +08:00
|
|
|
|
// 处理接收消息
|
|
|
|
|
|
if (payload.request_type === 'receive_message' && payload.data) {
|
2025-12-08 09:07:56 +08:00
|
|
|
|
this.handleMessage(payload.data as ChatMessage)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 处理朋友圈通知
|
|
|
|
|
|
if (payload.request_type === 'moment_notification' && payload.data) {
|
|
|
|
|
|
this.handleMomentNotification(payload.data as MomentNotifPayload)
|
2025-12-03 15:33:48 +08:00
|
|
|
|
}
|
2025-12-06 23:02:49 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// Ignore single line parse error
|
2025-12-03 15:33:48 +08:00
|
|
|
|
}
|
2025-12-03 12:56:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2025-12-06 23:02:49 +08:00
|
|
|
|
console.error('WebSocket message parse error:', error)
|
2025-12-03 12:56:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.ws.onerror = (error) => {
|
|
|
|
|
|
console.error('WebSocket error:', error)
|
|
|
|
|
|
reject(error)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.ws.onclose = () => {
|
|
|
|
|
|
console.log('WebSocket closed')
|
|
|
|
|
|
this.ws = null
|
|
|
|
|
|
this.attemptReconnect()
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
reject(error)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 断开连接
|
|
|
|
|
|
*/
|
|
|
|
|
|
disconnect() {
|
|
|
|
|
|
if (this.ws) {
|
|
|
|
|
|
this.ws.close()
|
|
|
|
|
|
this.ws = null
|
|
|
|
|
|
}
|
|
|
|
|
|
this.clientId = null
|
|
|
|
|
|
this.userId = null
|
|
|
|
|
|
this.messageHandlers = []
|
|
|
|
|
|
this.signalHandlers = []
|
2025-12-08 09:07:56 +08:00
|
|
|
|
this.momentNotifHandlers = []
|
2025-12-03 12:56:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取ClientID
|
|
|
|
|
|
*/
|
|
|
|
|
|
getClientId(): string | null {
|
|
|
|
|
|
return this.clientId
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-06 23:02:49 +08:00
|
|
|
|
* 添加普通消息处理器
|
2025-12-03 12:56:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
onMessage(handler: MessageHandler) {
|
|
|
|
|
|
this.messageHandlers.push(handler)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-06 23:02:49 +08:00
|
|
|
|
* 移除普通消息处理器
|
2025-12-03 12:56:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
offMessage(handler: MessageHandler) {
|
|
|
|
|
|
const index = this.messageHandlers.indexOf(handler)
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
this.messageHandlers.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2025-12-06 23:02:49 +08:00
|
|
|
|
* 添加信令处理器 (WebRTC用)
|
2025-12-03 12:56:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
onSignal(handler: SignalHandler) {
|
|
|
|
|
|
this.signalHandlers.push(handler)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 移除信令处理器
|
|
|
|
|
|
*/
|
|
|
|
|
|
offSignal(handler: SignalHandler) {
|
|
|
|
|
|
const index = this.signalHandlers.indexOf(handler)
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
this.signalHandlers.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-08 09:07:56 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 添加朋友圈通知处理器
|
|
|
|
|
|
*/
|
|
|
|
|
|
onMomentNotification(handler: MomentNotifHandler) {
|
|
|
|
|
|
this.momentNotifHandlers.push(handler)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 移除朋友圈通知处理器
|
|
|
|
|
|
*/
|
|
|
|
|
|
offMomentNotification(handler: MomentNotifHandler) {
|
|
|
|
|
|
const index = this.momentNotifHandlers.indexOf(handler)
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
|
|
this.momentNotifHandlers.splice(index, 1)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 内部处理朋友圈通知
|
|
|
|
|
|
*/
|
|
|
|
|
|
private handleMomentNotification(payload: MomentNotifPayload) {
|
|
|
|
|
|
this.momentNotifHandlers.forEach(handler => handler(payload))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-03 12:56:57 +08:00
|
|
|
|
/**
|
2025-12-06 23:02:49 +08:00
|
|
|
|
* 内部处理接收到的消息
|
2025-12-03 12:56:57 +08:00
|
|
|
|
*/
|
|
|
|
|
|
private handleMessage(message: ChatMessage) {
|
2025-12-06 23:02:49 +08:00
|
|
|
|
// 信令消息(message_type = 6)- 路由到signalHandlers
|
2025-12-03 12:56:57 +08:00
|
|
|
|
if (message.message_type === 6) {
|
|
|
|
|
|
this.signalHandlers.forEach(handler => handler(message))
|
2025-12-06 23:02:49 +08:00
|
|
|
|
return
|
2025-12-03 12:56:57 +08:00
|
|
|
|
}
|
2025-12-06 23:02:49 +08:00
|
|
|
|
|
2025-12-03 15:33:48 +08:00
|
|
|
|
// 普通消息 - 路由到messageHandlers
|
|
|
|
|
|
this.messageHandlers.forEach(handler => handler(message))
|
2025-12-03 12:56:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 尝试重连
|
|
|
|
|
|
*/
|
|
|
|
|
|
private attemptReconnect() {
|
|
|
|
|
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!this.userId) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.reconnectAttempts++
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (this.userId) {
|
|
|
|
|
|
this.connect(this.userId).catch(console.error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}, this.reconnectDelay)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export const wsManager = new WebSocketManager()
|