fix: 强制进入后端时,仍尝试建立ws连接
This commit is contained in:
@@ -71,9 +71,9 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { getConfig } from '@/utils/config'
|
||||
import { mirrorManager } from '@/utils/mirrorManager'
|
||||
import router from '@/router'
|
||||
import { useUpdateChecker } from '@/composables/useUpdateChecker'
|
||||
import { connectAfterBackendStart } from '@/composables/useWebSocket'
|
||||
import { forceEnterApp } from '@/utils/appEntry'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
// Props
|
||||
@@ -154,11 +154,12 @@ function handleForceEnter() {
|
||||
}
|
||||
|
||||
// 确认弹窗中的"我知道我在做什么"按钮,直接进入应用
|
||||
function handleForceEnterConfirm() {
|
||||
async function handleForceEnterConfirm() {
|
||||
clearTimers()
|
||||
aborted.value = true
|
||||
forceEnterVisible.value = false
|
||||
router.push('/home')
|
||||
|
||||
await forceEnterApp('自动模式-强行进入确认')
|
||||
}
|
||||
|
||||
// 事件处理 - 增强重新配置环境按钮功能
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
|
||||
import router from '@/router'
|
||||
import { forceEnterApp } from '@/utils/appEntry'
|
||||
|
||||
// Props
|
||||
interface Props {
|
||||
@@ -79,8 +79,8 @@ function handleReconfigure() {
|
||||
}
|
||||
|
||||
// 强行进入应用
|
||||
function handleForceEnter() {
|
||||
router.push('/home')
|
||||
async function handleForceEnter() {
|
||||
await forceEnterApp('环境不完整-强行进入')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { Modal } from 'ant-design-vue'
|
||||
|
||||
// ====== 配置项 ======
|
||||
const BASE_WS_URL = 'ws://localhost:36163/api/core/ws'
|
||||
const HEARTBEAT_INTERVAL = 15000
|
||||
const HEARTBEAT_TIMEOUT = 5000
|
||||
const BACKEND_CHECK_INTERVAL = 3000
|
||||
const HEARTBEAT_INTERVAL = 30000 // 30秒心跳间隔,与后端保持一致
|
||||
const HEARTBEAT_TIMEOUT = 45000 // 45秒超时,给网络延迟留够时间
|
||||
const BACKEND_CHECK_INTERVAL = 6000 // 6秒检查间隔
|
||||
const MAX_RESTART_ATTEMPTS = 3
|
||||
const RESTART_DELAY = 2000
|
||||
const MAX_QUEUE_SIZE = 50 // 每个 ID 或全局 type 队列最大条数
|
||||
@@ -111,7 +111,7 @@ const initGlobalStorage = (): GlobalWSStorage => ({
|
||||
|
||||
const getGlobalStorage = (): GlobalWSStorage => {
|
||||
if (!(window as any)[WS_STORAGE_KEY]) {
|
||||
;(window as any)[WS_STORAGE_KEY] = initGlobalStorage()
|
||||
; (window as any)[WS_STORAGE_KEY] = initGlobalStorage()
|
||||
}
|
||||
return (window as any)[WS_STORAGE_KEY]
|
||||
}
|
||||
@@ -166,7 +166,7 @@ const handleBackendFailure = async () => {
|
||||
okText: '重启应用',
|
||||
onOk: () => {
|
||||
if ((window.electronAPI as any)?.windowClose) {
|
||||
;(window.electronAPI as any).windowClose()
|
||||
; (window.electronAPI as any).windowClose()
|
||||
} else {
|
||||
window.location.reload()
|
||||
}
|
||||
@@ -178,13 +178,26 @@ const handleBackendFailure = async () => {
|
||||
setTimeout(async () => {
|
||||
const success = await restartBackend()
|
||||
if (success) {
|
||||
// 统一在一个地方管理连接权限
|
||||
setConnectionPermission(true, '后端重启后重连')
|
||||
setTimeout(() => {
|
||||
connectGlobalWebSocket('后端重启后重连').then(() => {
|
||||
setConnectionPermission(false, '正常运行中')
|
||||
})
|
||||
|
||||
// 等待后端完全启动
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const connected = await connectGlobalWebSocket('后端重启后重连')
|
||||
if (connected) {
|
||||
// 连接成功后再禁用权限
|
||||
setTimeout(() => {
|
||||
setConnectionPermission(false, '正常运行中')
|
||||
}, 1000)
|
||||
}
|
||||
} catch (e) {
|
||||
warn('重启后重连失败:', e)
|
||||
setConnectionPermission(false, '连接失败')
|
||||
}
|
||||
}, RESTART_DELAY)
|
||||
} else {
|
||||
// 重启失败,继续尝试
|
||||
setTimeout(handleBackendFailure, RESTART_DELAY)
|
||||
}
|
||||
}, RESTART_DELAY)
|
||||
@@ -208,12 +221,15 @@ const startBackendMonitoring = () => {
|
||||
setBackendStatus('stopped')
|
||||
}
|
||||
|
||||
if (global.lastPingTime > 0 && now - global.lastPingTime > HEARTBEAT_TIMEOUT * 2) {
|
||||
// 检查心跳超时:如果超过心跳超时时间且连接仍然打开,说明后端可能有问题
|
||||
if (global.lastPingTime > 0 && now - global.lastPingTime > HEARTBEAT_TIMEOUT) {
|
||||
if (global.wsRef?.readyState === WebSocket.OPEN) {
|
||||
setBackendStatus('error')
|
||||
// 主动关闭可能有问题的连接
|
||||
global.wsRef.close(1000, '心跳超时')
|
||||
}
|
||||
}
|
||||
}, BACKEND_CHECK_INTERVAL * 2)
|
||||
}, BACKEND_CHECK_INTERVAL)
|
||||
}
|
||||
|
||||
// ====== 心跳 ======
|
||||
@@ -239,7 +255,7 @@ const startGlobalHeartbeat = (ws: WebSocket) => {
|
||||
data: { Ping: pingTime, connectionId: global.connectionId },
|
||||
})
|
||||
)
|
||||
} catch {}
|
||||
} catch { }
|
||||
}
|
||||
}, HEARTBEAT_INTERVAL)
|
||||
}
|
||||
@@ -256,13 +272,13 @@ const cleanupExpiredMessages = (now: number) => {
|
||||
const messageMatchesFilter = (message: WebSocketBaseMessage, filter: SubscriptionFilter): boolean => {
|
||||
// 如果都不指定,匹配所有消息
|
||||
if (!filter.type && !filter.id) return true
|
||||
|
||||
|
||||
// 如果只指定type
|
||||
if (filter.type && !filter.id) return message.type === filter.type
|
||||
|
||||
|
||||
// 如果只指定id
|
||||
if (!filter.type && filter.id) return message.id === filter.id
|
||||
|
||||
|
||||
// 如果同时指定type和id,必须都匹配
|
||||
return message.type === filter.type && message.id === filter.id
|
||||
}
|
||||
@@ -278,11 +294,11 @@ const getCacheMarkerKey = (filter: SubscriptionFilter): string => {
|
||||
// 添加缓存标记
|
||||
const addCacheMarker = (filter: SubscriptionFilter) => {
|
||||
if (!filter.needCache) return
|
||||
|
||||
|
||||
const global = getGlobalStorage()
|
||||
const key = getCacheMarkerKey(filter)
|
||||
const existing = global.cacheMarkers.value.get(key)
|
||||
|
||||
|
||||
if (existing) {
|
||||
existing.refCount++
|
||||
} else {
|
||||
@@ -292,18 +308,18 @@ const addCacheMarker = (filter: SubscriptionFilter) => {
|
||||
refCount: 1
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
log(`缓存标记 ${key} 引用计数: ${global.cacheMarkers.value.get(key)?.refCount}`)
|
||||
}
|
||||
|
||||
// 移除缓存标记
|
||||
const removeCacheMarker = (filter: SubscriptionFilter) => {
|
||||
if (!filter.needCache) return
|
||||
|
||||
|
||||
const global = getGlobalStorage()
|
||||
const key = getCacheMarkerKey(filter)
|
||||
const existing = global.cacheMarkers.value.get(key)
|
||||
|
||||
|
||||
if (existing) {
|
||||
existing.refCount--
|
||||
if (existing.refCount <= 0) {
|
||||
@@ -318,7 +334,7 @@ const removeCacheMarker = (filter: SubscriptionFilter) => {
|
||||
// 检查消息是否需要缓存
|
||||
const shouldCacheMessage = (message: WebSocketBaseMessage): boolean => {
|
||||
const global = getGlobalStorage()
|
||||
|
||||
|
||||
for (const [, marker] of global.cacheMarkers.value) {
|
||||
const filter = { type: marker.type, id: marker.id }
|
||||
if (messageMatchesFilter(message, filter)) {
|
||||
@@ -361,8 +377,8 @@ const handleMessage = (raw: WebSocketBaseMessage) => {
|
||||
log(`消息已缓存: type=${raw.type}, id=${raw.id}`)
|
||||
}
|
||||
|
||||
// 定期清理过期消息(每 10 条触发一次,避免频繁)
|
||||
if (Math.random() < 0.1) {
|
||||
// 定期清理过期消息(每处理50条消息触发一次,避免频繁且更可预测)
|
||||
if (global.cachedMessages.value.length > 0 && global.cachedMessages.value.length % 50 === 0) {
|
||||
cleanupExpiredMessages(now)
|
||||
}
|
||||
|
||||
@@ -378,23 +394,23 @@ export const subscribe = (
|
||||
): string => {
|
||||
const global = getGlobalStorage()
|
||||
const subscriptionId = `sub_${++global.subscriptionCounter}_${Date.now()}`
|
||||
|
||||
|
||||
const subscription: WebSocketSubscription = {
|
||||
subscriptionId,
|
||||
filter,
|
||||
handler
|
||||
}
|
||||
|
||||
|
||||
global.subscriptions.value.set(subscriptionId, subscription)
|
||||
|
||||
|
||||
// 添加缓存标记
|
||||
addCacheMarker(filter)
|
||||
|
||||
|
||||
// 回放匹配的缓存消息
|
||||
const matchingMessages = global.cachedMessages.value.filter(cached =>
|
||||
const matchingMessages = global.cachedMessages.value.filter(cached =>
|
||||
messageMatchesFilter(cached.message, filter)
|
||||
)
|
||||
|
||||
|
||||
if (matchingMessages.length > 0) {
|
||||
log(`回放 ${matchingMessages.length} 条缓存消息给订阅 ${subscriptionId}`)
|
||||
matchingMessages.forEach(cached => {
|
||||
@@ -405,7 +421,7 @@ export const subscribe = (
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
log(`新订阅创建: ${subscriptionId}`, filter)
|
||||
return subscriptionId
|
||||
}
|
||||
@@ -413,14 +429,14 @@ export const subscribe = (
|
||||
export const unsubscribe = (subscriptionId: string): void => {
|
||||
const global = getGlobalStorage()
|
||||
const subscription = global.subscriptions.value.get(subscriptionId)
|
||||
|
||||
|
||||
if (subscription) {
|
||||
// 移除缓存标记
|
||||
removeCacheMarker(subscription.filter)
|
||||
|
||||
|
||||
// 清理缓存中没有任何标记的消息
|
||||
cleanupUnmarkedCache()
|
||||
|
||||
|
||||
global.subscriptions.value.delete(subscriptionId)
|
||||
log(`订阅已取消: ${subscriptionId}`)
|
||||
} else {
|
||||
@@ -431,7 +447,7 @@ export const unsubscribe = (subscriptionId: string): void => {
|
||||
// 清理没有标记的缓存消息
|
||||
const cleanupUnmarkedCache = () => {
|
||||
const global = getGlobalStorage()
|
||||
|
||||
|
||||
global.cachedMessages.value = global.cachedMessages.value.filter(cached => {
|
||||
// 检查是否还有标记需要这条消息
|
||||
for (const [, marker] of global.cacheMarkers.value) {
|
||||
@@ -455,7 +471,7 @@ const releaseConnectionLock = () => {
|
||||
isGlobalConnectingLock = false
|
||||
}
|
||||
|
||||
const allowedConnectionReasons = ['后端启动后连接', '后端重启后重连']
|
||||
const allowedConnectionReasons = ['后端启动后连接', '后端重启后重连', '系统初始化', '手动重连', '强制连接']
|
||||
const isValidConnectionReason = (reason: string) => allowedConnectionReasons.includes(reason)
|
||||
const checkConnectionPermission = () => getGlobalStorage().allowNewConnection
|
||||
const setConnectionPermission = (allow: boolean, reason: string) => {
|
||||
@@ -466,9 +482,19 @@ const setConnectionPermission = (allow: boolean, reason: string) => {
|
||||
|
||||
const createGlobalWebSocket = (): WebSocket => {
|
||||
const global = getGlobalStorage()
|
||||
|
||||
// 清理旧连接
|
||||
if (global.wsRef) {
|
||||
if (global.wsRef.readyState === WebSocket.OPEN) return global.wsRef
|
||||
if (global.wsRef.readyState === WebSocket.CONNECTING) return global.wsRef
|
||||
if (global.wsRef.readyState === WebSocket.OPEN) {
|
||||
log('警告:尝试创建新连接但当前连接仍有效')
|
||||
return global.wsRef
|
||||
}
|
||||
if (global.wsRef.readyState === WebSocket.CONNECTING) {
|
||||
log('警告:尝试创建新连接但当前连接正在建立中')
|
||||
return global.wsRef
|
||||
}
|
||||
// 清理已关闭或错误状态的连接
|
||||
global.wsRef = null
|
||||
}
|
||||
|
||||
const ws = new WebSocket(BASE_WS_URL)
|
||||
@@ -480,7 +506,11 @@ const createGlobalWebSocket = (): WebSocket => {
|
||||
global.reconnectAttempts = 0
|
||||
setGlobalStatus('已连接')
|
||||
startGlobalHeartbeat(ws)
|
||||
setConnectionPermission(false, '正常运行中')
|
||||
|
||||
// 只有在特殊连接原因下才设置为正常运行
|
||||
if (global.connectionReason !== '系统初始化') {
|
||||
setConnectionPermission(false, '正常运行中')
|
||||
}
|
||||
|
||||
try {
|
||||
ws.send(
|
||||
@@ -495,35 +525,52 @@ const createGlobalWebSocket = (): WebSocket => {
|
||||
data: { Pong: Date.now(), connectionId: global.connectionId },
|
||||
})
|
||||
)
|
||||
} catch {}
|
||||
} catch (e) {
|
||||
warn('发送初始信号失败:', e)
|
||||
}
|
||||
|
||||
initializeGlobalSubscriptions()
|
||||
log('WebSocket连接已建立并初始化完成')
|
||||
}
|
||||
|
||||
ws.onmessage = ev => {
|
||||
try {
|
||||
const raw = JSON.parse(ev.data) as WebSocketBaseMessage
|
||||
handleMessage(raw)
|
||||
} catch {}
|
||||
} catch (e) {
|
||||
warn('解析WebSocket消息失败:', e, '原始数据:', ev.data)
|
||||
}
|
||||
}
|
||||
|
||||
ws.onerror = (error) => {
|
||||
setGlobalStatus('连接错误')
|
||||
warn('WebSocket错误:', error)
|
||||
}
|
||||
|
||||
ws.onerror = () => setGlobalStatus('连接错误')
|
||||
ws.onclose = event => {
|
||||
setGlobalStatus('已断开')
|
||||
stopGlobalHeartbeat()
|
||||
global.isConnecting = false
|
||||
|
||||
log(`WebSocket连接关闭: code=${event.code}, reason="${event.reason}"`)
|
||||
|
||||
// 根据关闭原因决定是否需要处理后端故障
|
||||
if (event.code === 1000 && event.reason === 'Ping超时') {
|
||||
handleBackendFailure().catch(e => warn('handleBackendFailure error:', e))
|
||||
} else if (event.code === 1000 && event.reason === '心跳超时') {
|
||||
handleBackendFailure().catch(e => warn('handleBackendFailure error:', e))
|
||||
}
|
||||
}
|
||||
|
||||
return ws
|
||||
}
|
||||
|
||||
const connectGlobalWebSocket = async (reason: string = '未指定原因'): Promise<boolean> => {
|
||||
const connectGlobalWebSocket = async (reason: string = '手动重连'): Promise<boolean> => {
|
||||
const global = getGlobalStorage()
|
||||
if (!checkConnectionPermission() || !isValidConnectionReason(reason)) return false
|
||||
if (!checkConnectionPermission() || !isValidConnectionReason(reason)) {
|
||||
warn(`连接被拒绝: 权限=${checkConnectionPermission()}, 原因="${reason}"是否有效=${isValidConnectionReason(reason)}`)
|
||||
return false
|
||||
}
|
||||
if (!acquireConnectionLock()) return false
|
||||
|
||||
try {
|
||||
@@ -573,6 +620,72 @@ export const connectAfterBackendStart = async (): Promise<boolean> => {
|
||||
}
|
||||
}
|
||||
|
||||
// 强制连接模式,用于强行进入应用时
|
||||
export const forceConnectWebSocket = async (): Promise<boolean> => {
|
||||
log('强制WebSocket连接模式开始')
|
||||
|
||||
const global = getGlobalStorage()
|
||||
|
||||
// 显示当前状态
|
||||
log('当前连接状态:', {
|
||||
status: global.status.value,
|
||||
wsReadyState: global.wsRef?.readyState,
|
||||
allowNewConnection: global.allowNewConnection,
|
||||
connectionReason: global.connectionReason
|
||||
})
|
||||
|
||||
// 设置连接权限
|
||||
setConnectionPermission(true, '强制连接')
|
||||
log('已设置强制连接权限')
|
||||
|
||||
try {
|
||||
// 尝试连接,最多重试3次
|
||||
let connected = false
|
||||
let attempts = 0
|
||||
const maxAttempts = 3
|
||||
|
||||
while (!connected && attempts < maxAttempts) {
|
||||
attempts++
|
||||
log(`强制连接尝试 ${attempts}/${maxAttempts}`)
|
||||
|
||||
try {
|
||||
connected = await connectGlobalWebSocket('强制连接')
|
||||
if (connected) {
|
||||
startBackendMonitoring()
|
||||
log('强制WebSocket连接成功')
|
||||
break
|
||||
} else {
|
||||
warn(`强制连接尝试 ${attempts} 失败`)
|
||||
if (attempts < maxAttempts) {
|
||||
// 等待1秒后重试
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
}
|
||||
}
|
||||
} catch (attemptError) {
|
||||
warn(`强制连接尝试 ${attempts} 异常:`, attemptError)
|
||||
if (attempts < maxAttempts) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!connected) {
|
||||
warn('所有强制连接尝试均失败,但不阻止应用启动')
|
||||
}
|
||||
|
||||
return connected
|
||||
} catch (error) {
|
||||
warn('强制WebSocket连接异常:', error)
|
||||
return false
|
||||
} finally {
|
||||
// 稍后重置连接权限,给连接时间
|
||||
setTimeout(() => {
|
||||
setConnectionPermission(false, '强制连接完成')
|
||||
log('强制连接权限已重置')
|
||||
}, 2000) // 增加到2秒
|
||||
}
|
||||
}
|
||||
|
||||
// ====== 全局处理器 ======
|
||||
let _defaultHandlersLoaded = true
|
||||
let _defaultTaskManagerHandler = schedulerHandlers.handleTaskManagerMessage
|
||||
@@ -627,7 +740,7 @@ const initializeGlobalSubscriptions = () => {
|
||||
data: { Pong: msg.data.Ping, connectionId: global.connectionId },
|
||||
})
|
||||
)
|
||||
} catch {}
|
||||
} catch { }
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -648,8 +761,22 @@ export function useWebSocket() {
|
||||
const ws = global.wsRef
|
||||
if (ws?.readyState === WebSocket.OPEN) {
|
||||
try {
|
||||
ws.send(JSON.stringify({ id, type, data }))
|
||||
} catch {}
|
||||
const message = { id, type, data }
|
||||
ws.send(JSON.stringify(message))
|
||||
if (DEBUG && type !== 'Signal') { // 避免心跳消息spam日志
|
||||
log('发送消息:', message)
|
||||
}
|
||||
return true
|
||||
} catch (e) {
|
||||
warn('发送消息失败:', e, { id, type, data })
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
warn('WebSocket未连接,无法发送消息:', {
|
||||
readyState: ws?.readyState,
|
||||
message: { id, type, data }
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,6 +806,21 @@ export function useWebSocket() {
|
||||
lastCheck: global.lastBackendCheck,
|
||||
})
|
||||
|
||||
// 调试功能
|
||||
const debug = {
|
||||
forceConnect: forceConnectWebSocket,
|
||||
normalConnect: connectAfterBackendStart,
|
||||
getGlobalStorage,
|
||||
setConnectionPermission,
|
||||
checkConnectionPermission,
|
||||
allowedReasons: allowedConnectionReasons
|
||||
}
|
||||
|
||||
// 在开发模式下暴露调试功能到全局
|
||||
if (DEBUG && typeof window !== 'undefined') {
|
||||
; (window as any).wsDebug = debug
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
unsubscribe,
|
||||
@@ -688,6 +830,7 @@ export function useWebSocket() {
|
||||
backendStatus: global.backendStatus,
|
||||
restartBackend: restartBackendManually,
|
||||
getBackendStatus,
|
||||
debug: DEBUG ? debug : undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
81
frontend/src/utils/appEntry.ts
Normal file
81
frontend/src/utils/appEntry.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
// appEntry.ts - 统一的应用进入逻辑
|
||||
import router from '@/router'
|
||||
import { connectAfterBackendStart, forceConnectWebSocket } from '@/composables/useWebSocket'
|
||||
|
||||
/**
|
||||
* 统一的进入应用函数,会自动尝试建立WebSocket连接
|
||||
* @param reason 进入应用的原因,用于日志记录
|
||||
* @param forceEnter 是否强制进入(即使WebSocket连接失败)
|
||||
* @returns Promise<boolean> 是否成功进入应用
|
||||
*/
|
||||
export async function enterApp(reason: string = '正常进入', forceEnter: boolean = true): Promise<boolean> {
|
||||
console.log(`${reason}:开始进入应用流程,尝试建立WebSocket连接...`)
|
||||
|
||||
let wsConnected = false
|
||||
|
||||
try {
|
||||
// 尝试建立WebSocket连接
|
||||
wsConnected = await connectAfterBackendStart()
|
||||
if (wsConnected) {
|
||||
console.log(`${reason}:WebSocket连接建立成功`)
|
||||
} else {
|
||||
console.warn(`${reason}:WebSocket连接建立失败`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`${reason}:WebSocket连接尝试失败:`, error)
|
||||
}
|
||||
|
||||
// 决定是否进入应用
|
||||
if (wsConnected || forceEnter) {
|
||||
if (!wsConnected && forceEnter) {
|
||||
console.warn(`${reason}:WebSocket连接失败,但强制进入应用`)
|
||||
}
|
||||
|
||||
// 跳转到主页
|
||||
router.push('/home')
|
||||
console.log(`${reason}:已进入应用`)
|
||||
return true
|
||||
} else {
|
||||
console.error(`${reason}:WebSocket连接失败且不允许强制进入`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 强行进入应用(忽略WebSocket连接状态)
|
||||
* @param reason 进入原因
|
||||
*/
|
||||
export async function forceEnterApp(reason: string = '强行进入'): Promise<void> {
|
||||
console.log(`🚀 ${reason}:强行进入应用流程开始`)
|
||||
console.log(`📡 ${reason}:尝试强制建立WebSocket连接...`)
|
||||
|
||||
try {
|
||||
// 使用强制连接模式
|
||||
const wsConnected = await forceConnectWebSocket()
|
||||
if (wsConnected) {
|
||||
console.log(`✅ ${reason}:强制WebSocket连接成功!`)
|
||||
} else {
|
||||
console.warn(`⚠️ ${reason}:强制WebSocket连接失败,但继续进入应用`)
|
||||
}
|
||||
|
||||
// 等待一下确保连接状态稳定
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ ${reason}:强制WebSocket连接异常:`, error)
|
||||
}
|
||||
|
||||
// 无论WebSocket是否成功,都进入应用
|
||||
console.log(`🏠 ${reason}:跳转到主页...`)
|
||||
router.push('/home')
|
||||
console.log(`✨ ${reason}:已强行进入应用`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 正常进入应用(需要WebSocket连接成功)
|
||||
* @param reason 进入原因
|
||||
* @returns 是否成功进入
|
||||
*/
|
||||
export async function normalEnterApp(reason: string = '正常进入'): Promise<boolean> {
|
||||
return await enterApp(reason, false)
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import ManualMode from '@/components/initialization/ManualMode.vue'
|
||||
import EnvironmentIncomplete from '@/components/initialization/EnvironmentIncomplete.vue'
|
||||
import type { DownloadProgress } from '@/types/initialization'
|
||||
import { mirrorManager } from '@/utils/mirrorManager'
|
||||
import { forceEnterApp } from '@/utils/appEntry'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -69,8 +70,8 @@ const mirrorConfigStatus = ref({
|
||||
const manualModeRef = ref()
|
||||
|
||||
// 基础功能函数
|
||||
function skipToHome() {
|
||||
router.push('/home')
|
||||
async function skipToHome() {
|
||||
await forceEnterApp('跳过初始化直接进入')
|
||||
}
|
||||
|
||||
function switchToManualMode() {
|
||||
@@ -84,10 +85,14 @@ async function enterApp() {
|
||||
try {
|
||||
// 设置初始化完成标记
|
||||
await setInitialized(true)
|
||||
console.log('设置初始化完成标记,跳转到首页')
|
||||
router.push('/home')
|
||||
console.log('设置初始化完成标记,准备进入应用...')
|
||||
|
||||
// 使用统一的进入应用函数
|
||||
await forceEnterApp('初始化完成后进入')
|
||||
} catch (error) {
|
||||
console.error('进入应用失败:', error)
|
||||
// 即使出错也强制进入
|
||||
await forceEnterApp('初始化失败后强制进入')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
254
websocket_force_test.html
Normal file
254
websocket_force_test.html
Normal file
@@ -0,0 +1,254 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WebSocket强制连接测试</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
button {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
.force-btn {
|
||||
background: #dc3545;
|
||||
}
|
||||
|
||||
.force-btn:hover {
|
||||
background: #c82333;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.success {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
}
|
||||
|
||||
.error {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background: #fff3cd;
|
||||
color: #856404;
|
||||
}
|
||||
|
||||
.log {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>WebSocket强制连接测试工具</h1>
|
||||
|
||||
<div class="status" id="status">状态:未知</div>
|
||||
|
||||
<div>
|
||||
<button onclick="testNormalConnect()">测试正常连接</button>
|
||||
<button onclick="testForceConnect()" class="force-btn">测试强制连接</button>
|
||||
<button onclick="testDirectConnect()">测试直接WebSocket连接</button>
|
||||
<button onclick="checkStatus()">检查连接状态</button>
|
||||
<button onclick="clearLog()">清空日志</button>
|
||||
</div>
|
||||
|
||||
<div class="log" id="log"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let ws = null;
|
||||
|
||||
function log(message, type = 'info') {
|
||||
const logDiv = document.getElementById('log');
|
||||
const timestamp = new Date().toLocaleTimeString();
|
||||
const className = type === 'error' ? 'color: red;' :
|
||||
type === 'success' ? 'color: green;' :
|
||||
type === 'warning' ? 'color: orange;' : '';
|
||||
logDiv.innerHTML += `<div style="${className}">[${timestamp}] ${message}</div>`;
|
||||
logDiv.scrollTop = logDiv.scrollHeight;
|
||||
}
|
||||
|
||||
function updateStatus(message, type = 'info') {
|
||||
const statusDiv = document.getElementById('status');
|
||||
statusDiv.textContent = `状态:${message}`;
|
||||
statusDiv.className = `status ${type}`;
|
||||
}
|
||||
|
||||
// 测试正常连接(通过前端框架)
|
||||
async function testNormalConnect() {
|
||||
log('🔍 测试正常连接模式...');
|
||||
updateStatus('测试正常连接中...', 'warning');
|
||||
|
||||
try {
|
||||
// 这里需要调用前端框架的连接方法
|
||||
if (window.wsDebug && window.wsDebug.normalConnect) {
|
||||
const result = await window.wsDebug.normalConnect();
|
||||
if (result) {
|
||||
log('✅ 正常连接成功', 'success');
|
||||
updateStatus('正常连接成功', 'success');
|
||||
} else {
|
||||
log('❌ 正常连接失败', 'error');
|
||||
updateStatus('正常连接失败', 'error');
|
||||
}
|
||||
} else {
|
||||
log('⚠️ wsDebug.normalConnect 不可用', 'warning');
|
||||
updateStatus('调试功能不可用', 'warning');
|
||||
}
|
||||
} catch (error) {
|
||||
log(`❌ 正常连接异常: ${error}`, 'error');
|
||||
updateStatus('正常连接异常', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 测试强制连接(通过前端框架)
|
||||
async function testForceConnect() {
|
||||
log('🚀 测试强制连接模式...');
|
||||
updateStatus('测试强制连接中...', 'warning');
|
||||
|
||||
try {
|
||||
if (window.wsDebug && window.wsDebug.forceConnect) {
|
||||
const result = await window.wsDebug.forceConnect();
|
||||
if (result) {
|
||||
log('✅ 强制连接成功', 'success');
|
||||
updateStatus('强制连接成功', 'success');
|
||||
} else {
|
||||
log('❌ 强制连接失败', 'error');
|
||||
updateStatus('强制连接失败', 'error');
|
||||
}
|
||||
} else {
|
||||
log('⚠️ wsDebug.forceConnect 不可用', 'warning');
|
||||
updateStatus('调试功能不可用', 'warning');
|
||||
}
|
||||
} catch (error) {
|
||||
log(`❌ 强制连接异常: ${error}`, 'error');
|
||||
updateStatus('强制连接异常', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 测试直接WebSocket连接
|
||||
function testDirectConnect() {
|
||||
log('🔗 测试直接WebSocket连接...');
|
||||
updateStatus('测试直接连接中...', 'warning');
|
||||
|
||||
try {
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
log('⚠️ WebSocket已经连接,先关闭', 'warning');
|
||||
ws.close();
|
||||
}
|
||||
|
||||
ws = new WebSocket('ws://localhost:36163/api/core/ws');
|
||||
|
||||
ws.onopen = () => {
|
||||
log('✅ 直接WebSocket连接成功', 'success');
|
||||
updateStatus('直接连接成功', 'success');
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
log(`📨 收到消息: ${JSON.stringify(data)}`, 'info');
|
||||
} catch (e) {
|
||||
log(`📨 收到消息: ${event.data}`, 'info');
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
log(`❌ WebSocket错误: ${error}`, 'error');
|
||||
updateStatus('直接连接错误', 'error');
|
||||
};
|
||||
|
||||
ws.onclose = (event) => {
|
||||
log(`🔒 WebSocket关闭: code=${event.code}, reason="${event.reason}"`, 'warning');
|
||||
updateStatus('连接已关闭', 'warning');
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
log(`❌ 直接连接异常: ${error}`, 'error');
|
||||
updateStatus('直接连接异常', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// 检查连接状态
|
||||
function checkStatus() {
|
||||
log('🔍 检查连接状态...');
|
||||
|
||||
// 检查直接WebSocket
|
||||
if (ws) {
|
||||
const states = ['CONNECTING', 'OPEN', 'CLOSING', 'CLOSED'];
|
||||
log(`📡 直接WebSocket状态: ${states[ws.readyState]} (${ws.readyState})`);
|
||||
} else {
|
||||
log('📡 直接WebSocket: 未创建');
|
||||
}
|
||||
|
||||
// 检查框架WebSocket
|
||||
if (window.wsDebug) {
|
||||
try {
|
||||
const info = window.wsDebug.getConnectionInfo ? window.wsDebug.getConnectionInfo() : {};
|
||||
log(`🔧 框架WebSocket状态: ${JSON.stringify(info, null, 2)}`);
|
||||
|
||||
const storage = window.wsDebug.getGlobalStorage ? window.wsDebug.getGlobalStorage() : {};
|
||||
log(`💾 全局存储状态: allowNewConnection=${storage.allowNewConnection}, connectionReason="${storage.connectionReason}"`);
|
||||
|
||||
if (window.wsDebug.allowedReasons) {
|
||||
log(`✅ 允许的连接原因: ${JSON.stringify(window.wsDebug.allowedReasons)}`);
|
||||
}
|
||||
} catch (error) {
|
||||
log(`❌ 检查框架状态异常: ${error}`, 'error');
|
||||
}
|
||||
} else {
|
||||
log('🔧 框架WebSocket调试功能不可用');
|
||||
}
|
||||
}
|
||||
|
||||
function clearLog() {
|
||||
document.getElementById('log').innerHTML = '';
|
||||
}
|
||||
|
||||
// 页面加载时检查状态
|
||||
window.addEventListener('load', () => {
|
||||
log('📋 WebSocket强制连接测试工具已加载');
|
||||
checkStatus();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user