فهرست منبع

用户和发信息红娘

mazhenhang 1 ماه پیش
والد
کامیت
3a30a3e844

+ 17 - 6
LiangZhiYUMao/pages/matchmaker-workbench/message.vue

@@ -139,7 +139,7 @@
 					
 					// 3. 获取 UserSig
 					const sigRes = await uni.request({
-						url: `http://localhost:1004/api/im/getUserSig?userId=${this.imUserId}`,
+						url: `http://localhost:8083/api/im/getUserSig?userId=${this.imUserId}`,
 						method: 'GET'
 					})
 					
@@ -148,16 +148,25 @@
 						return
 					}
 					
-					const userSig = sigRes[1].data.data
-					console.log('✅ 获取到 UserSig')
+					const userSig = sigRes[1].data.data.userSig
+					console.log('✅ 获取到 UserSig:', userSig ? '成功' : '失败')
 					
-					// 4. 登录 IM
+					// 4. 先登出当前账号(如果已登录)
+					try {
+						await timManager.logout()
+						console.log('✅ 已登出之前的账号')
+					} catch (e) {
+						console.log('⚠️ 登出失败或未登录:', e.message)
+					}
+					
+					// 5. 以红娘身份登录 IM
 					await timManager.login(this.imUserId, userSig)
 					console.log('✅ 红娘已登录 IM:', this.imUserId)
 					
-					// 5. 监听事件
+					// 6. 监听事件
 					this.addListeners()
 					
+					// 7. 加载会话列表
 					// 6. 加载会话列表
 					await this.loadConversationList()
 					
@@ -226,8 +235,10 @@
 			// 打开聊天页面
 			openChat(conversation) {
 				const targetUserId = conversation.userProfile.userID
+				const targetUserName = encodeURIComponent(conversation.userProfile.nick || `用户${targetUserId}`)
+				const targetUserAvatar = encodeURIComponent(conversation.userProfile.avatar || '')
 				uni.navigateTo({
-					url: `/pages/message/chat?targetUserId=${targetUserId}&fromMatchmaker=true`
+					url: `/pages/message/chat?targetUserId=${targetUserId}&targetUserName=${targetUserName}&targetUserAvatar=${targetUserAvatar}&fromMatchmaker=true`
 				})
 			},
 			

+ 106 - 158
LiangZhiYUMao/pages/message/chat.vue

@@ -162,8 +162,8 @@
         </view>
       </view>
     </scroll-view>
-		<!-- 新增:消息发送限制提示 -->
-		<view class="message-limit-tip">
+		<!-- 新增:消息发送限制提示(双方都不是红娘,且不是从红娘工作台进入时才显示) -->
+		<view class="message-limit-tip" v-if="!(String(userId).startsWith('m_') || String(targetUserId).startsWith('m_') || fromMatchmaker)">
 		  <!-- 同时判断isVip和hasMessageLimit,增强可靠性     -->
 		  <text v-if="isVip || !hasMessageLimit" class="vip-tip">✨ VIP特权:无发送次数限制</text>
 		  <text v-else class="limit-tip">
@@ -363,27 +363,42 @@ export default {
       rawUserId = parseInt(rawUserId);
     }
     
