miniprogramme/test.js
2025-09-12 16:08:17 +08:00

393 lines
No EOL
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { defineStore } from 'pinia'
export const useWebSocketStore = defineStore('websocket', () => {
let socketTask = null;
let reconnectTimer = null;
let heartbeatTimer = null;
let reconnectAttempts = 0;
const MAX_RECONNECT_ATTEMPTS = 5;
const HEARTBEAT_INTERVAL = 30000; // 30秒心跳
// 连接状态
const connectionState = ref('disconnected'); // disconnected, connecting, connected, reconnecting
/**
* 建立WebSocket连接
* @param {Object} registerInfo - 注册信息
* @param {string} registerInfo.device_id - 设备ID
* @param {string} registerInfo.token - JWT Token
*/
const buildWebSocket = async (registerInfo) => {
try {
// 确保先关闭已有连接
if (socketTask) {
socketTask.close({ code: 1000, reason: '重新连接' });
socketTask = null;
}
// 清除重连定时器
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
connectionState.value = 'connecting';
// WebSocket地址
const wsUrl = `wss://api.faxianwo.me/api/v1/ws?device_id=${registerInfo.device_id}`;
console.log('开始连接WebSocket:', wsUrl);
// uniapp WebSocket连接配置
socketTask = uni.connectSocket({
url: wsUrl,
header: {
'Authorization': `Bearer ${registerInfo.token}`,
'Content-Type': 'application/json'
},
// 移除protocols参数
success: () => {
console.log('WebSocket连接请求发送成功');
},
fail: (error) => {
console.error('WebSocket连接请求失败:', error);
connectionState.value = 'disconnected';
handleConnectionError(error);
}
});
// 连接成功事件
socketTask.onOpen((response) => {
console.log('WebSocket连接成功:', response);
connectionState.value = 'connected';
reconnectAttempts = 0; // 重置重连次数
// 发送认证确认消息
sendAuthConfirmation();
// 开始心跳
startHeartbeat();
// 显示连接成功提示
uni.showToast({
title: '连接成功',
icon: 'success',
duration: 2000
});
});
// 接收消息事件
socketTask.onMessage((message) => {
try {
const data = JSON.parse(message.data);
console.log('收到WebSocket消息:', data);
handleIncomingMessage(data);
} catch (error) {
console.error('解析WebSocket消息失败:', error, message.data);
}
});
// 连接错误事件
socketTask.onError((error) => {
console.error('WebSocket连接错误:', error);
connectionState.value = 'disconnected';
stopHeartbeat();
handleConnectionError(error);
});
// 连接关闭事件
socketTask.onClose((closeEvent) => {
console.log('WebSocket连接关闭:', closeEvent);
connectionState.value = 'disconnected';
stopHeartbeat();
// 如果不是主动关闭,尝试重连
if (closeEvent.code !== 1000 && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
attemptReconnect(registerInfo);
}
});
return socketTask;
} catch (error) {
console.error('建立WebSocket连接异常:', error);
connectionState.value = 'disconnected';
return null;
}
};
/**
* 发送认证确认消息
*/
const sendAuthConfirmation = () => {
if (!socketTask || connectionState.value !== 'connected') {
console.warn('WebSocket未连接无法发送认证确认');
return;
}
const authMessage = {
type: 'auth_confirm',
id: `auth_${Date.now()}`,
data: {
timestamp: Date.now()
}
};
socketTask.send({
data: JSON.stringify(authMessage),
success: () => console.log('认证确认发送成功'),
fail: (err) => console.error('认证确认发送失败:', err)
});
};
/**
* 开始心跳检测
*/
const startHeartbeat = () => {
stopHeartbeat(); // 先清除已有的心跳
heartbeatTimer = setInterval(() => {
if (socketTask && connectionState.value === 'connected') {
const heartbeatMessage = {
type: 'heartbeat',
id: `hb_${Date.now()}`,
data: {
timestamp: Date.now()
}
};
socketTask.send({
data: JSON.stringify(heartbeatMessage),
success: () => console.log('心跳发送成功'),
fail: (err) => {
console.error('心跳发送失败:', err);
// 心跳失败可能表示连接有问题
connectionState.value = 'disconnected';
}
});
}
}, HEARTBEAT_INTERVAL);
};
/**
* 停止心跳检测
*/
const stopHeartbeat = () => {
if (heartbeatTimer) {
clearInterval(heartbeatTimer);
heartbeatTimer = null;
}
};
/**
* 处理接收到的消息
*/
const handleIncomingMessage = (message) => {
switch (message.type) {
case 'new_message':
// 处理新消息
console.log('收到新消息:', message.data);
break;
case 'message_read':
// 处理已读回执
console.log('消息已读:', message.data);
break;
case 'message_recalled':
// 处理消息撤回
console.log('消息撤回:', message.data);
break;
case 'heartbeat_response':
// 心跳响应
console.log('收到心跳响应');
break;
default:
console.log('收到其他类型消息:', message);
}
};
/**
* 发送消息的通用方法
*/
const sendMessage = (messageType, messageData) => {
if (!socketTask || connectionState.value !== 'connected') {
console.warn('WebSocket未连接无法发送消息');
return false;
}
const message = {
type: messageType,
id: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
data: messageData
};
socketTask.send({
data: JSON.stringify(message),
success: () => console.log(` ${messageType} 发送成功`),
fail: (err) => console.error(` ${messageType} 发送失败:`, err)
});
return true;
};
/**
* 发送聊天消息
*/
const sendChatMessage = (receiverId, content, msgType = 0, chatType = 0) => {
return sendMessage('send_message', {
receiverId: String(receiverId), // 确保是字符串格式
chatType,
msgType,
content,
timestamp: Date.now()
});
};
/**
* 处理连接错误
*/
const handleConnectionError = (error) => {
let errorMessage = '连接错误';
// 完善的错误码处理
switch (error.errCode) {
case 1000:
errorMessage = '正常关闭';
break;
case 1001:
errorMessage = '连接断开';
break;
case 1002:
errorMessage = '协议错误';
break;
case 1003:
errorMessage = '数据格式错误';
break;
case 1006:
errorMessage = '异常断开';
break;
case 1011:
errorMessage = '服务器错误';
break;
case 1012:
errorMessage = '服务重启';
break;
default:
console.log(`未知错误代码: ${error.errCode}`);
errorMessage = `连接错误 (${error.errCode})`;
}
console.error(` ${errorMessage}:`, error);
// 只有在非正常关闭时才显示错误提示
if (error.errCode !== 1000) {
uni.showToast({
title: errorMessage,
icon: 'none',
duration: 3000
});
}
};
/**
* 重连机制
*/
const attemptReconnect = (registerInfo) => {
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
console.error('已达到最大重连次数,停止重连');
uni.showToast({
title: '连接失败,请检查网络',
icon: 'none',
duration: 3000
});
return;
}
reconnectAttempts++;
connectionState.value = 'reconnecting';
// 指数退避策略
const delay = Math.min(2000 * Math.pow(1.5, reconnectAttempts), 30000);
console.log(` 将在 ${delay}ms 后尝试重连 (第${reconnectAttempts}次)`);
reconnectTimer = setTimeout(async () => {
console.log(`开始重连 (第${reconnectAttempts}次)`);
try {
const task = await buildWebSocket(registerInfo);
if (task) {
console.log('重连成功');
}
} catch (error) {
console.error(`重连失败 (第${reconnectAttempts}次):`, error);
// 继续尝试重连
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
attemptReconnect(registerInfo);
}
}
}, delay);
};
/**
* 关闭WebSocket连接
*/
const closeWebSocket = () => {
console.log('主动关闭WebSocket连接');
// 停止心跳
stopHeartbeat();
// 清除重连定时器
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
// 重置重连计数器
reconnectAttempts = 0;
// 关闭连接
if (socketTask) {
socketTask.close({
code: 1000, // 正常关闭
reason: '用户主动关闭',
success: () => {
console.log('WebSocket已关闭');
connectionState.value = 'disconnected';
},
fail: (err) => console.error('关闭WebSocket失败:', err)
});
socketTask = null;
}
};
/**
* 获取连接状态
*/
const getConnectionState = () => connectionState.value;
/**
* 检查是否已连接
*/
const isConnected = () => connectionState.value === 'connected';
return {
// 主要方法
buildWebSocket,
closeWebSocket,
sendMessage,
sendChatMessage,
// 状态查询
getConnectionState,
isConnected,
// 响应式状态用于UI绑定
connectionState: readonly(connectionState)
};
});