Parcourir la source

用户在线状态修复

mazhenhang il y a 1 mois
Parent
commit
8af0f2f534

+ 7 - 2
LiangZhiYUMao/pages/index/index.vue

@@ -303,14 +303,19 @@
 			this.loadCharmIndex()
 			this.loadHotActivities()
 			this.loadSuccessCases()
+			// 监听未读消息数更新事件
+			uni.$on('conversationUnreadUpdate', (totalUnread) => {
+				console.log('📬 首页收到未读数更新:', totalUnread);
+				this.$store.dispatch('updateTotalUnread', totalUnread);
+			});
 		},
 
 		onUnload() {
 			// 清理资源
+			uni.$off('conversationUnreadUpdate');
 		},
 onShow() {
-  // 如果需要主动刷新未读数,可以在这里触发
-  this.$store.dispatch('updateTotalUnread', this.unreadCount);
+  // 页面显示时不需要手动刷新,Vuex 会自动响应
 },
 		methods: {
 			// 加载用户信息

+ 151 - 42
LiangZhiYUMao/pages/message/chat.vue

@@ -124,7 +124,10 @@
                 @click.stop="toggleVoicePlay(msg)">
                 <text class="voice-duration">{{ msg.duration }}''</text>
                 <view class="voice-icon-wrapper" :class="{playing: playingVoiceId === msg.messageId}">
-                  <image class="voice-icon" src="/static/voice-icon.png" mode="aspectFit"></image>
+                  <image 
+                    class="voice-icon" 
+                    :src="msg.fromUserId === userId ? 'http://115.190.125.125:9000/static-images/%E6%88%91%E6%96%B9%E8%AF%AD%E9%9F%B3%E6%B6%88%E6%81%AF' : 'http://115.190.125.125:9000/static-images/%E5%AF%B9%E6%96%B9%E8%AF%AD%E9%9F%B3%E6%B6%88%E6%81%AF'" 
+                    mode="aspectFit"></image>
                 </view>
                 <!-- 暂停后的继续播放按钮 -->
                 <view 
@@ -176,7 +179,12 @@
     <view class="input-bar">
       <!-- 语音按钮 -->
       <view class="input-icon" @click="switchInputType">
-        <text>{{ inputType === 'text' ? '🎤' : '⌨️' }}</text>
+        <image 
+          v-if="inputType === 'text'"
+          class="icon-image"
+          src="http://115.190.125.125:9000/static-images/%E8%AF%AD%E9%9F%B3%E6%B6%88%E6%81%AF%E6%8C%89%E9%92%AE"
+          mode="aspectFit" />
+        <text v-else>⌨️</text>
       </view>
       
       <!-- 文本输入 -->
@@ -415,6 +423,9 @@ export default {
     // 监听新消息
     this.listenMessages();
     
+    // 初始化在线状态监听(HTTP 轮询方式)
+    this.initOnlineStatusPolling();
+    
     // 如果有预设消息,自动发送
     if (options.message) {
       const message = decodeURIComponent(options.message);
@@ -1471,8 +1482,44 @@ export default {
           this.isRecording = true;
           this.showVoiceRecording = true;
           this.voiceStartTime = Date.now();
+          this.voiceRecordingTime = 0;
+          this.voiceVolume = 0.3;
           console.log('   - isRecording:', this.isRecording);
           console.log('   - showVoiceRecording:', this.showVoiceRecording);
+          
+          // 启动计时器
+          let volumeDirection = 1;
+          let currentVolume = 0.3;
+          this.voiceRecordingTimer = setInterval(() => {
+            this.voiceRecordingTime = Math.floor((Date.now() - this.voiceStartTime) / 1000);
+            
+            // 检查是否达到60秒,自动停止录音
+            if (this.voiceRecordingTime >= 60) {
+              console.log('⏰ 录音时长达到60秒,自动停止');
+              clearInterval(this.voiceRecordingTimer);
+              this.voiceRecordingTimer = null;
+              this.voiceCanceling = false; // 确保不是取消状态
+              if (this.recorderManager) {
+                this.recorderManager.stop();
+              }
+              return;
+            }
+            
+            // 模拟更真实的音量波动
+            if (Math.random() > 0.2) {
+              currentVolume += volumeDirection * (0.1 + Math.random() * 0.2);
+              if (currentVolume > 1.0) {
+                currentVolume = 1.0;
+                volumeDirection = -1;
+              } else if (currentVolume < 0.4) {
+                currentVolume = 0.4;
+                volumeDirection = 1;
+              }
+              this.voiceVolume = currentVolume;
+            } else {
+              this.voiceVolume = Math.max(0.2, this.voiceVolume * 0.8);
+            }
+          }, 100);
         });
         
         // 录音结束
@@ -1481,6 +1528,12 @@ export default {
           this.isRecording = false;
           this.showVoiceRecording = false;
           
+          // 清除计时器
+          if (this.voiceRecordingTimer) {
+            clearInterval(this.voiceRecordingTimer);
+            this.voiceRecordingTimer = null;
+          }
+          
           // 如果是取消状态,不发送
           if (this.voiceCanceling) {
             console.log('❌ 录音已取消,不发送');
@@ -1518,51 +1571,32 @@ export default {
         // 录音错误
         this.recorderManager.onError((err) => {
           console.error('❌ 录音错误:', err);
+          
+          // 立即清理录音状态和界面
           this.isRecording = false;
           this.showVoiceRecording = false;
-          uni.showToast({
-            title: '录音失败',
-            icon: 'none'
-          });
+          this.voiceCanceling = true;
+          
+          // 清除计时器
+          if (this.voiceRecordingTimer) {
+            clearInterval(this.voiceRecordingTimer);
+            this.voiceRecordingTimer = null;
+          }
+          
+          // 如果是权限错误,不显示任何提示(用户会看到系统权限弹窗)
+          // 其他错误才显示提示
+          if (err.errCode !== 'authorize' && err.errMsg && !err.errMsg.includes('authorize')) {
+            uni.showToast({
+              title: '录音失败',
+              icon: 'none'
+            });
+          }
+          
+          console.log('✅ 已清理录音状态,隐藏录音界面');
         });
       }
       
-      // 立即设置录音状态(不等待onStart回调)
-      this.isRecording = true;
-      this.showVoiceRecording = true;
-      this.voiceStartTime = Date.now();
-      this.voiceRecordingTime = 0;
-      this.voiceVolume = 0.3;
-      console.log('🎬 立即设置录音状态');
-      console.log('   - isRecording:', this.isRecording);
-      console.log('   - showVoiceRecording:', this.showVoiceRecording);
-      
-      // 启动计时器
-      let volumeDirection = 1;
-      let currentVolume = 0.3;
-      this.voiceRecordingTimer = setInterval(() => {
-        this.voiceRecordingTime = Math.floor((Date.now() - this.voiceStartTime) / 1000);
-        
-        // 模拟更真实的音量波动
-        // 随机决定是否有声音(80%概率有声音)
-        if (Math.random() > 0.2) {
-          // 有声音:平滑波动
-          currentVolume += volumeDirection * (0.1 + Math.random() * 0.2);
-          if (currentVolume > 1.0) {
-            currentVolume = 1.0;
-            volumeDirection = -1;
-          } else if (currentVolume < 0.4) {
-            currentVolume = 0.4;
-            volumeDirection = 1;
-          }
-          this.voiceVolume = currentVolume;
-        } else {
-          // 无声音:降低到最小
-          this.voiceVolume = Math.max(0.2, this.voiceVolume * 0.8);
-        }
-      }, 100);
-      
-      // 开始录音
+      // 开始录音(等待 onStart 回调后才显示界面)
       console.log('🎙️ 调用 recorderManager.start()');
       try {
         this.recorderManager.start({
@@ -2054,6 +2088,76 @@ export default {
 		        uni.showToast({ title: error.message || '拉黑失败', icon: 'none' });
 		      }
 		    },
+		    
+		    /**
+		     * 初始化在线状态监听(WebSocket 实时推送 + HTTP 轮询)
+		     */
+		    async initOnlineStatusPolling() {
+		      console.log('🔄 初始化在线状态监听(WebSocket + HTTP)');
+		      
+		      const timPresenceManager = require('@/utils/tim-presence-manager.js').default;
+		      
+		      // 1. 立即查询一次
+		      await this.checkOnlineStatus();
+		      
+		      // 2. 监听 WebSocket 实时推送(实时性强)
+		      this.onlineStatusCallback = (status) => {
+		        console.log(`⚡ 实时收到用户 ${this.targetUserId} 状态变更: ${status}`);
+		        this.isTargetOnline = (status === 'online');
+		      };
+		      
+		      timPresenceManager.onStatusChange(this.targetUserId, this.onlineStatusCallback);
+		      
+		      // 3. 启动 HTTP 轮询作为补充(30秒间隔)
+		      this.onlineStatusTimer = setInterval(() => {
+		        this.checkOnlineStatus();
+		      }, 30000);
+		      
+		      console.log('✅ 在线状态监听已启动(WebSocket 实时 + HTTP 轮询)');
+		    },
+		    
+		    /**
+		     * 查询在线状态
+		     */
+		    async checkOnlineStatus() {
+		      try {
+		        const [err, res] = await uni.request({
+		          url: 'http://localhost:8083/api/online/checkStatus',
+		          method: 'GET',
+		          data: {
+		            userId: this.targetUserId
+		          }
+		        });
+		        
+		        if (!err && res.data && res.data.code === 200) {
+		          this.isTargetOnline = res.data.data.online || false;
+		          console.log(`� 用户 ${this.targetUserId} 在线状态:`, this.isTargetOnline);
+		        }
+		      } catch (error) {
+		        console.error('❌ 查询在线状态失败:', error);
+		      }
+		    }
+  },
+  
+  /**
+   * 页面卸载时清理
+   */
+  onUnload() {
+    console.log('=== 聊天页面卸载 ===');
+    
+    // 清理在线状态轮询
+    if (this.onlineStatusTimer) {
+      clearInterval(this.onlineStatusTimer);
+      this.onlineStatusTimer = null;
+      console.log('✅ 已停止在线状态轮询');
+    }
+    
+    // 清理 WebSocket 监听
+    if (this.onlineStatusCallback) {
+      const timPresenceManager = require('@/utils/tim-presence-manager.js').default;
+      timPresenceManager.offStatusChange(this.targetUserId, this.onlineStatusCallback);
+      console.log('✅ 已清理 WebSocket 监听');
+    }
   }
 };
 </script>
@@ -2427,6 +2531,11 @@ export default {
   font-size: 50rpx;
 }
 
+.input-icon .icon-image {
+  width: 50rpx;
+  height: 50rpx;
+}
+
 .send-button {
   padding: 0 30rpx;
   height: 70rpx;

+ 49 - 52
LiangZhiYUMao/pages/message/index.vue

@@ -397,22 +397,21 @@ export default {
 	// 移除拉黑状态变更监听
 	uni.$off('blacklistUpdated', this.handleBlacklistUpdate);
 	
-	// 清理在线状态监听
+	// 清理在线状态轮询定时器
+	if (this.onlineStatusTimer) {
+	  clearInterval(this.onlineStatusTimer);
+	  this.onlineStatusTimer = null;
+	  console.log('✅ 已停止在线状态轮询');
+	}
+	
+	// 清理 WebSocket 在线状态监听
 	const timPresenceManager = require('@/utils/tim-presence-manager.js').default;
-	const userIdList = [];
 	this.conversations.forEach(conv => {
 	  if (conv.onlineStatusCallback) {
 	    timPresenceManager.offStatusChange(conv.targetUserId, conv.onlineStatusCallback);
-	    userIdList.push(conv.targetUserId);
 	  }
 	});
-	
-	// 取消订阅用户状态
-	if (userIdList.length > 0) {
-	  timPresenceManager.unsubscribeUserStatus(userIdList).catch(err => {
-	    console.error('❌ 取消订阅失败:', err);
-	  });
-	}
+	console.log('✅ 已清理 WebSocket 监听');
   },
   
   methods: {
@@ -1228,34 +1227,20 @@ export default {
     },
     
     /**
-     * 初始化在线状态监听
+     * 初始化在线状态监听(WebSocket 实时推送 + HTTP 轮询)
      */
     async initOnlineStatusListener() {
-      console.log('👂 初始化在线状态监听');
+      console.log('🔄 初始化在线状态监听(WebSocket + HTTP)');
       
       const timPresenceManager = require('@/utils/tim-presence-manager.js').default;
       
-      // 收集所有需要订阅的用户ID
-      const userIdList = this.conversations.map(conv => conv.targetUserId).filter(id => id);
-      
-      if (userIdList.length === 0) {
-        console.log('⚠️ 没有需要订阅的用户');
-        return;
-      }
+      // 1. 立即查询一次
+      await this.refreshOnlineStatus();
       
-      // 使用 TIM 原生能力订阅用户状态
-      try {
-        await timPresenceManager.subscribeUserStatus(userIdList);
-        console.log('✅ 已订阅用户状态:', userIdList);
-      } catch (error) {
-        console.error('❌ 订阅用户状态失败:', error);
-      }
-      
-      // 监听所有用户的在线状态变化
+      // 2. 为每个会话用户注册 WebSocket 监听
       this.conversations.forEach(conv => {
-        // 创建回调并保存引用
         const callback = (status) => {
-          console.log(`👤 用户 ${conv.targetUserId} 状态变更: ${status}`);
+          console.log(`⚡ 实时收到用户 ${conv.targetUserId} 状态变更: ${status}`);
           
           // 更新会话列表中的在线状态
           const conversation = this.conversations.find(c => c.targetUserId === conv.targetUserId);
@@ -1264,35 +1249,45 @@ export default {
           }
         };
         
-        // 保存回调引用以便后续清理
+        // 保存回调引用
         conv.onlineStatusCallback = callback;
         
         // 注册监听
         timPresenceManager.onStatusChange(conv.targetUserId, callback);
-        
-        // 获取缓存的状态并初始化
-        const cachedStatus = timPresenceManager.getCachedStatus(conv.targetUserId);
-        if (cachedStatus !== null) {
-          this.$set(conv, 'isOnline', cachedStatus);
-        }
       });
+      
+      // 3. 启动 HTTP 轮询作为补充(30秒间隔)
+      this.onlineStatusTimer = setInterval(() => {
+        this.refreshOnlineStatus();
+      }, 30000);
+      
+      console.log('✅ 在线状态监听已启动(WebSocket 实时 + HTTP 轮询)');
     },
     
     /**
-     * 刷新在线状态
+     * 刷新在线状态(HTTP 查询方式)
      */
     async refreshOnlineStatus() {
-      console.log('🔄 刷新在线状态');
-      
-      const timPresenceManager = require('@/utils/tim-presence-manager.js').default;
+      console.log('🔄 刷新在线状态(HTTP 查询)');
       
-      // 从缓存中获取在线状态
-      this.conversations.forEach(conv => {
-        const cachedStatus = timPresenceManager.getCachedStatus(conv.targetUserId);
-        if (cachedStatus !== null) {
-          this.$set(conv, 'isOnline', cachedStatus);
+      // 批量查询所有会话用户的在线状态
+      for (const conv of this.conversations) {
+        try {
+          const [err, res] = await uni.request({
+            url: 'http://localhost:8083/api/online/checkStatus',
+            method: 'GET',
+            data: {
+              userId: conv.targetUserId
+            }
+          });
+          
+          if (!err && res.data && res.data.code === 200) {
+            this.$set(conv, 'isOnline', res.data.data.online || false);
+          }
+        } catch (error) {
+          console.error(`❌ 查询用户 ${conv.targetUserId} 在线状态失败:`, error);
         }
-      });
+      }
       
       console.log('✅ 在线状态刷新完成');
     },
@@ -1628,13 +1623,15 @@ export default {
     
     .online-badge {
       position: absolute;
-      bottom: 0;
-      right: 0;
-      width: 24rpx;
-      height: 24rpx;
+      bottom: 2rpx;
+      right: 2rpx;
+      width: 26rpx;
+      height: 26rpx;
       background-color: #07C160;
-      border: 3rpx solid #FFFFFF;
+      border: 4rpx solid #FFFFFF;
       border-radius: 50%;
+      box-shadow: 0 0 8rpx rgba(7, 193, 96, 0.5);
+      z-index: 10;
     }
     
     .unread-badge {

+ 0 - 363
LiangZhiYUMao/utils/presence-manager.js

@@ -1,363 +0,0 @@
-/**
- * 用户在线状态管理器
- * 基于WebSocket心跳检测实现
- */
-
-class PresenceManager {
-  constructor() {
-    this.ws = null;
-    this.heartbeatTimer = null;
-    this.reconnectTimer = null;
-    this.heartbeatInterval = 30000; // 30秒心跳
-    this.reconnectInterval = 5000; // 5秒重连
-    this.isConnected = false;
-    this.userId = null;
-    this.statusCallbacks = new Map(); // 存储状态变化回调
-    this.onlineStatusCache = new Map(); // 缓存在线状态
-    this.messageCallbacks = []; // 存储消息回调(用于已读回执等)
-    
-    // WebSocket服务器地址(使用实际的聊天WebSocket服务)
-    this.wsUrl = 'ws://localhost:8083/ws/chat';
-    this.httpUrl = 'http://localhost:8083/api/online'; // HTTP API地址
-  }
-  
-  /**
-   * 连接WebSocket
-   * @param {String} userId 当前用户ID
-   */
-  connect(userId) {
-    if (this.isConnected || !userId) {
-      return;
-    }
-    
-    this.userId = userId;
-    
-    try {
-      // uni-app的WebSocket API
-      this.ws = uni.connectSocket({
-        url: `${this.wsUrl}?userId=${userId}`,
-        success: () => {
-          console.log('🔌 WebSocket连接请求已发送');
-        },
-        fail: (err) => {
-          console.error('❌ WebSocket连接失败:', err);
-          this.scheduleReconnect();
-        }
-      });
-      
-      // 监听连接打开
-      uni.onSocketOpen(() => {
-        console.log('✅ WebSocket已连接');
-        this.isConnected = true;
-        this.startHeartbeat();
-      });
-      
-      // 监听消息
-      uni.onSocketMessage((res) => {
-        this.handleMessage(res.data);
-      });
-      
-      // 监听错误
-      uni.onSocketError((err) => {
-        console.error('❌ WebSocket错误:', err);
-        this.isConnected = false;
-        this.scheduleReconnect();
-      });
-      
-      // 监听关闭
-      uni.onSocketClose(() => {
-        console.log('🔌 WebSocket已关闭');
-        this.isConnected = false;
-        this.stopHeartbeat();
-        this.scheduleReconnect();
-      });
-      
-    } catch (error) {
-      console.error('❌ WebSocket连接异常:', error);
-      this.scheduleReconnect();
-    }
-  }
-  
-  /**
-   * 处理接收到的消息
-   */
-  handleMessage(data) {
-    try {
-      const message = typeof data === 'string' ? JSON.parse(data) : data;
-      
-      switch (message.type) {
-        case 'PONG':
-          // 心跳响应
-          console.log('💓 收到心跳响应');
-          break;
-          
-        case 'ONLINE':
-          // 用户上线通知
-          if (message.fromUserId) {
-            this.onlineStatusCache.set(String(message.fromUserId), true);
-            this.notifyStatusChange(String(message.fromUserId), 'online');
-          }
-          break;
-          
-        case 'OFFLINE':
-          // 用户离线通知
-          if (message.fromUserId) {
-            this.onlineStatusCache.set(String(message.fromUserId), false);
-            this.notifyStatusChange(String(message.fromUserId), 'offline');
-          }
-          break;
-          
-        case 'STATUS_UPDATE':
-          // 用户状态更新
-          if (message.userId) {
-            const status = message.online ? 'online' : 'offline';
-            this.onlineStatusCache.set(String(message.userId), message.online);
-            this.notifyStatusChange(String(message.userId), status);
-          }
-          break;
-          
-        default:
-          // 其他消息类型,调用注册的回调
-          if (this.messageCallbacks && this.messageCallbacks.length > 0) {
-            this.messageCallbacks.forEach(callback => {
-              try {
-                callback(message);
-              } catch (err) {
-                console.error('❌ 消息回调执行失败:', err);
-              }
-            });
-          }
-          break;
-      }
-    } catch (error) {
-      console.error('❌ 消息解析失败:', error);
-    }
-  }
-  
-  /**
-   * 通过HTTP API查询用户在线状态
-   * @param {String} userId 目标用户ID
-   * @return {Promise<Boolean>} 是否在线
-   */
-  async queryOnlineStatus(userId) {
-    try {
-      const [err, res] = await uni.request({
-        url: `${this.httpUrl}/checkStatus`,
-        method: 'GET',
-        data: {
-          userId: userId
-        }
-      });
-      
-      if (err) {
-        console.error('❌ 查询在线状态失败:', err);
-        return false;
-      }
-      
-      if (res.data && res.data.code === 200) {
-        const isOnline = res.data.data.online || false;
-        this.onlineStatusCache.set(String(userId), isOnline);
-        return isOnline;
-      }
-      
-      return false;
-    } catch (error) {
-      console.error('❌ 查询在线状态异常:', error);
-      return false;
-    }
-  }
-  
-  /**
-   * 获取缓存的在线状态
-   * @param {String} userId 目标用户ID
-   * @return {Boolean|null} 在线状态,null表示未知
-   */
-  getCachedStatus(userId) {
-    return this.onlineStatusCache.get(String(userId)) || null;
-  }
-  
-  /**
-   * 启动心跳
-   */
-  startHeartbeat() {
-    this.stopHeartbeat();
-    
-    this.heartbeatTimer = setInterval(() => {
-      if (this.isConnected) {
-        this.sendMessage({
-          type: 'PING',
-          fromUserId: this.userId,
-          timestamp: Date.now()
-        });
-      }
-    }, this.heartbeatInterval);
-  }
-  
-  /**
-   * 停止心跳
-   */
-  stopHeartbeat() {
-    if (this.heartbeatTimer) {
-      clearInterval(this.heartbeatTimer);
-      this.heartbeatTimer = null;
-    }
-  }
-  
-  /**
-   * 发送消息
-   */
-  sendMessage(data) {
-    if (!this.isConnected) {
-      console.warn('⚠️ WebSocket未连接,无法发送消息');
-      return;
-    }
-    
-    try {
-      uni.sendSocketMessage({
-        data: JSON.stringify(data),
-        fail: (err) => {
-          console.error('❌ 发送消息失败:', err);
-        }
-      });
-    } catch (error) {
-      console.error('❌ 发送消息异常:', error);
-    }
-  }
-  
-  /**
-   * 订阅用户状态
-   * @param {String} targetUserId 目标用户ID
-   */
-  subscribeUser(targetUserId) {
-    if (!this.isConnected || !targetUserId) {
-      return;
-    }
-    
-    this.sendMessage({
-      type: 'SUBSCRIBE',
-      fromUserId: this.userId,
-      toUserId: targetUserId
-    });
-  }
-  
-  /**
-   * 取消订阅用户状态
-   * @param {String} targetUserId 目标用户ID
-   */
-  unsubscribeUser(targetUserId) {
-    if (!this.isConnected || !targetUserId) {
-      return;
-    }
-    
-    this.sendMessage({
-      type: 'UNSUBSCRIBE',
-      fromUserId: this.userId,
-      toUserId: targetUserId
-    });
-  }
-  
-  /**
-   * 监听用户状态变化
-   * @param {String} targetUserId 目标用户ID
-   * @param {Function} callback 回调函数 (status) => {}
-   */
-  onStatusChange(targetUserId, callback) {
-    if (!this.statusCallbacks.has(targetUserId)) {
-      this.statusCallbacks.set(targetUserId, []);
-    }
-    this.statusCallbacks.get(targetUserId).push(callback);
-    
-    // 订阅该用户
-    this.subscribeUser(targetUserId);
-  }
-  
-  /**
-   * 移除状态监听
-   * @param {String} targetUserId 目标用户ID
-   * @param {Function} callback 回调函数
-   */
-  offStatusChange(targetUserId, callback) {
-    if (!this.statusCallbacks.has(targetUserId)) {
-      return;
-    }
-    
-    const callbacks = this.statusCallbacks.get(targetUserId);
-    const index = callbacks.indexOf(callback);
-    if (index > -1) {
-      callbacks.splice(index, 1);
-    }
-    
-    // 如果没有回调了,取消订阅
-    if (callbacks.length === 0) {
-      this.statusCallbacks.delete(targetUserId);
-      this.unsubscribeUser(targetUserId);
-    }
-  }
-  
-  /**
-   * 通知状态变化
-   */
-  notifyStatusChange(userId, status) {
-    console.log(`👤 用户 ${userId} 状态变更: ${status}`);
-    
-    const callbacks = this.statusCallbacks.get(userId);
-    if (callbacks && callbacks.length > 0) {
-      callbacks.forEach(callback => {
-        try {
-          callback(status);
-        } catch (error) {
-          console.error('❌ 状态回调执行失败:', error);
-        }
-      });
-    }
-  }
-  
-  /**
-   * 计划重连
-   */
-  scheduleReconnect() {
-    if (this.reconnectTimer) {
-      return;
-    }
-    
-    console.log('🔄 将在5秒后重连...');
-    this.reconnectTimer = setTimeout(() => {
-      this.reconnectTimer = null;
-      if (!this.isConnected && this.userId) {
-        console.log('🔄 尝试重连...');
-        this.connect(this.userId);
-      }
-    }, this.reconnectInterval);
-  }
-  
-  /**
-   * 断开连接
-   */
-  disconnect() {
-    this.stopHeartbeat();
-    
-    if (this.reconnectTimer) {
-      clearTimeout(this.reconnectTimer);
-      this.reconnectTimer = null;
-    }
-    
-    if (this.isConnected) {
-      uni.closeSocket();
-      this.isConnected = false;
-    }
-    
-    this.statusCallbacks.clear();
-    this.messageCallbacks = []; // 清空消息回调
-    this.userId = null;
-  }
-  
-  /**
-   * 获取连接状态
-   */
-  getConnectionStatus() {
-    return this.isConnected;
-  }
-}
-
-// 导出单例
-export default new PresenceManager();

+ 19 - 4
LiangZhiYUMao/utils/tim-manager.js

@@ -214,17 +214,32 @@ class TIMManager {
     
     console.log('📊 总未读消息数:', totalUnread);
     
-    // 更新到全局状态(Vuex)
+    // 更新到全局状态(Vuex)- 多种方式确保更新成功
     try {
-      // 使用 getApp() 获取全局实例
+      // 方式1: 使用 getApp() 获取全局实例
       const app = getApp();
       if (app && app.$store) {
         app.$store.dispatch('updateTotalUnread', totalUnread);
-        console.log('✅ 已更新全局未读数到 Vuex');
+        console.log('✅ 已更新全局未读数到 Vuex (getApp):', totalUnread);
+      } else {
+        console.warn('⚠️ getApp() 未获取到 $store');
       }
       
-      // 同时触发自定义事件,通知所有页面
+      // 方式2: 直接导入 store(备用方案)
+      try {
+        const store = require('../store/index.js').default;
+        if (store) {
+          store.dispatch('updateTotalUnread', totalUnread);
+          console.log('✅ 已更新全局未读数到 Vuex (require):', totalUnread);
+        }
+      } catch (e) {
+        console.warn('⚠️ require store 失败:', e.message);
+      }
+      
+      // 方式3: 触发自定义事件,通知所有页面
       uni.$emit('conversationUnreadUpdate', totalUnread);
+      console.log('✅ 已触发 conversationUnreadUpdate 事件:', totalUnread);
+      
     } catch (error) {
       console.error('❌ 更新全局未读数失败:', error);
     }

+ 112 - 15
LiangZhiYUMao/utils/tim-presence-manager.js

@@ -29,24 +29,17 @@ class TIMPresenceManager {
   }
   
   /**
-   * 初始化并连接
-   * @param {String} userId 当前用户ID
+   * 初始化
    */
   async init(userId) {
-    if (this.isConnected || !userId) {
-      return;
-    }
-    
     this.userId = userId;
     
-    // 1. 建立 WebSocket 连接
-    this.connectWebSocket();
+    console.log('🚀 初始化 tim-presence-manager,用户ID:', userId);
     
-    // 2. 监听 TIM 连接状态变更
-    this.listenTIMConnectionStatus();
+    // 连接 WebSocket(接收服务端推送的状态变更
+    this.connectWebSocket();
     
-    // 3. 监听 TIM 用户状态更新
-    this.listenTIMUserStatus();
+    console.log('✅ tim-presence-manager 初始化完成');
   }
   
   /**
@@ -150,17 +143,27 @@ class TIMPresenceManager {
     
     // 监听用户状态更新事件
     this.timStatusListener = (event) => {
-      console.log('👥 收到 TIM 用户状态更新:', event.data);
+      console.log('=== 👥 收到 TIM 用户状态更新事件 ===');
+      console.log('   - 事件数据:', JSON.stringify(event.data, null, 2));
       
       const { userStatusList } = event.data;
       
       if (userStatusList && userStatusList.length > 0) {
+        console.log(`   - 状态变更用户数: ${userStatusList.length}`);
+        
         userStatusList.forEach(item => {
           const userId = item.userID;
+          console.log(`   - 用户 ${userId}:`);
+          console.log(`     原始状态类型: ${item.statusType}`);
+          console.log(`     USER_STATUS_ONLINE: ${TIM.TYPES.USER_STATUS_ONLINE}`);
+          console.log(`     USER_STATUS_OFFLINE: ${TIM.TYPES.USER_STATUS_OFFLINE}`);
+          
           const status = item.statusType === TIM.TYPES.USER_STATUS_ONLINE ? 'online' : 'offline';
+          console.log(`     判断结果: ${status}`);
           
           // 更新本地缓存
           this.onlineStatusCache.set(String(userId), status === 'online');
+          console.log(`     已更新缓存: ${status === 'online'}`);
           
           // 通知状态变化
           this.notifyStatusChange(String(userId), status);
@@ -168,11 +171,15 @@ class TIMPresenceManager {
           // 也可以通过 WS 上报给服务端,保证服务端状态一致
           this.reportFriendStatus(userId, status);
         });
+      } else {
+        console.warn('   ⚠️ userStatusList 为空或不存在');
       }
+      console.log('=== 状态更新处理完成 ===');
     };
     
     timManager.tim.on(TIM.EVENT.USER_STATUS_UPDATED, this.timStatusListener);
-    console.log('✅ 已监听 TIM 用户状态更新');
+    console.log('✅ 已监听 TIM 用户状态更新事件');
+    console.log('   - 事件名称:', TIM.EVENT.USER_STATUS_UPDATED);
   }
   
   /**
@@ -234,32 +241,121 @@ class TIMPresenceManager {
   async subscribeUserStatus(userIdList) {
     if (!timManager.tim || !timManager.isLogin) {
       console.warn('⚠️ TIM 未登录,无法订阅用户状态');
+      console.warn('   - timManager.tim:', !!timManager.tim);
+      console.warn('   - timManager.isLogin:', timManager.isLogin);
+      
+      // 使用 HTTP 轮询作为备用方案
+      console.log('💡 启用 HTTP 轮询备用方案');
+      this.startHttpPolling(userIdList);
       return;
     }
     
+    console.log('📡 开始订阅用户状态...');
+    console.log('   - 订阅用户列表:', userIdList);
+    console.log('   - TIM SDK 版本:', timManager.tim.VERSION);
+    
     try {
       const result = await timManager.tim.subscribeUserStatus({
         userIDList: userIdList.map(id => String(id))
       });
       
-      console.log('✅ 订阅用户状态成功:', result);
+      console.log('✅ 订阅用户状态成功!');
+      console.log('   - 完整结果:', JSON.stringify(result, null, 2));
       
       // 订阅成功后,初始化这些用户的状态
       if (result.data && result.data.successUserList) {
+        console.log('📋 成功订阅的用户列表:', result.data.successUserList.length, '个');
+        
         result.data.successUserList.forEach(user => {
+          console.log(`   - 用户 ${user.userID}:`);
+          console.log(`     状态类型: ${user.statusType}`);
+          console.log(`     USER_STATUS_ONLINE 常量:`, timManager.tim.TIM.TYPES.USER_STATUS_ONLINE);
+          console.log(`     USER_STATUS_OFFLINE 常量:`, timManager.tim.TIM.TYPES.USER_STATUS_OFFLINE);
+          
           const status = user.statusType === timManager.tim.TIM.TYPES.USER_STATUS_ONLINE ? 'online' : 'offline';
+          console.log(`     最终判断状态: ${status}`);
+          
           this.onlineStatusCache.set(String(user.userID), status === 'online');
           this.notifyStatusChange(String(user.userID), status);
         });
       }
       
+      // 检查失败列表
+      if (result.data && result.data.failureUserList && result.data.failureUserList.length > 0) {
+        console.warn('⚠️ 部分用户订阅失败:', result.data.failureUserList);
+      }
+      
       return result;
     } catch (error) {
       console.error('❌ 订阅用户状态失败:', error);
+      console.error('   - 错误详情:', error.message);
+      console.error('   - 错误代码:', error.code);
+      
+      // 如果订阅失败,启用 HTTP 轮询备用方案
+      if (error.code === 70402 || error.code === 70403) {
+        console.log('💡 TIM 在线状态功能未开启,启用 HTTP 轮询备用方案');
+        this.startHttpPolling(userIdList);
+      }
+      
       throw error;
     }
   }
   
+  /**
+   * HTTP 轮询备用方案(当 TIM 订阅失败时使用)
+   * @param {Array<String>} userIdList 用户ID列表
+   */
+  startHttpPolling(userIdList) {
+    console.log('🔄 启动 HTTP 轮询,间隔 30 秒');
+    
+    // 立即查询一次
+    this.pollUserStatus(userIdList);
+    
+    // 每 30 秒轮询一次
+    this.pollingTimer = setInterval(() => {
+      this.pollUserStatus(userIdList);
+    }, 30000);
+  }
+  
+  /**
+   * 轮询用户状态
+   */
+  async pollUserStatus(userIdList) {
+    for (const userId of userIdList) {
+      try {
+        const [err, res] = await uni.request({
+          url: 'http://localhost:8083/api/online/checkStatus',
+          method: 'GET',
+          data: { userId }
+        });
+        
+        if (!err && res.data && res.data.code === 200) {
+          const isOnline = res.data.data.online || false;
+          const oldStatus = this.onlineStatusCache.get(String(userId));
+          
+          // 只有状态变化时才通知
+          if (oldStatus !== isOnline) {
+            this.onlineStatusCache.set(String(userId), isOnline);
+            this.notifyStatusChange(String(userId), isOnline ? 'online' : 'offline');
+          }
+        }
+      } catch (error) {
+        console.error(`查询用户 ${userId} 状态失败:`, error);
+      }
+    }
+  }
+  
+  /**
+   * 停止 HTTP 轮询
+   */
+  stopHttpPolling() {
+    if (this.pollingTimer) {
+      clearInterval(this.pollingTimer);
+      this.pollingTimer = null;
+      console.log('🛑 已停止 HTTP 轮询');
+    }
+  }
+  
   /**
    * 取消订阅用户状态
    * @param {Array<String>} userIdList 用户ID列表
@@ -449,6 +545,7 @@ class TIMPresenceManager {
    */
   disconnect() {
     this.stopHeartbeat();
+    this.stopHttpPolling(); // 停止 HTTP 轮询
     
     if (this.reconnectTimer) {
       clearTimeout(this.reconnectTimer);