|
@@ -1,12 +1,10 @@
|
|
|
/**
|
|
/**
|
|
|
- * 基于 WebSocket + 腾讯 IM 的用户在线状态管理器
|
|
|
|
|
- * 核心思路:
|
|
|
|
|
- * 1. 利用腾讯 IM SDK 感知自身和好友的 IM 连接状态
|
|
|
|
|
- * 2. 通过 WebSocket 向服务端上报 IM 状态变更
|
|
|
|
|
- * 3. 通过 WebSocket 接收服务端推送的其他用户状态变更
|
|
|
|
|
|
|
+ * TIM在线状态管理器 + WebSocket聊天管理器
|
|
|
|
|
+ * 统一管理WebSocket连接、心跳、在线状态、聊天消息
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
import timManager from './tim-manager.js';
|
|
import timManager from './tim-manager.js';
|
|
|
|
|
+import TIM from 'tim-wx-sdk';
|
|
|
|
|
|
|
|
class TIMPresenceManager {
|
|
class TIMPresenceManager {
|
|
|
constructor() {
|
|
constructor() {
|
|
@@ -19,13 +17,21 @@ class TIMPresenceManager {
|
|
|
this.userId = null;
|
|
this.userId = null;
|
|
|
this.statusCallbacks = new Map(); // 存储状态变化回调
|
|
this.statusCallbacks = new Map(); // 存储状态变化回调
|
|
|
this.onlineStatusCache = new Map(); // 缓存在线状态
|
|
this.onlineStatusCache = new Map(); // 缓存在线状态
|
|
|
-
|
|
|
|
|
- // WebSocket服务器地址(通过网关)
|
|
|
|
|
this.wsUrl = 'ws://localhost:8083/ws/chat';
|
|
this.wsUrl = 'ws://localhost:8083/ws/chat';
|
|
|
|
|
|
|
|
// TIM 状态监听器引用(用于清理)
|
|
// TIM 状态监听器引用(用于清理)
|
|
|
this.timStatusListener = null;
|
|
this.timStatusListener = null;
|
|
|
this.timConnectListener = null;
|
|
this.timConnectListener = null;
|
|
|
|
|
+
|
|
|
|
|
+ // 聊天消息相关
|
|
|
|
|
+ this.messageQueue = []; // 消息队列(断线时暂存)
|
|
|
|
|
+ this.maxQueueSize = 100;
|
|
|
|
|
+ this.onMessageCallback = null; // 消息回调
|
|
|
|
|
+ this.onConnectCallback = null; // 连接回调
|
|
|
|
|
+ this.onDisconnectCallback = null; // 断开回调
|
|
|
|
|
+ this.onErrorCallback = null; // 错误回调
|
|
|
|
|
+ this.reconnectCount = 0;
|
|
|
|
|
+ this.maxReconnectCount = 10;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -34,12 +40,16 @@ class TIMPresenceManager {
|
|
|
async init(userId) {
|
|
async init(userId) {
|
|
|
this.userId = userId;
|
|
this.userId = userId;
|
|
|
|
|
|
|
|
- console.log('🚀 初始化 tim-presence-manager,用户ID:', userId);
|
|
|
|
|
|
|
+ console.log('🚀 ========== 初始化 tim-presence-manager ==========');
|
|
|
|
|
+ console.log('🚀 用户ID:', userId);
|
|
|
|
|
+ console.log('🚀 WebSocket URL:', this.wsUrl);
|
|
|
|
|
|
|
|
// 连接 WebSocket(接收服务端推送的状态变更)
|
|
// 连接 WebSocket(接收服务端推送的状态变更)
|
|
|
|
|
+ console.log('🚀 开始连接 WebSocket...');
|
|
|
this.connectWebSocket();
|
|
this.connectWebSocket();
|
|
|
|
|
|
|
|
- console.log('✅ tim-presence-manager 初始化完成');
|
|
|
|
|
|
|
+ console.log('✅ tim-presence-manager 初始化完成(WebSocket连接中...)');
|
|
|
|
|
+ console.log('================================================');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -52,6 +62,15 @@ class TIMPresenceManager {
|
|
|
console.log(' 当前用户ID:', this.userId);
|
|
console.log(' 当前用户ID:', this.userId);
|
|
|
console.log(' WebSocket URL:', wsUrl);
|
|
console.log(' WebSocket URL:', wsUrl);
|
|
|
|
|
|
|
|
|
|
+ // 先关闭旧连接
|
|
|
|
|
+ if (this.ws) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ uni.closeSocket();
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('关闭旧连接失败:', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
this.ws = uni.connectSocket({
|
|
this.ws = uni.connectSocket({
|
|
|
url: wsUrl,
|
|
url: wsUrl,
|
|
|
success: () => {
|
|
success: () => {
|
|
@@ -64,8 +83,8 @@ class TIMPresenceManager {
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // 监听连接打开
|
|
|
|
|
- uni.onSocketOpen((res) => {
|
|
|
|
|
|
|
+ // 使用 SocketTask 对象的监听器(推荐方式)
|
|
|
|
|
+ this.ws.onOpen((res) => {
|
|
|
console.log('🎉 ========== WebSocket 连接成功 ==========');
|
|
console.log('🎉 ========== WebSocket 连接成功 ==========');
|
|
|
console.log(' 响应数据:', res);
|
|
console.log(' 响应数据:', res);
|
|
|
console.log(' 用户ID:', this.userId);
|
|
console.log(' 用户ID:', this.userId);
|
|
@@ -73,19 +92,31 @@ class TIMPresenceManager {
|
|
|
console.log('==========================================');
|
|
console.log('==========================================');
|
|
|
|
|
|
|
|
this.isConnected = true;
|
|
this.isConnected = true;
|
|
|
|
|
+ this.reconnectCount = 0; // 重置重连计数
|
|
|
|
|
+
|
|
|
|
|
+ // 启动心跳
|
|
|
this.startHeartbeat();
|
|
this.startHeartbeat();
|
|
|
|
|
|
|
|
|
|
+ // 发送队列中的消息
|
|
|
|
|
+ this.flushMessageQueue();
|
|
|
|
|
+
|
|
|
// 连接成功后,立即上报当前 TIM 连接状态
|
|
// 连接成功后,立即上报当前 TIM 连接状态
|
|
|
this.reportCurrentTIMStatus();
|
|
this.reportCurrentTIMStatus();
|
|
|
|
|
+
|
|
|
|
|
+ // 触发连接回调
|
|
|
|
|
+ if (this.onConnectCallback) {
|
|
|
|
|
+ this.onConnectCallback();
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// 监听消息
|
|
// 监听消息
|
|
|
- uni.onSocketMessage((res) => {
|
|
|
|
|
|
|
+ this.ws.onMessage((res) => {
|
|
|
|
|
+ console.log('📨 收到WebSocket消息:', res.data);
|
|
|
this.handleMessage(res.data);
|
|
this.handleMessage(res.data);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// 监听错误
|
|
// 监听错误
|
|
|
- uni.onSocketError((err) => {
|
|
|
|
|
|
|
+ this.ws.onError((err) => {
|
|
|
console.error('❌ ========== WebSocket 错误 ==========');
|
|
console.error('❌ ========== WebSocket 错误 ==========');
|
|
|
console.error(' 错误信息:', err);
|
|
console.error(' 错误信息:', err);
|
|
|
console.error(' 错误详情:', JSON.stringify(err));
|
|
console.error(' 错误详情:', JSON.stringify(err));
|
|
@@ -94,11 +125,17 @@ class TIMPresenceManager {
|
|
|
console.error('======================================');
|
|
console.error('======================================');
|
|
|
|
|
|
|
|
this.isConnected = false;
|
|
this.isConnected = false;
|
|
|
|
|
+
|
|
|
|
|
+ // 触发错误回调
|
|
|
|
|
+ if (this.onErrorCallback) {
|
|
|
|
|
+ this.onErrorCallback(err);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
this.scheduleReconnect();
|
|
this.scheduleReconnect();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// 监听关闭
|
|
// 监听关闭
|
|
|
- uni.onSocketClose((res) => {
|
|
|
|
|
|
|
+ this.ws.onClose((res) => {
|
|
|
console.log('🔌 ========== WebSocket 连接关闭 ==========');
|
|
console.log('🔌 ========== WebSocket 连接关闭 ==========');
|
|
|
console.log(' 关闭信息:', res);
|
|
console.log(' 关闭信息:', res);
|
|
|
console.log(' 关闭码:', res?.code);
|
|
console.log(' 关闭码:', res?.code);
|
|
@@ -108,6 +145,12 @@ class TIMPresenceManager {
|
|
|
|
|
|
|
|
this.isConnected = false;
|
|
this.isConnected = false;
|
|
|
this.stopHeartbeat();
|
|
this.stopHeartbeat();
|
|
|
|
|
+
|
|
|
|
|
+ // 触发断开回调
|
|
|
|
|
+ if (this.onDisconnectCallback) {
|
|
|
|
|
+ this.onDisconnectCallback();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
this.scheduleReconnect();
|
|
this.scheduleReconnect();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -406,7 +449,7 @@ class TIMPresenceManager {
|
|
|
const message = typeof data === 'string' ? JSON.parse(data) : data;
|
|
const message = typeof data === 'string' ? JSON.parse(data) : data;
|
|
|
|
|
|
|
|
switch (message.type) {
|
|
switch (message.type) {
|
|
|
- case 'PONG':
|
|
|
|
|
|
|
+ case 'pong': // 改为小写,匹配后端
|
|
|
// 心跳响应
|
|
// 心跳响应
|
|
|
console.log('💓 收到心跳响应');
|
|
console.log('💓 收到心跳响应');
|
|
|
break;
|
|
break;
|
|
@@ -445,11 +488,24 @@ class TIMPresenceManager {
|
|
|
|
|
|
|
|
this.heartbeatTimer = setInterval(() => {
|
|
this.heartbeatTimer = setInterval(() => {
|
|
|
if (this.isConnected) {
|
|
if (this.isConnected) {
|
|
|
|
|
+ console.log('💓 ========== 发送心跳PING ==========');
|
|
|
|
|
+ console.log('💓 用户ID:', this.userId);
|
|
|
|
|
+ console.log('💓 WebSocket连接状态:', this.isConnected);
|
|
|
|
|
+ console.log('💓 发送消息:', {
|
|
|
|
|
+ type: 'ping',
|
|
|
|
|
+ fromUserId: this.userId,
|
|
|
|
|
+ timestamp: Date.now()
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
this.sendMessage({
|
|
this.sendMessage({
|
|
|
- type: 'PING',
|
|
|
|
|
|
|
+ type: 'ping', // 改为小写,匹配后端
|
|
|
fromUserId: this.userId,
|
|
fromUserId: this.userId,
|
|
|
timestamp: Date.now()
|
|
timestamp: Date.now()
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ console.log('💓 ====================================');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.warn('⚠️ WebSocket未连接,跳过心跳');
|
|
|
}
|
|
}
|
|
|
}, this.heartbeatInterval);
|
|
}, this.heartbeatInterval);
|
|
|
}
|
|
}
|
|
@@ -563,6 +619,22 @@ class TIMPresenceManager {
|
|
|
}, this.reconnectInterval);
|
|
}, this.reconnectInterval);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 发送队列中的消息
|
|
|
|
|
+ */
|
|
|
|
|
+ flushMessageQueue() {
|
|
|
|
|
+ if (this.messageQueue.length === 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`📤 发送队列中的 ${this.messageQueue.length} 条消息`);
|
|
|
|
|
+
|
|
|
|
|
+ while (this.messageQueue.length > 0) {
|
|
|
|
|
+ const message = this.messageQueue.shift();
|
|
|
|
|
+ this.sendMessage(message);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 断开连接
|
|
* 断开连接
|
|
|
*/
|
|
*/
|