Files
nl-um-vue-ts/src/api/websocket/index.ts

179 lines
4.1 KiB
TypeScript
Raw Normal View History

2025-12-03 12:56:57 +08:00
import type { ChatMessage } from '@/types/api'
export interface WebSocketMessage {
request_type?: string
clientId?: string
data?: ChatMessage
}
export type MessageHandler = (message: ChatMessage) => void
export type SignalHandler = (message: ChatMessage) => void
class WebSocketManager {
private ws: WebSocket | null = null
private clientId: string | null = null
private userId: string | null = null
private messageHandlers: MessageHandler[] = []
private signalHandlers: SignalHandler[] = []
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 {
// 处理可能的多行JSON或消息分割
const data = event.data.toString().trim()
const lines = data.split('\n').filter(line => line.trim())
2025-12-06 23:02:49 +08:00
for (const line of lines) {
try {
const payload: WebSocketMessage = JSON.parse(line)
2025-12-06 23:02:49 +08:00
// 接收clientId
if (payload.clientId) {
this.clientId = payload.clientId
}
2025-12-06 23:02:49 +08:00
// 处理接收消息
if (payload.request_type === 'receive_message' && payload.data) {
this.handleMessage(payload.data)
}
2025-12-06 23:02:49 +08:00
} catch (e) {
// Ignore single line parse error
}
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 = []
}
/**
* 获取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-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
// 普通消息 - 路由到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()