miniprogramme/test.js

393 lines
10 KiB
JavaScript
Raw Normal View History

2025-09-12 16:08:17 +08:00
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)
};
});