-    if (!rawUserId || isNaN(rawUserId)) {
-      console.error('❌ 无法获取有效的用户ID');
-      uni.showModal({
-        title: '用户信息错误',
-        content: '无法获取用户ID,请重新登录',
-        showCancel: false,
-        success: () => {
-          uni.removeStorageSync('token');
-          uni.removeStorageSync('userInfo');
-          uni.removeStorageSync('userId');
-          uni.reLaunch({
-            url: '/pages/page3/page3'
-          });
-        }
-      });
-      return;
+    // 标记是否为用户与红娘的聊天,用于跳过每日5条限制和文本审核
+    // 支持两种格式:'1' 或 'true'
+    this.fromMatchmaker = options.fromMatchmaker === '1' || options.fromMatchmaker === 'true';
+    
+    // 根据入口来源确定当前会话中的“自己”是谁
+    if (this.fromMatchmaker) {
+      // 红娘工作台入口:使用当前 TIM 登录账号作为 userId(例如 m_22)
+      const imUserId = timManager.getCurrentUserId();
+      if (!imUserId) {
+        console.error('❌ TIM 未登录,无法获取红娘IM账号');
+        return;
+      }
+      this.userId = String(imUserId);
+    } else {
+      // 普通用户入口:仍然使用本地存储的 userId
+      if (!rawUserId || isNaN(rawUserId)) {
+        console.error('❌ 无法获取有效的用户ID');
+        uni.showModal({
+          title: '用户信息错误',
+          content: '无法获取用户ID,请重新登录',
+          showCancel: false,
+          success: () => {
+            uni.removeStorageSync('token');
+            uni.removeStorageSync('userInfo');
+            uni.removeStorageSync('userId');
+            uni.reLaunch({
+              url: '/pages/page3/page3'
+            });
+          }
+        });
+        return;
+      }
+      this.userId = String(rawUserId);
     }
-	
     
-    // 保存用户ID(TIM需要字符串格式)
-    this.userId = String(rawUserId);
+    // 头像:默认先用用户信息中的头像(红娘入口会被 loadMatchmakerAvatarByImId 覆盖)
     this.userAvatar = userInfo.avatar || userInfo.avatarUrl || '/static/default-avatar.svg';
     
     // 获取对方用户信息(确保是字符串格式)
@@ -391,133 +406,60 @@ export default {
     this.targetUserName = decodeURIComponent(options.targetUserName || '用户');
     this.targetUserAvatar = decodeURIComponent(options.targetUserAvatar || '/static/default-avatar.svg');
     
-    // 标记是否为用户与红娘的聊天,用于跳过每日5条限制和文本审核
-    // 支持两种格式:'1' 或 'true'
-    this.fromMatchmaker = options.fromMatchmaker === '1' || options.fromMatchmaker === 'true';
-    
     // 生成会话 ID
     this.conversationID = `C2C${this.targetUserId}`;
     
     console.log('✅ 聊天页面初始化成功:');
-    console.log('   - 当前用户ID:', rawUserId, '(TIM格式:', this.userId, ')');
+    console.log('   - 当前用户ID:', this.userId);
     console.log('   - 对方用户ID:', this.targetUserId);
     console.log('   - 会话ID:', this.conversationID);
-    
+
     // 初始化 TIM
     await this.initTIM();
-	// 获取用户消息发送限制(VIP状态+剩余次数)
+
+    // 获取用户消息发送限制(VIP 状态 + 剩余次数)
     await this.getUserMessageLimit();
+
     // 等待 SDK Ready 后再加载消息
     await this.waitForSDKReady();
+
     // 先检查拉黑状态
-      this.isBlockedByTarget = await this.checkIsBlockedByTarget();
-      if (this.isBlockedByTarget) {
-        uni.showToast({
-          title: '你已被对方拉黑,无法发送消息',
-          icon: 'none',
-          duration: 3000
-        });
-      }
+    this.isBlockedByTarget = await this.checkIsBlockedByTarget();
+    if (this.isBlockedByTarget) {
+      uni.showToast({
+        title: '你已被对方拉黑,无法发送消息',
+        icon: 'none',
+        duration: 3000
+      });
+    }
+
     // 加载历史消息
     await this.loadMessages();
-    
+
     // 监听新消息
     this.listenMessages();
-    
-    // 监听已读回执(确保在 SDK Ready 之后调用)
-    this.listenMessageReadReceipt();
-    
+
+    // 监听已读回执
+    this.listenMessageReadReceipt && this.listenMessageReadReceipt();
+
     // 标记当前会话的消息为已读
-    this.markConversationRead();
-    
-    // 初始化在线状态监听(HTTP 轮询方式)
-    this.initOnlineStatusPolling();
-    
+    this.markConversationRead && this.markConversationRead();
+
+    // 初始化在线状态轮询
+    this.initOnlineStatusPolling && this.initOnlineStatusPolling();
+
     // 如果有预设消息,自动发送
     if (options.message) {
       const message = decodeURIComponent(options.message);
       console.log('检测到预设消息,准备自动发送:', message);
-      // 等待一下再发送,确保TIM已经初始化完成
       setTimeout(() => {
         this.inputText = message;
         this.sendTextMessage();
       }, 1500);
     }
   },
-  
-  /**
-   * 页面显示时(从其他页面返回)
-   */
-  async onShow() {
-    console.log('=== 聊天页面显示 ===');
-    
-    // 🔥 重新查询会话的 peerReadTime,更新已读状态
-    // 这样即使 A 离开页面期间 B 阅读了消息,A 返回时也能看到最新的已读状态
-    if (this.conversationID && timManager.tim) {
-      try {
-        console.log('🔄 重新查询会话已读状态...');
-        const conversationRes = await timManager.tim.getConversationProfile(this.conversationID);
-        
-        if (conversationRes && conversationRes.data && conversationRes.data.conversation) {
-          const peerReadTime = conversationRes.data.conversation.peerReadTime;
-          console.log('   - 对方最后阅读时间:', peerReadTime, peerReadTime > 0 ? new Date(peerReadTime * 1000).toLocaleString() : '未读');
-          
-          if (peerReadTime && peerReadTime > 0) {
-            let updatedCount = 0;
-            
-            // 更新所有发送时间 <= peerReadTime 的消息为已读
-            this.messages.forEach((msg, index) => {
-              if (msg.fromUserId === this.userId && !msg.isPeerRead && msg.sendStatus !== 4) {
-                const msgTime = Math.floor(msg.sendTime.getTime() / 1000);
-                
-                if (msgTime <= peerReadTime) {
-                  this.$set(this.messages[index], 'isPeerRead', true);
-                  updatedCount++;
-                  console.log(`   - 消息 ${msg.messageId} 已标记为已读`);
-                }
-              }
-            });
-            
-            console.log(`✅ 页面显示时更新了 ${updatedCount} 条消息为已读状态`);
-          }
-        }
-      } catch (error) {
-        console.error('❌ 重新查询会话已读状态失败:', error);
-      }
-    }
-    
-    // 重新标记当前会话为已读(通知对方)
-    this.markConversationRead();
-  },
-  
-  onUnload() {
-    // 页面卸载时移除监听
-    timManager.offMessage(this.handleNewMessage);
-    
-    // 移除消息状态变更监听(已注释)
-    /*
-    if (timManager.tim && this.handleStatusChange) {
-      timManager.tim.off(TIM.EVENT.MESSAGE_STATUS_CHANGED, this.handleStatusChange);
-    }
-    */
-  },
-  
+
   methods: {
-    /**
-     * 计算语音消息宽度(根据时长动态变化)
-     * @param {Number} duration 语音时长(秒)
-     * @return {String} 宽度值
-     */
-    getVoiceWidth(duration) {
-      // 基础宽度 100rpx,每秒增加 10rpx,最大 400rpx
-      const baseWidth = 100;
-      const widthPerSecond = 10;
-      const maxWidth = 400;
-      
-      const width = Math.min(baseWidth + (duration * widthPerSecond), maxWidth);
-      return width + 'rpx';
-    },
-    
     /**
      * 初始化 TIM
      */
@@ -525,19 +467,19 @@ export default {
       try {
         // 如果未初始化,先初始化
         if (!timManager.tim) {
-          timManager.init(1600109674);  // ✅ 已更新为正确的 SDKAppID
+          timManager.init(1600109674); // 使用正确的 SDKAppID
         }
-        
+
         // 如果未登录,获取 userSig 并登录
         if (!timManager.isLogin) {
-          // 先导入当前用户和目标用户到腾讯云IM
+          // 先导入当前用户和目标用户到腾讯云 IM
           await this.importUsers();
-          
+
           // 从后端获取 userSig
           const userSig = await this.getUserSig();
           await timManager.login(this.userId, userSig);
         }
-        
+
         this.isLogin = true;
         console.log('✅ TIM 初始化完成');
       } catch (error) {
@@ -548,26 +490,27 @@ export default {
         });
       }
     },
-	/**
-	   * 跳转到VIP页面
-	   */
-	  goToVipPage() {
-	    console.log('点击跳转VIP页面');
-	    // 替换为你的实际VIP页面路径(例如会员开通页面)
-	    uni.navigateTo({
-	      url: '/pages/vip/index', // 请根据项目实际路径修改
-	      success: () => {
-	        console.log('跳转VIP页面成功');
-	      },
-	      fail: (err) => {
-	        console.error('跳转VIP页面失败:', err);
-	        uni.showToast({
-	          title: 'VIP页面不存在',
-	          icon: 'none'
-	        });
-	      }
-	    });
-	  },
+
+    /**
+     * 跳转到VIP页面
+     */
+    goToVipPage() {
+      console.log('点击跳转VIP页面');
+      // 替换为你的实际VIP页面路径(例如会员开通页面)
+      uni.navigateTo({
+        url: '/pages/vip/index', // 请根据项目实际路径修改
+        success: () => {
+          console.log('跳转VIP页面成功');
+        },
+        fail: (err) => {
+          console.error('跳转VIP页面失败:', err);
+          uni.showToast({
+            title: 'VIP页面不存在',
+            icon: 'none'
+          });
+        }
+      });
+    },
     more(userid) {
       console.log('点击了更多按钮,开始跳转...', userid);
       // 如果目标页面是普通页面,用navigateTo(保留当前页面);如果是tabbar页面,用switchTab
@@ -604,7 +547,7 @@ export default {
         
         // 导入当前用户(确保userId是字符串)
         const currentUserRes = await uni.request({
-          url: 'http://localhost:1004/api/im/importUser',
+          url: 'http://localhost:8083/api/im/importUser',
           method: 'POST',
           data: {
             userId: String(this.userId),
@@ -619,7 +562,7 @@ export default {
         
         // 导入目标用户(确保userId是字符串)
         const targetUserRes = await uni.request({
-          url: 'http://localhost:1004/api/im/importUser',
+          url: 'http://localhost:8083/api/im/importUser',
           method: 'POST',
           data: {
             userId: String(this.targetUserId),
@@ -644,7 +587,7 @@ export default {
     async getUserSig() {
       try {
         const [err, res] = await uni.request({
-          url: 'http://localhost:1004/api/im/getUserSig',
+          url: 'http://localhost:8083/api/im/getUserSig',
           method: 'GET',
           data: {
             userId: this.userId
@@ -1100,8 +1043,11 @@ export default {
      * 发送文本消息
      */
     async sendTextMessage() {
+		  // 判断是否为涉及红娘的会话:任一方ID以 m_ 开头,或来自红娘工作台
+		  const isMatchmakerChat = this.fromMatchmaker || String(this.userId).startsWith('m_') || String(this.targetUserId).startsWith('m_');
 		
-		if (this.hasMessageLimit && this.remainingCount <= 0) {
+		  // 仅普通用户之间的会话才做消息次数限制
+		  if (!isMatchmakerChat && this.hasMessageLimit && this.remainingCount <= 0) {
 		    uni.showToast({
 		      title: '今日消息发送次数已用完,开通VIP无限制',
 		      icon: 'none',
@@ -1117,11 +1063,11 @@ export default {
       const content = this.inputText;
       this.inputText = '';
       
-      // 1. 先进行消息内容审核(红娘聊天跳过审核)
-      if (!this.fromMatchmaker) {
+      // 1. 先进行消息内容审核(只要有一方是红娘就跳过审核)
+      if (!isMatchmakerChat) {
         try {
           const checkRes = await uni.request({
-            url: 'http://localhost:1004/api/chat/checkMessage',
+            url: 'http://localhost:8083/api/chat/checkMessage',
             method: 'POST',
             data: {
               userId: String(this.userId),
@@ -1227,7 +1173,8 @@ export default {
         
         console.log('✅ 消息发送成功');
         this.syncMessageToMySQL(message);
-		if (!this.isVip) {
+		  // 仅普通用户之间的会话才扣减消息次数
+		  if (!isMatchmakerChat && !this.isVip) {
 		    await this.updateMessageCount();
 		  }
       } catch (error) {
@@ -1347,7 +1294,7 @@ export default {
         
         // 调用后端同步接口
         const res = await uni.request({
-          url: 'http://localhost:1004/api/chat/syncTIMMessage',
+          url: 'http://localhost:8083/api/chat/syncTIMMessage',
           method: 'POST',
           data: syncData,
           header: {
@@ -2093,7 +2040,7 @@ export default {
         
         // 使用uni.uploadFile上传到后端MinIO接口
         const [err, res] = await uni.uploadFile({
-          url: 'http://localhost:1004/api/voice/upload',
+          url: 'http://localhost:8083/api/voice/upload',
           filePath: this.voiceTempPath,
           name: 'file',
           header: {
@@ -2296,12 +2243,13 @@ export default {
 		     * 从后端获取VIP状态和今日剩余发送次数
 		     */
 		    async getUserMessageLimit() {
-		      // 用户与红娘的聊天:不做每日5条限制,直接视为无限制
-		      if (this.fromMatchmaker) {
+		      // 只要一方是红娘(ID 以 m_ 开头),就不做每日5条限制,直接视为无限制
+		      const isMatchmakerChat = String(this.userId).startsWith('m_') || String(this.targetUserId).startsWith('m_');
+		      if (isMatchmakerChat || this.fromMatchmaker) {
 		        this.hasMessageLimit = false;
 		        this.isVip = true;
 		        this.remainingCount = 999;
-		        console.log('✅ 红娘聊天模式:无消息限制');
+		        console.log('✅ 红娘聊天模式:无消息限制(跳过 getUserMessageLimit 接口)');
 		        return;
 		      }
 		      

+ 91 - 59
LiangZhiYUMao/pages/message/index.vue

@@ -711,76 +711,109 @@ export default {
      */
     async loadUserAvatars() {
       try {
-        if (this.conversations.length === 0) {
-          return;
-        }
+        // 分离普通用户和红娘ID
+        const normalUserIds = [];
+        const matchmakerIds = [];
         
-        // 收集所有需要获取头像的用户ID
-        const userIds = this.conversations
-          .map(conv => conv.targetUserId)
-          .filter(id => id)  // 过滤掉空值
-          .join(',');
+        this.conversations.forEach(conv => {
+          if (!conv.targetUserId) return;
+          
+          if (conv.targetUserId.startsWith('m_')) {
+            // 红娘ID,去掉 m_ 前缀
+            const matchmakerId = conv.targetUserId.substring(2);
+            matchmakerIds.push(matchmakerId);
+          } else {
+            // 普通用户ID
+            normalUserIds.push(conv.targetUserId);
+          }
+        });
         
-        if (!userIds) {
-          console.log('⚠️ 没有需要获取头像的用户');
-          return;
-        }
+        console.log('🔄 开始批量获取信息 - 用户:', normalUserIds.length, '个, 红娘:', matchmakerIds.length, '个');
         
-        console.log('🔄 开始批量获取用户头像,用户ID列表:', userIds);
+        // 创建ID到信息的映射
+        const userInfoMap = {};
         
-        // 调用批量获取用户信息接口
-        const res = await uni.request({
-          url: 'http://localhost:8083/api/user/batch',
-          method: 'GET',
-          data: {
-            userIds: userIds
+        // 1. 批量查询普通用户
+        if (normalUserIds.length > 0) {
+          const userRes = await uni.request({
+            url: 'http://localhost:8083/api/user/batch',
+            method: 'GET',
+            data: {
+              userIds: normalUserIds.join(',')
+            }
+          });
+          
+          if (userRes[1].statusCode === 200 && userRes[1].data.code === 200) {
+            const userList = userRes[1].data.data || [];
+            userList.forEach(user => {
+              userInfoMap[user.userId] = {
+                nickname: user.nickname,
+                avatarUrl: user.avatarUrl
+              };
+            });
+            console.log('✅ 批量获取用户信息成功,数量:', userList.length);
           }
-        });
+        }
         
-        if (res[1].statusCode === 200 && res[1].data.code === 200) {
-          const userList = res[1].data.data || [];
-          console.log('✅ 批量获取用户信息成功,数量:', userList.length);
-          
-          // 创建用户ID到用户信息的映射
-          const userMap = {};
-          userList.forEach(user => {
-            userMap[user.userId] = user;
+        // 2. 批量查询红娘
+        if (matchmakerIds.length > 0) {
+          const matchmakerRes = await uni.request({
+            url: 'http://localhost:8081/api/matchmaker/batch',
+            method: 'GET',
+            data: {
+              matchmakerIds: matchmakerIds.join(',')
+            }
           });
           
-          // 收集数据库中不存在的用户ID
-          const deletedUserIds = [];
-          
-          // 更新会话列表中的头像和昵称
-          for (const conv of this.conversations) {
-            const userInfo = userMap[conv.targetUserId];
-            if (userInfo) {
-              // 更新头像(如果数据库中有头像)
-              if (userInfo.avatarUrl) {
-                conv.targetUserAvatar = userInfo.avatarUrl;
-              }
-              // 更新昵称(如果数据库中的昵称更准确)
-              if (userInfo.nickname && userInfo.nickname !== `用户${conv.targetUserId}`) {
-                conv.targetUserName = userInfo.nickname;
-              }
-              
-              console.log(`🖼️ 更新用户 ${conv.targetUserId} 的信息:`, {
-                昵称: conv.targetUserName,
-                头像: conv.targetUserAvatar
-              });
-            } else {
-              // 数据库中不存在该用户,标记为需要删除
+          if (matchmakerRes[1].statusCode === 200 && matchmakerRes[1].data.code === 200) {
+            const matchmakerList = matchmakerRes[1].data.data || [];
+            matchmakerList.forEach(matchmaker => {
+              // 红娘的 IM ID 是 m_ + matchmakerId
+              const imUserId = 'm_' + matchmaker.matchmakerId;
+              userInfoMap[imUserId] = {
+                nickname: matchmaker.realName,
+                avatarUrl: matchmaker.avatarUrl
+              };
+            });
+            console.log('✅ 批量获取红娘信息成功,数量:', matchmakerList.length);
+          }
+        }
+        
+        // 收集数据库中不存在的用户ID(排除红娘)
+        const deletedUserIds = [];
+        
+        // 3. 更新会话列表中的头像和昵称
+        for (const conv of this.conversations) {
+          const userInfo = userInfoMap[conv.targetUserId];
+          if (userInfo) {
+            // 更新头像
+            if (userInfo.avatarUrl) {
+              conv.targetUserAvatar = userInfo.avatarUrl;
+            }
+            // 更新昵称
+            if (userInfo.nickname && userInfo.nickname !== `用户${conv.targetUserId}`) {
+              conv.targetUserName = userInfo.nickname;
+            }
+            
+            console.log(`🖼️ 更新用户 ${conv.targetUserId} 的信息:`, {
+              昵称: conv.targetUserName,
+              头像: conv.targetUserAvatar
+            });
+          } else {
+            // 只有普通用户不存在时才标记为删除(红娘不删除)
+            if (!conv.targetUserId.startsWith('m_')) {
               console.warn(`⚠️ 用户 ${conv.targetUserId} 在数据库中不存在`);
               deletedUserIds.push(conv.targetUserId);
+            } else {
+              console.warn(`⚠️ 红娘 ${conv.targetUserId} 在数据库中不存在`);
             }
           }
-          
-          // 如果有不存在的用户,删除TIM中的会话和好友
-          if (deletedUserIds.length > 0) {
-            console.log(`🗑️ 发现 ${deletedUserIds.length} 个不存在的用户,开始清理TIM数据...`);
-            await this.cleanupDeletedUsers(deletedUserIds);
-          }
-        } else {
-          console.error('❌ 批量获取用户信息失败:', res[1].data);
+        }
+        
+        // 4. 如果有不存在的普通用户,删除TIM中的会话和好友
+        if (deletedUserIds.length > 0) {
+          console.log(`🗑️ 发现 ${deletedUserIds.length} 个不存在的用户,开始清理TIM数据...`);
+          await this.cleanupDeletedUsers(deletedUserIds);
         }
       } catch (error) {
         console.error('❌ 批量获取用户头像失败:', error);
@@ -790,7 +823,6 @@ export default {
     
     /**
      * 格式化会话数据
-     * 将TIM的会话格式转换为UI需要的格式
      */
     formatConversation(timConv) {
       // conversationID 格式: C2C{userId}

+ 14 - 0
LiangZhiYUMao/utils/tim-manager.js

@@ -524,6 +524,20 @@ class TIMManager {
       console.log('✅ 已移除已读回执回调,剩余回调数:', this.messageReadCallbacks.length);
     }
   }
+
+  /**
+   * 获取 TIM 实例
+   */
+  getTim() {
+    return this.tim;
+  }
+
+  /**
+   * 获取当前登录的 IM 用户ID
+   */
+  getCurrentUserId() {
+    return this.userId ? String(this.userId) : null;
+  }
 }
 
 // 导出单例

+ 1 - 1
gateway/src/main/java/com/zhentao/filter/GatewayRoutes.java

@@ -43,7 +43,7 @@ public class GatewayRoutes {
                 // 首页服务路由 - 课程接口(端口8081)
                 .route("homepage_course_route", r -> r.path("/api/course/**")
                         .uri("http://localhost:8081"))
-                .route("homepage_chatfriend_route", r -> r.path("/api/chatfriend/**")
+                .route("homepage_chatfriend_route", r -> r.path("/api/chatfriend/**","/api/online/**","/api/im/**")
                         .uri("http://localhost:1004"))
                 
                 // 首页服务路由 - 我的资源接口(端口8081)

+ 38 - 0
service/homePage/src/main/java/com/zhentao/controller/MatchmakerController.java

@@ -13,6 +13,7 @@ import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.client.RestTemplate;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -273,6 +274,43 @@ public class MatchmakerController {
         }
     }
     
+    /**
+     * 批量查询红娘信息
+     * 
+     * @param matchmakerIds 红娘ID列表,逗号分隔(例如:22,23,24)
+     * @return 红娘信息列表
+     */
+    @GetMapping("/batch")
+    public Result<List<MatchmakerVO>> batchGetMatchmakers(@RequestParam String matchmakerIds) {
+        try {
+            if (matchmakerIds == null || matchmakerIds.trim().isEmpty()) {
+                return Result.success(Collections.emptyList());
+            }
+            
+            // 解析ID列表
+            String[] idArray = matchmakerIds.split(",");
+            List<Integer> ids = new java.util.ArrayList<>();
+            for (String id : idArray) {
+                try {
+                    ids.add(Integer.parseInt(id.trim()));
+                } catch (NumberFormatException e) {
+                    System.err.println("⚠️ 无效的红娘ID: " + id);
+                }
+            }
+            
+            if (ids.isEmpty()) {
+                return Result.success(Collections.emptyList());
+            }
+            
+            // 批量查询
+            List<MatchmakerVO> matchmakers = matchmakerService.batchGetMatchmakers(ids);
+            return Result.success(matchmakers);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return Result.error("批量查询红娘信息失败:" + e.getMessage());
+        }
+    }
+    
     /**
      * 批量导入现有红娘到腾讯云 IM(一次性任务)
      * 

+ 8 - 0
service/homePage/src/main/java/com/zhentao/mapper/MatchmakerMapper.java

@@ -54,5 +54,13 @@ public interface MatchmakerMapper extends BaseMapper<Matchmaker> {
      * @return 红娘排行榜列表
      */
     List<MatchmakerVO> selectRankingList(@Param("limit") Integer limit);
+    
+    /**
+     * 批量查询红娘信息
+     * 
+     * @param matchmakerIds 红娘ID列表
+     * @return 红娘VO列表
+     */
+    List<MatchmakerVO> selectBatchMatchmakers(@Param("matchmakerIds") List<Integer> matchmakerIds);
 }
 

+ 8 - 0
service/homePage/src/main/java/com/zhentao/service/MatchmakerService.java

@@ -56,5 +56,13 @@ public interface MatchmakerService extends IService<Matchmaker> {
      * @return 红娘实体
      */
     Matchmaker getMatchmakerByUserId(Integer userId);
+    
+    /**
+     * 批量查询红娘信息
+     * 
+     * @param matchmakerIds 红娘ID列表
+     * @return 红娘VO列表
+     */
+    java.util.List<MatchmakerVO> batchGetMatchmakers(java.util.List<Integer> matchmakerIds);
 }
 

+ 16 - 0
service/homePage/src/main/java/com/zhentao/service/impl/MatchmakerServiceImpl.java

@@ -15,6 +15,7 @@ import org.springframework.stereotype.Service;
 
 import java.time.LocalDate;
 import java.time.Period;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -336,4 +337,19 @@ public class MatchmakerServiceImpl extends ServiceImpl<MatchmakerMapper, Matchma
                 .eq(Matchmaker::getUserId, userId.longValue())
                 .one();
     }
+    
+    @Override
+    public List<MatchmakerVO> batchGetMatchmakers(List<Integer> matchmakerIds) {
+        if (matchmakerIds == null || matchmakerIds.isEmpty()) {
+            return new ArrayList<>();
+        }
+        
+        // 批量查询红娘详情
+        List<MatchmakerVO> matchmakers = matchmakerMapper.selectBatchMatchmakers(matchmakerIds);
+        
+        // 处理数据(计算年龄、处理头像等)
+        matchmakers.forEach(this::processMatchmakerVO);
+        
+        return matchmakers;
+    }
 }

+ 33 - 0
service/homePage/src/main/resources/mapper/MatchmakerMapper.xml

@@ -141,5 +141,38 @@
         ORDER BY m.success_couples DESC, m.level DESC
         LIMIT #{limit}
     </select>
+    
+    <!-- 批量查询红娘信息 -->
+    <select id="selectBatchMatchmakers" resultMap="MatchmakerVOMap">
+        SELECT 
+            m.matchmaker_id,
+            m.real_name,
+            m.phone,
+            m.email,
+            m.gender,
+            m.birth_date,
+            m.avatar_url,
+            m.matchmaker_type,
+            m.level,
+            m.success_couples,
+            m.address_detail,
+            m.profile,
+            m.status,
+            m.created_at,
+            m.province_id,
+            p.name AS province_name,
+            m.city_id,
+            c.name AS city_name,
+            m.area_id,
+            a.name AS area_name
+        FROM matchmakers m
+        LEFT JOIN province p ON m.province_id = p.id
+        LEFT JOIN city c ON m.city_id = c.id
+        LEFT JOIN area a ON m.area_id = a.id
+        WHERE m.matchmaker_id IN
+        <foreach collection="matchmakerIds" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
 </mapper>
 

+ 12 - 1
service/websocket/src/main/java/com/zhentao/WebSocketApplication.java

@@ -18,8 +18,19 @@ import org.springframework.context.annotation.ComponentScan;
 @MapperScan({"com.zhentao.repository", "com.zhentao.mapper"})
 public class WebSocketApplication {
     public static void main(String[] args) {
-        SpringApplication.run(WebSocketApplication.class, args);
+        org.springframework.context.ConfigurableApplicationContext context = SpringApplication.run(WebSocketApplication.class, args);
         System.out.println("WebSocket聊天服务启动成功!");
         System.out.println("WebSocket连接地址: ws://localhost:1004/ws/chat?userId={userId}");
+        
+        System.out.println("====== 检查 Bean 是否存在 ======");
+        boolean hasChatController = context.containsBean("chatController");
+        System.out.println("ChatController 存在: " + hasChatController);
+        
+        if (hasChatController) {
+            System.out.println("ChatController Bean: " + context.getBean("chatController"));
+        } else {
+            System.err.println("❌ 严重错误: ChatController 未被注册!请检查包扫描路径或编译输出。");
+        }
+        System.out.println("==============================");
     }
 }

+ 17 - 0
service/websocket/src/main/java/com/zhentao/config/MinioConfig.java

@@ -3,6 +3,7 @@ package com.zhentao.config;
 import io.minio.MinioClient;
 import lombok.Data;
 import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -10,6 +11,7 @@ import org.springframework.context.annotation.Configuration;
  * MinIO配置类
  */
 @Configuration
+@EnableConfigurationProperties
 @ConfigurationProperties(prefix = "minio")
 @Data
 public class MinioConfig {
@@ -39,6 +41,21 @@ public class MinioConfig {
      */
     @Bean
     public MinioClient minioClient() {
+        if (endpoint == null || endpoint.isEmpty()) {
+            throw new IllegalStateException("MinIO endpoint 未配置!请检查 application.yml 中的 minio.endpoint 配置");
+        }
+        if (accessKey == null || accessKey.isEmpty()) {
+            throw new IllegalStateException("MinIO accessKey 未配置!请检查 application.yml 中的 minio.access-key 配置");
+        }
+        if (secretKey == null || secretKey.isEmpty()) {
+            throw new IllegalStateException("MinIO secretKey 未配置!请检查 application.yml 中的 minio.secret-key 配置");
+        }
+        
+        System.out.println("✅ MinIO 配置加载成功:");
+        System.out.println("   - endpoint: " + endpoint);
+        System.out.println("   - accessKey: " + accessKey);
+        System.out.println("   - bucketName: " + bucketName);
+        
         return MinioClient.builder()
                 .endpoint(endpoint)
                 .credentials(accessKey, secretKey)

+ 5 - 0
service/websocket/src/main/java/com/zhentao/controller/ChatController.java

@@ -34,6 +34,11 @@ public class ChatController {
 
     @Autowired
     private UserVipService userVipService;
+    @GetMapping("/test")
+    public String test() {
+        return "ChatController is working!";
+    }
+
     /**
      * 获取会话消息列表(优化版:添加更多参数支持)
      * GET /api/chat/messages?userId=10001&targetUserId=10002&page=0&size=20&lastMessageId=xxx

+ 21 - 4
service/websocket/src/main/java/com/zhentao/controller/OnlineStatusController.java

@@ -20,18 +20,30 @@ public class OnlineStatusController {
 
     /**
      * 检查用户是否在线
-     * @param userId 用户ID
+     * @param userId 用户ID(支持普通用户ID和红娘IM ID,如 "123" 或 "m_22")
      * @return 在线状态
      */
     @GetMapping("/checkStatus")
-    public Map<String, Object> checkUserOnlineStatus(@RequestParam("userId") Long userId) {
+    public Map<String, Object> checkUserOnlineStatus(@RequestParam("userId") String userId) {
         Map<String, Object> result = new HashMap<>();
         
         try {
-            boolean isOnline = onlineUserService.isUserOnline(userId);
+            // 将字符串 userId 转换为 Long(如果是红娘ID "m_22",需要特殊处理)
+            Long userIdLong;
+            if (userId.startsWith("m_")) {
+                // 红娘ID格式:m_22 -> 提取数字部分
+                // 注意:这里假设 OnlineUserService 使用的是红娘的 matchmaker_id
+                // 如果需要使用完整的 IM ID,需要修改 OnlineUserService
+                userIdLong = Long.parseLong(userId.substring(2));
+            } else {
+                // 普通用户ID
+                userIdLong = Long.parseLong(userId);
+            }
+            
+            boolean isOnline = onlineUserService.isUserOnline(userIdLong);
             
             Map<String, Object> data = new HashMap<>();
-            data.put("userId", userId);
+            data.put("userId", userId);  // 返回原始 userId
             data.put("online", isOnline);
             data.put("timestamp", System.currentTimeMillis());
             
@@ -40,6 +52,11 @@ public class OnlineStatusController {
             result.put("data", data);
             
             System.out.println("查询用户 " + userId + " 在线状态: " + (isOnline ? "在线" : "离线"));
+        } catch (NumberFormatException e) {
+            System.err.println("无效的用户ID格式: " + userId);
+            result.put("code", 400);
+            result.put("message", "无效的用户ID格式");
+            result.put("data", null);
         } catch (Exception e) {
             System.err.println("查询在线状态失败: " + e.getMessage());
             result.put("code", 500);

+ 2 - 2
service/websocket/src/main/java/com/zhentao/controller/TIMController.java

@@ -33,11 +33,11 @@ public class TIMController {
      * GET /api/im/getUserSig?userId=1
      */
     @GetMapping("/getUserSig")
-    public Map<String, Object> getUserSig(@RequestParam Long userId) {
+    public Map<String, Object> getUserSig(@RequestParam String userId) {
         Map<String, Object> result = new HashMap<>();
         
         try {
-            String userSig = timUtils.generateUserSig(String.valueOf(userId));
+            String userSig = timUtils.generateUserSig(userId);
             
             Map<String, String> data = new HashMap<>();
             data.put("userSig", userSig);

+ 3 - 3
service/websocket/src/main/resources/application.yml

@@ -81,6 +81,6 @@ tim:
 # MinIO 对象存储配置
 minio:
   endpoint: http://115.190.125.125:9000
-  access-key: minioadmin
-  secret-key: minioadmin
-  bucket-name: minion-voice-files
+  accessKey: minioadmin
+  secretKey: minioadmin
+  bucketName: minion-voice-files