diff --git a/components/ask/utils.ts b/components/ask/utils.ts index 79f046a..fb1250d 100644 --- a/components/ask/utils.ts +++ b/components/ask/utils.ts @@ -3,7 +3,6 @@ import { Message } from "@/types/ask"; import * as FileSystem from 'expo-file-system'; import * as MediaLibrary from 'expo-media-library'; import { TFunction } from "i18next"; -import { useCallback } from "react"; import { Alert } from 'react-native'; // 实现一个函数,从两个数组中轮流插入新数组 @@ -19,12 +18,12 @@ export const mergeArrays = (arr1: any[], arr2: any[]) => { // 创建新对话并获取消息 -export const createNewConversation = useCallback(async (user_text: string) => { +export const createNewConversation = async (user_text: string) => { const data = await fetchApi("/chat/new", { method: "POST", }); return data -}, []); +}; // 获取对话信息 export const getConversation = async ({ diff --git a/lib/server-api-util.ts b/lib/server-api-util.ts index 7202d05..6ae3205 100644 --- a/lib/server-api-util.ts +++ b/lib/server-api-util.ts @@ -25,7 +25,7 @@ export interface PagedResult { // 获取.env文件中的变量 -export const API_ENDPOINT = Constants.expoConfig?.extra?.API_ENDPOINT || "http://192.168.31.16:31646/api"; +export const API_ENDPOINT = process.env.EXPO_PUBLIC_API_ENDPOINT || Constants.expoConfig?.extra?.API_ENDPOINT; // 更新 access_token 的逻辑 - 用于React组件中 diff --git a/lib/websocket-util.ts b/lib/websocket-util.ts index 70e8290..2314f2e 100644 --- a/lib/websocket-util.ts +++ b/lib/websocket-util.ts @@ -4,7 +4,7 @@ import { TFunction } from 'i18next'; import { Platform } from 'react-native'; // 从环境变量或默认值中定义 WebSocket 端点 -export const WEBSOCKET_ENDPOINT = Constants.expoConfig?.extra?.WEBSOCKET_ENDPOINT || "ws://192.168.31.16:31646/ws/chat"; +export const WEBSOCKET_ENDPOINT = process.env.EXPO_PUBLIC_WEBSOCKET_ENDPOINT || Constants.expoConfig?.extra?.WEBSOCKET_ENDPOINT; export type WebSocketStatus = 'connecting' | 'connected' | 'disconnected' | 'reconnecting'; @@ -34,6 +34,8 @@ class WebSocketManager { private readonly reconnectInterval = 1000; // 初始重连间隔为1秒 private pingIntervalId: ReturnType | null = null; private readonly pingInterval = 30000; // 30秒发送一次心跳 + private isConnecting = false; // 防止并发重复连接 + private connectTimeoutId: ReturnType | null = null; // 连接超时 constructor() { // 这是一个单例类,连接通过调用 connect() 方法来启动 @@ -51,66 +53,91 @@ class WebSocketManager { * 会自动获取并使用存储的认证 token。 */ public async connect() { - if (this.ws && (this.status === 'connected' || this.status === 'connecting')) { - if (this.status === 'connected' || this.status === 'connecting') { - return; - } + // 已连或正在连,直接返回(基于状态的幂等) + if (this.status === 'connected' || this.status === 'connecting' || this.isConnecting) { + return; } + this.isConnecting = true; this.setStatus('connecting'); let token = ""; - if (Platform.OS === 'web') { - token = localStorage.getItem('token') || ""; - } else { - token = await SecureStore.getItemAsync('token') || ""; + try { + if (Platform.OS === 'web') { + token = localStorage.getItem('token') || ""; + } else { + token = await SecureStore.getItemAsync('token') || ""; + } + } catch (e) { + console.error('WebSocket: 获取 token 失败:', e); } if (!token) { console.error('WebSocket: 未找到认证 token,无法连接。'); + this.isConnecting = false; this.setStatus('disconnected'); return; - } else { - console.log('WebSocket: 认证 token:', token); } - const url = `${WEBSOCKET_ENDPOINT}?token=${token}`; - console.log('WebSocket: 连接 URL:', url); - this.ws = new WebSocket(url); + const url = `${WEBSOCKET_ENDPOINT}?token=${encodeURIComponent(token)}`; + console.log('WebSocket: 开始连接到服务器'); + const ws = new WebSocket(url); + this.ws = ws; - this.ws.onopen = () => { + // 连接超时(15s) + if (this.connectTimeoutId) { + clearTimeout(this.connectTimeoutId); + this.connectTimeoutId = null; + } + this.connectTimeoutId = setTimeout(() => { + if (this.ws === ws && ws.readyState !== WebSocket.OPEN) { + try { ws.close(); } catch { /* noop */ } + } + }, 15000); + + ws.onopen = () => { + if (this.connectTimeoutId) { + clearTimeout(this.connectTimeoutId); + this.connectTimeoutId = null; + } console.log('WebSocket connected'); this.setStatus('connected'); this.reconnectAttempts = 0; // 重置重连尝试次数 + this.isConnecting = false; this.startPing(); }; - this.ws.onmessage = (event) => { + ws.onmessage = (event) => { try { const message: WsMessage = JSON.parse(event.data); - // console.log('WebSocket received message:', message) - // 根据消息类型分发 const eventListeners = this.messageListeners.get(message.type); if (eventListeners) { eventListeners.forEach(callback => callback(message)); } - // 可以在这里处理通用的消息,比如 Pong - if (message.type === 'Pong') { - // console.log('Received Pong'); - } } catch (error) { console.error('处理 WebSocket 消息失败:', error); } }; - this.ws.onerror = (error) => { + ws.onerror = (error) => { + if (this.connectTimeoutId) { + clearTimeout(this.connectTimeoutId); + this.connectTimeoutId = null; + } console.error('WebSocket 发生错误:', error); + this.stopPing(); + this.isConnecting = false; }; - this.ws.onclose = () => { + ws.onclose = () => { + if (this.connectTimeoutId) { + clearTimeout(this.connectTimeoutId); + this.connectTimeoutId = null; + } console.log('WebSocket disconnected'); this.ws = null; this.stopPing(); + this.isConnecting = false; // 只有在不是手动断开连接时才重连 if (this.status !== 'disconnected') { this.setStatus('reconnecting'); @@ -141,8 +168,8 @@ class WebSocketManager { * @param message 要发送的消息对象,必须包含 type 字段。 */ public send(message: WsMessage) { - if (this.status !== 'connected' || !this.ws) { - console.error('WebSocket 未连接,无法发送消息。'); + if (!this.ws || this.ws.readyState !== WebSocket.OPEN) { + console.error('WebSocket 未连接或未就绪,无法发送消息。'); return; } this.ws.send(JSON.stringify(message));