通知板块 及 功能调整

This commit is contained in:
浅念
2025-08-22 18:27:26 +08:00
parent b319944007
commit 329acaf68d
22 changed files with 1771 additions and 218 deletions
+151 -78
View File
@@ -3,56 +3,38 @@ import { useUserStore } from '@/store/modules/user'
type JsonValue = Record<string, any> | any[]
const WS_BASE = 'wss://mzl.dev.hnxinyouhe.com/ws?token='
const WS_BASE = import.meta.env.VITE_API_URL_WSS + '/ws?token='
const MAX_RECONNECT_ATTEMPTS = 10
const MAX_RECONNECT_DELAY = 50000
const HEARTBEAT_INTERVAL = 50000
let socket: WebSocket | null = null
let reconnectTimer: any = null
let heartbeatTimer: any = null
let reconnectAttempts = 0
const maxReconnectDelayMs = 30000
const heartbeatIntervalMs = 30000
let isInitialConnection = true
// 生成全局唯一的 trace_id(优先使用 crypto.randomUUID)
// 生成唯一ID
const generateTraceId = (): string => {
try {
// @ts-ignore
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
// @ts-ignore
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
return `req-${crypto.randomUUID()}`
}
} catch (_) {}
// Fallback:基于时间戳 + 随机数
const timestampPart = Date.now().toString(36)
let randomPart = ''
try {
// @ts-ignore
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
const buf = new Uint8Array(16)
// @ts-ignore
crypto.getRandomValues(buf)
randomPart = Array.from(buf)
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
} else {
randomPart = Array.from({ length: 16 })
.map(() =>
Math.floor(Math.random() * 256)
.toString(16)
.padStart(2, '0')
)
.join('')
}
} catch (_) {
randomPart = Math.random().toString(36).slice(2)
} catch {
// 忽略错误,使用fallback方案
}
return `req-${timestampPart}-${randomPart}`
const timestamp = Date.now().toString(36)
const random = Math.random().toString(36).slice(2)
return `req-${timestamp}-${random}`
}
// 计算重连延迟
const getReconnectDelay = () => {
const base = 1000 * Math.pow(2, reconnectAttempts)
return Math.min(base, maxReconnectDelayMs)
return Math.min(base, MAX_RECONNECT_DELAY)
}
// 清理定时器
const cleanup = () => {
if (reconnectTimer) {
clearTimeout(reconnectTimer)
@@ -64,107 +46,198 @@ const cleanup = () => {
}
}
// 启动心跳
const startHeartbeat = () => {
// 清理上一次的心跳
if (heartbeatTimer) {
clearTimeout(heartbeatTimer)
heartbeatTimer = null
}
// 递归 setTimeout,确保固定间隔执行
heartbeatTimer = setTimeout(() => {
try {
if (socket && socket.readyState === WebSocket.OPEN) {
if (socket?.readyState === WebSocket.OPEN) {
const msg = {
event: 'system.ping',
data: {},
trace_id: generateTraceId() // 可选,用于请求-响应匹配
trace_id: generateTraceId()
}
socket.send(JSON.stringify(msg))
}
} catch (_) {}
// 下一次心跳
} catch {
// 忽略心跳发送错误
}
startHeartbeat()
}, heartbeatIntervalMs)
}, HEARTBEAT_INTERVAL)
}
// 发送心跳消息
const sendPing = () => {
try {
if (socket?.readyState === WebSocket.OPEN) {
const msg = {
event: 'system.ping',
data: {},
trace_id: generateTraceId()
}
socket.send(JSON.stringify(msg))
}
} catch {
// 忽略发送错误
}
}
// 处理WebSocket消息
const handleMessage = (event: MessageEvent) => {
let data: any = event.data
try {
data = JSON.parse(event.data)
} catch {
// 忽略解析错误
}
console.log('WebSocket message:', data)
if (data.event == 'notify.receive') {
ElNotification({
title: data.data.notice_data.message.title || '通知',
message: data.data.notice_data.message.content,
type: 'primary'
})
mittBus.emit('notification-data-changed', 1)
}
mittBus.emit('ws:message', data)
}
// 设置WebSocket事件处理器
const setupWebSocketHandlers = (ws: WebSocket) => {
ws.onopen = () => {
isInitialConnection = false
startHeartbeat()
mittBus.emit('ws:open')
sendPing()
}
ws.onmessage = handleMessage
ws.onerror = (err) => {
mittBus.emit('ws:error', err)
}
ws.onclose = () => {
mittBus.emit('ws:close')
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
scheduleReconnect()
}
}
}
// 安排重连
const scheduleReconnect = () => {
if (!isInitialConnection) {
reconnectAttempts += 1
}
if (reconnectAttempts > MAX_RECONNECT_ATTEMPTS) {
console.warn(`WebSocket重连失败,已达到最大重连次数(${MAX_RECONNECT_ATTEMPTS}),停止重连`)
mittBus.emit('ws:max-reconnect-reached')
return
}
cleanup()
const delay = getReconnectDelay()
console.log(`WebSocket重连尝试 ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS},延迟: ${delay}ms`)
reconnectTimer = setTimeout(() => {
const { accessToken } = useUserStore()
if (accessToken) initWebSocket(accessToken)
if (accessToken) {
try {
if (socket) {
try {
socket.close()
} catch {
// 忽略关闭错误
}
socket = null
}
const url = `${WS_BASE}${accessToken}`
socket = new WebSocket(url)
setupWebSocketHandlers(socket)
} catch (e) {
mittBus.emit('ws:error', e)
if (reconnectAttempts <= MAX_RECONNECT_ATTEMPTS) {
scheduleReconnect()
}
}
}
}, delay)
}
// 初始化WebSocket连接
export const initWebSocket = (token: string) => {
try {
if (!token) return
if (socket) {
try {
socket.close()
} catch (_) {}
} catch {
// 忽略关闭错误
}
socket = null
}
cleanup()
reconnectAttempts = 0
isInitialConnection = true
const url = `${WS_BASE}${token}`
socket = new WebSocket(url)
socket.onopen = () => {
reconnectAttempts = 0
startHeartbeat()
mittBus.emit('ws:open')
try {
const msg = {
event: 'system.ping',
data: {},
trace_id: generateTraceId() // 可选,用于请求-响应匹配
}
socket?.send(JSON.stringify(msg))
} catch (_) {}
}
socket.onmessage = (event) => {
const dataStr = event.data
let data: any = dataStr
try {
data = JSON.parse(dataStr)
} catch (_) {}
mittBus.emit('ws:message', data)
}
socket.onerror = (err) => {
mittBus.emit('ws:error', err)
}
socket.onclose = () => {
mittBus.emit('ws:close')
reconnectAttempts += 1
scheduleReconnect()
}
setupWebSocketHandlers(socket)
} catch (e) {
mittBus.emit('ws:error', e)
scheduleReconnect()
}
}
// 关闭WebSocket连接
export const closeWebSocket = () => {
cleanup()
try {
socket?.close()
} catch (_) {}
} catch {
// 忽略关闭错误
}
socket = null
reconnectAttempts = 0
isInitialConnection = true
}
// 发送WebSocket消息
export const sendWebSocket = (payload: string | JsonValue) => {
if (!socket || socket.readyState !== WebSocket.OPEN) return false
try {
const data = typeof payload === 'string' ? payload : JSON.stringify(payload)
socket.send(data)
return true
} catch (_) {
} catch {
return false
}
}
// 重置重连次数
export const resetWebSocketReconnect = () => {
reconnectAttempts = 0
isInitialConnection = true
console.log('WebSocket重连次数已重置')
}
// 获取当前重连次数
export const getWebSocketReconnectAttempts = () => reconnectAttempts
// 获取最大重连次数
export const getMaxReconnectAttempts = () => MAX_RECONNECT_ATTEMPTS
// 获取WebSocket连接状态
export const getWebSocketStatus = () => ({
socket,
readyState: socket?.readyState,
reconnectAttempts,
maxReconnectAttempts: MAX_RECONNECT_ATTEMPTS,
isReconnecting: reconnectTimer !== null,
isHeartbeating: heartbeatTimer !== null,
isInitialConnection
})