Просмотр исходного кода

webSocket 连接实现与测试

shizhongqi 11 месяцев назад
Родитель
Сommit
c0669a8928
3 измененных файлов с 469 добавлено и 86 удалено
  1. 227 53
      hdApp/src/admin/chat/chatPage.vue
  2. 1 1
      hdApp/src/admin/chat/list.vue
  3. 241 32
      mallApp/src/pages/chat/chatPage.vue

+ 227 - 53
hdApp/src/admin/chat/chatPage.vue

@@ -61,7 +61,7 @@
 </template>
 </template>
 
 
 <script>
 <script>
-import { getChatHistory } from "@/api/chat";
+import { getChatHistory } from '@/api/chat';
 export default {
 export default {
   data() {
   data() {
     return {
     return {
@@ -73,9 +73,9 @@ export default {
       isMultiUser: false,
       isMultiUser: false,
       // 当前用户信息
       // 当前用户信息
       currentUser: {
       currentUser: {
+        id: 0,
         customId: 0,
         customId: 0,
         shopId: 0,
         shopId: 0,
-        id: 0,
         username: '',
         username: '',
         avatar: ''
         avatar: ''
       },
       },
@@ -87,7 +87,13 @@ export default {
       room: '',
       room: '',
       reconnectCount: 0,
       reconnectCount: 0,
       maxReconnectCount: 3,
       maxReconnectCount: 3,
-      isManualDisconnect: false // 添加标志位:是否主动断开连接
+      isManualDisconnect: false, // 添加标志位:是否主动断开连接
+      // 调试选项
+      debugOptions: {
+        forceBasic: false, // 强制使用基础参数
+        forceSegmented: false, // 强制使用分段传输
+        logDetails: true // 是否输出详细日志
+      }
     };
     };
   },
   },
   onLoad(options) {
   onLoad(options) {
@@ -107,30 +113,25 @@ export default {
     if (options.name) {
     if (options.name) {
       this.currentUser.username = options.name;
       this.currentUser.username = options.name;
     }
     }
-    if (options.shopId) {
-      // 是门店,则使用shopId
-      this.currentUser.id = options.shopId;
+    if (options.staffId) {
+      // 是门店,则使用staffId
+      this.currentUser.id = options.staffId;
     }
     }
     if (options.avatar) {
     if (options.avatar) {
       this.currentUser.avatar = options.avatar;
       this.currentUser.avatar = options.avatar;
     }
     }
 
 
     // 获取房间号
     // 获取房间号
-    if (options.room) {
-      this.room = options.room;
+    if (this.currentUser.customId && this.currentUser.shopId) {
+      this.room = 'custom_id-' + this.currentUser.customId + 'AND' + 'shop_id-' + this.currentUser.shopId;
     } else {
     } else {
-      if (this.currentUser.customId && this.currentUser.shopId) {
-        this.room = 'custom_id-' + this.currentUser.customId + 'AND' + 'shop_id-' + this.currentUser.shopId;
-      } else {
-        // 如果没有传入房间号,使用默认值或生成一个
-        this.room = 'default_room';
-      }
+      // 如果没有传入房间号,使用默认值或生成一个
+      this.room = 'default_room';
     }
     }
     console.log('---------- this.room: ', this.room);
     console.log('---------- this.room: ', this.room);
 
 
     // 加载聊天记录
     // 加载聊天记录
     this.loadChatHistory();
     this.loadChatHistory();
-
     // 连接WebSocket
     // 连接WebSocket
     this.connectWebSocket();
     this.connectWebSocket();
   },
   },
@@ -159,11 +160,9 @@ export default {
     init() {},
     init() {},
     // 加载聊天历史记录
     // 加载聊天历史记录
     loadChatHistory() {
     loadChatHistory() {
-      // 在实际应用中,这里应该从服务器加载历史聊天记录
-      // 可以通过API请求获取该房间的历史消息
       getChatHistory({
       getChatHistory({
         roomName: this.room,
         roomName: this.room,
-        userId: this.currentUser.id,
+        userId: this.currentUser.id
       }).then((res) => {
       }).then((res) => {
         if (res.code == 1 && res.data) {
         if (res.code == 1 && res.data) {
           this.messageList = res.data;
           this.messageList = res.data;
@@ -172,43 +171,17 @@ export default {
           });
           });
         }
         }
       });
       });
-
-      // 临时保留一些示例数据用于开发测试
-      // if (process.env.NODE_ENV === "development") {
-      //   const sampleMessages = [
-      //     {
-      //       id: "msg_001",
-      //       content: "您好,请问有什么可以帮助您的吗?",
-      //       time: "14:30",
-      //       isMine: false,
-      //       username: "花店客服",
-      //       avatar: "https://cdn.uviewui.com/uview/demo/user/2.jpg",
-      //     },
-      //     {
-      //       id: "msg_002",
-      //       content: "我想订购一束玫瑰花",
-      //       time: "14:31",
-      //       isMine: true,
-      //       username: "我",
-      //       avatar: "https://cdn.uviewui.com/uview/demo/user/1.jpg",
-      //     },
-      //   ];
-      //   this.messageList = sampleMessages;
-      // }
     },
     },
 
 
     // 获取主机地址
     // 获取主机地址
     getHost() {
     getHost() {
-      // 在实际项目中,这里应该返回你的WebSocket服务器地址
-      // 可以从配置文件中读取
-
       // #ifdef H5
       // #ifdef H5
-      return window.location.host;
+      return this.$constant.webSocketUrl;
       // #endif
       // #endif
 
 
       // #ifdef MP-WEIXIN || APP-PLUS
       // #ifdef MP-WEIXIN || APP-PLUS
       // 小程序和App环境下,需要配置具体的服务器地址
       // 小程序和App环境下,需要配置具体的服务器地址
-      return '192.168.10.57:8080'; // 替换为实际的服务器地址
+      return this.$constant.webSocketUrl; // 替换为实际的服务器地址
       // #endif
       // #endif
     },
     },
 
 
@@ -225,11 +198,15 @@ export default {
 
 
       try {
       try {
         // 构建WebSocket URL
         // 构建WebSocket URL
-        const wsUrl = `ws://${this.getHost()}/room?room=${this.room}`;
+        const wsUrl = `ws://${this.getHost()}/room`;
 
 
+        // 构建子协议参数(模拟POST body传输)
+        const protocols = this.buildWebSocketProtocols();
+        console.log('protocols: ', protocols);
         // 创建WebSocket连接
         // 创建WebSocket连接
         this.socket = uni.connectSocket({
         this.socket = uni.connectSocket({
           url: wsUrl,
           url: wsUrl,
+          protocols: protocols,
           success: () => {
           success: () => {
             console.log('WebSocket连接创建成功');
             console.log('WebSocket连接创建成功');
           },
           },
@@ -245,12 +222,6 @@ export default {
           this.isConnected = true;
           this.isConnected = true;
           this.reconnectCount = 0;
           this.reconnectCount = 0;
           this.isManualDisconnect = false; // 连接成功时重置标志位
           this.isManualDisconnect = false; // 连接成功时重置标志位
-
-          // uni.showToast({
-          //   title: "连接成功",
-          //   icon: "success",
-          //   duration: 1500,
-          // });
         });
         });
 
 
         // 监听WebSocket消息
         // 监听WebSocket消息
@@ -283,6 +254,208 @@ export default {
       }
       }
     },
     },
 
 
+    // 构建WebSocket子协议参数(模拟POST body传输)
+    buildWebSocketProtocols() {
+      try {
+        // 创建房间参数对象
+        const roomParams = {
+          u_id: parseInt(this.currentUser.id), // TODO 就是 shopId,应该修改为 staffId
+          c_id: parseInt(this.currentUser.customId), //因为是门店客户端,customId为0,好推测出是门店端连接
+          s_id: parseInt(this.currentUser.shopId),
+          u_name: this.currentUser.username && this.currentUser.username.length > 30 ? this.currentUser.username.substring(0, 30) : this.currentUser.username,
+          platform: this.getPlatformInfo(),
+          type: 'shop' // 客户端类型:shop:门店端, customer:客户端
+        };
+
+        // 分段传输(如果参数过长)
+        const segments = this.createSegmentedProtocols(roomParams);
+        if (segments.length == 0) {
+          this.$msg('分段传输失败');
+          return;
+        }
+
+        // 根据调试选项和长度选择合适的方案
+        let protocols;
+        if (segments.length > 0) {
+          protocols = ['chat', ...segments];
+        }
+
+        // 验证协议字符串的合法性
+        const isValid = this.validateProtocols(protocols);
+        console.log('协议验证结果:', isValid);
+        if (!isValid) {
+          this.$msg('协议验证失败');
+          return;
+        }
+
+        return protocols;
+      } catch (error) {
+        console.error('构建WebSocket protocols失败:', error);
+        return ['chat']; // 返回基础协议作为fallback
+      }
+    },
+
+    // 获取平台信息
+    getPlatformInfo() {
+      // #ifdef MP-WEIXIN
+      return 'MP-WEIXIN';
+      // #endif
+
+      // #ifdef APP-PLUS
+      return 'APP-PLUS';
+      // #endif
+
+      // #ifdef MP-ALIPAY
+      return 'MP-ALIPAY';
+      // #endif
+
+      // #ifdef H5
+      return 'H5';
+      // #endif
+
+      return 'UNKNOWN';
+    },
+
+    // Base64编码方法(跨平台兼容)
+    base64Encode(str) {
+      // #ifdef H5
+      // H5环境使用浏览器原生方法
+      try {
+        return btoa(str);
+      } catch (error) {
+        console.error('H5 Base64编码失败:', error);
+        return this.customBase64Encode(str);
+      }
+      // #endif
+
+      // #ifdef MP-WEIXIN || MP-ALIPAY || APP-PLUS
+      // 小程序和App环境使用自定义编码
+      return this.customBase64Encode(str);
+      // #endif
+    },
+
+    // 自定义Base64编码实现
+    customBase64Encode(str) {
+      const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
+      let output = '';
+      let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+      let i = 0;
+
+      // 转换为UTF-8字节
+      str = this.utf8Encode(str);
+
+      while (i < str.length) {
+        chr1 = str.charCodeAt(i++);
+        chr2 = str.charCodeAt(i++);
+        chr3 = str.charCodeAt(i++);
+
+        enc1 = chr1 >> 2;
+        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+        enc4 = chr3 & 63;
+
+        if (isNaN(chr2)) {
+          enc3 = enc4 = 64;
+        } else if (isNaN(chr3)) {
+          enc4 = 64;
+        }
+
+        output = output + chars.charAt(enc1) + chars.charAt(enc2) + chars.charAt(enc3) + chars.charAt(enc4);
+      }
+
+      return output;
+    },
+
+    // UTF-8编码
+    utf8Encode(str) {
+      str = str.replace(/\r\n/g, '\n');
+      let utftext = '';
+
+      for (let n = 0; n < str.length; n++) {
+        const c = str.charCodeAt(n);
+
+        if (c < 128) {
+          utftext += String.fromCharCode(c);
+        } else if (c > 127 && c < 2048) {
+          utftext += String.fromCharCode((c >> 6) | 192);
+          utftext += String.fromCharCode((c & 63) | 128);
+        } else {
+          utftext += String.fromCharCode((c >> 12) | 224);
+          utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+          utftext += String.fromCharCode((c & 63) | 128);
+        }
+      }
+
+      return utftext;
+    },
+
+    // URL安全的Base64编码
+    urlSafeBase64Encode(str) {
+      // 先进行标准Base64编码
+      const base64 = this.base64Encode(str);
+
+      // 转换为URL安全格式:替换 +/= 字符
+      return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
+    },
+
+    // 创建分段协议(用于长参数)- 每次取两个键值对,将参数分成多个小块
+    createSegmentedProtocols(roomParams) {
+      const segments = [];
+      const MAX_SEGMENT_LENGTH = 64; // 每段最大长度
+
+      try {
+        // 将参数分成多个小段
+        const paramEntries = Object.entries(roomParams);
+        const chunks = [];
+
+        // 每次取两个键值对,将参数分成多个小块
+        for (let i = 0; i < paramEntries.length; i += 2) {
+          const chunk = Object.fromEntries(paramEntries.slice(i, i + 2));
+          chunks.push(chunk);
+        }
+
+        chunks.forEach((chunk, index) => {
+          const chunkStr = JSON.stringify(chunk);
+          const encoded = this.urlSafeBase64Encode(chunkStr);
+
+          if (encoded.length <= MAX_SEGMENT_LENGTH) {
+            segments.push(`p${index}-${encoded}`);
+          }
+        });
+
+        return segments;
+      } catch (error) {
+        console.error('创建分段协议失败:', error);
+        return [];
+      }
+    },
+
+    // 验证协议字符串的合法性
+    validateProtocols(protocols) {
+      const MAX_SEGMENT_LENGTH = 64 + 30; // WebSocket 子协议名称按照 RFC 6455 规范,建议长度不超过 64 字符
+      if (!protocols || !Array.isArray(protocols)) {
+        return false;
+      }
+
+      for (const protocol of protocols) {
+        // 检查协议名称是否符合规范(RFC 6455)
+        // 只允许字母、数字、连字符、下划线、点
+        if (!/^[a-zA-Z0-9\-._]+$/.test(protocol)) {
+          console.error('协议包含非法字符:', protocol);
+          return false;
+        }
+
+        // 检查长度限制
+        if (protocol.length > MAX_SEGMENT_LENGTH) {
+          console.error('协议长度超限:', protocol.length);
+          return false;
+        }
+        console.log('protocal -- ', protocol, protocol.length)
+      }
+
+      return true;
+    },
+
     // 断开WebSocket连接
     // 断开WebSocket连接
     disconnectWebSocket() {
     disconnectWebSocket() {
       if (this.socket) {
       if (this.socket) {
@@ -406,6 +579,7 @@ export default {
 
 
         // 构建要发送的消息数据
         // 构建要发送的消息数据
         const messageData = {
         const messageData = {
+          receiver: this.currentUser.customId,
           message: messageContent,
           message: messageContent,
           username: this.currentUser.username,
           username: this.currentUser.username,
           shopId: this.currentUser.shopId, // 门店使用shopId,客户使用customId
           shopId: this.currentUser.shopId, // 门店使用shopId,客户使用customId

+ 1 - 1
hdApp/src/admin/chat/list.vue

@@ -13,7 +13,7 @@
     <view class="list-wrap">
     <view class="list-wrap">
       <block v-if="$util.isEmpty(list.data) === false">
       <block v-if="$util.isEmpty(list.data) === false">
         <block v-for="(item, index) in list.data" :key="index">
         <block v-for="(item, index) in list.data" :key="index">
-          <tui-list-cell :arrow="true" @click="pageTo({ url: '/admin/chat/chatPage', query: {customId: item.id, shopId: getLoginInfo.shopId, name: getLoginInfo.name, account: getLoginInfo.shopId, avatar: shopAvatar } })">
+          <tui-list-cell :arrow="true" @click="pageTo({ url: '/admin/chat/chatPage', query: {customId: item.id, staffId: getLoginInfo.shopAdminId, shopId: getLoginInfo.shopId, name: getLoginInfo.name, account: getLoginInfo.shopId, avatar: shopAvatar } })">
             <view class="user-card">
             <view class="user-card">
               <view class="user-avatar">
               <view class="user-avatar">
                 <image :src="item.avatar" class="avatar-img" mode="aspectFill" />
                 <image :src="item.avatar" class="avatar-img" mode="aspectFill" />

+ 241 - 32
mallApp/src/pages/chat/chatPage.vue

@@ -31,8 +31,8 @@
               message.username
               message.username
             }}</view>
             }}</view>
             <view class="message-bubble left-bubble">
             <view class="message-bubble left-bubble">
-                <text class="message-text">{{ message.message }}</text>
-              </view>
+              <text class="message-text">{{ message.message }}</text>
+            </view>
             <view class="message-time">{{ message.time }}</view>
             <view class="message-time">{{ message.time }}</view>
           </view>
           </view>
         </view>
         </view>
@@ -86,7 +86,11 @@
 
 
 <script>
 <script>
 import { getChatHistory } from "@/api/chat";
 import { getChatHistory } from "@/api/chat";
+import {mapGetters } from "vuex";
 export default {
 export default {
+  computed: {
+    ...mapGetters(["getLoginInfo"]),
+  },
   data() {
   data() {
     return {
     return {
       inputText: "",
       inputText: "",
@@ -97,9 +101,9 @@ export default {
       isMultiUser: false,
       isMultiUser: false,
       // 当前用户信息
       // 当前用户信息
       currentUser: {
       currentUser: {
+        id: 0,
         customId: 0,
         customId: 0,
         shopId: 0,
         shopId: 0,
-        id: 0,
         username: "",
         username: "",
         avatar: "",
         avatar: "",
       },
       },
@@ -131,35 +135,27 @@ export default {
     if (options.name) {
     if (options.name) {
       this.currentUser.username = options.name;
       this.currentUser.username = options.name;
     }
     }
-    if (options.customId) {
-      // 如果是客户,则使用customId
-      this.currentUser.id = options.customId;
-    }
     if (options.avatar) {
     if (options.avatar) {
       this.currentUser.avatar = options.avatar;
       this.currentUser.avatar = options.avatar;
     }
     }
 
 
     // 获取房间号
     // 获取房间号
-    if (options.room) {
-      this.room = options.room;
+    if (this.currentUser.customId && this.currentUser.shopId) {
+      this.room =
+        "custom_id-" +
+        this.currentUser.customId +
+        "AND" +
+        "shop_id-" +
+        this.currentUser.shopId;
     } else {
     } else {
-      if (this.currentUser.customId && this.currentUser.shopId) {
-        this.room =
-          "custom_id-" +
-          this.currentUser.customId +
-          "AND" +
-          "shop_id-" +
-          this.currentUser.shopId;
-      } else {
-        // 如果没有传入房间号,使用默认值或生成一个
-        this.room = "default_room";
-      }
+      // 如果没有传入房间号,使用默认值或生成一个
+      this.room = "default_room";
     }
     }
+    
     console.log("---------- this.room: ", this.room);
     console.log("---------- this.room: ", this.room);
 
 
     // 加载聊天记录
     // 加载聊天记录
     this.loadChatHistory();
     this.loadChatHistory();
-
     // 连接WebSocket
     // 连接WebSocket
     this.connectWebSocket();
     this.connectWebSocket();
   },
   },
@@ -188,8 +184,6 @@ export default {
     init() {},
     init() {},
     // 加载聊天历史记录
     // 加载聊天历史记录
     loadChatHistory() {
     loadChatHistory() {
-      // 在实际应用中,这里应该从服务器加载历史聊天记录
-      // 可以通过API请求获取该房间的历史消息
       getChatHistory({
       getChatHistory({
         roomName: this.room,
         roomName: this.room,
         userId: this.currentUser.id,
         userId: this.currentUser.id,
@@ -205,16 +199,14 @@ export default {
 
 
     // 获取主机地址
     // 获取主机地址
     getHost() {
     getHost() {
-      // 在实际项目中,这里应该返回你的WebSocket服务器地址
-      // 可以从配置文件中读取
-
       // #ifdef H5
       // #ifdef H5
-      return window.location.host;
+      return this.$constant.webSocketUrl;
       // #endif
       // #endif
 
 
       // #ifdef MP-WEIXIN || APP-PLUS
       // #ifdef MP-WEIXIN || APP-PLUS
       // 小程序和App环境下,需要配置具体的服务器地址
       // 小程序和App环境下,需要配置具体的服务器地址
-      return "192.168.10.57:8080"; // 替换为实际的服务器地址
+      // return this.$constant.webSocketUrl;
+      return "192.168.10.182:8080";
       // #endif
       // #endif
     },
     },
 
 
@@ -230,12 +222,17 @@ export default {
       }
       }
 
 
       try {
       try {
-        // 构建WebSocket URL
-        const wsUrl = `ws://${this.getHost()}/room?room=${this.room}`;
+        // 构建WebSocket URL(移除查询参数)
+        const wsUrl = `ws://${this.getHost()}/room`;
+        console.log("---------- wsUrl: ", wsUrl);
+
+        // 构建子协议参数(模拟POST body传输)
+        const protocols = this.buildWebSocketProtocols();
 
 
-        // 创建WebSocket连接
+        // 创建WebSocket连接,使用子协议传递参数
         this.socket = uni.connectSocket({
         this.socket = uni.connectSocket({
           url: wsUrl,
           url: wsUrl,
+          protocols: protocols,
           success: () => {
           success: () => {
             console.log("WebSocket连接创建成功");
             console.log("WebSocket连接创建成功");
           },
           },
@@ -264,7 +261,10 @@ export default {
           this.isConnected = false;
           this.isConnected = false;
 
 
           // 只有在非主动断开的情况下才尝试重连
           // 只有在非主动断开的情况下才尝试重连
-          if (!this.isManualDisconnect && this.reconnectCount < this.maxReconnectCount) {
+          if (
+            !this.isManualDisconnect &&
+            this.reconnectCount < this.maxReconnectCount
+          ) {
             setTimeout(() => {
             setTimeout(() => {
               this.reconnectWebSocket();
               this.reconnectWebSocket();
             }, 3000);
             }, 3000);
@@ -283,6 +283,214 @@ export default {
       }
       }
     },
     },
 
 
+    // 构建WebSocket子协议参数(模拟POST body传输)
+    buildWebSocketProtocols() {
+      try {
+        // 创建房间参数对象
+        const roomParams = {
+          u_id: parseInt(this.loginInfo.id), // TODO 就是 customId,应该修改为: userId ???
+          c_id: parseInt(this.currentUser.customId),
+          s_id: parseInt(this.currentUser.shopId), //因为是花惠宝端用户,shopId为0,好推测出是花惠宝端连接
+          u_name: this.currentUser.username && this.currentUser.username.length > 30 ? this.currentUser.username.substring(0, 30) : this.currentUser.username,
+          platform: this.getPlatformInfo(),
+          type: "customer", // 客户端类型:shop:门店端, customer:客户端
+        };
+
+        // 分段传输(如果参数过长)
+        const segments = this.createSegmentedProtocols(roomParams);
+        if (segments.length == 0) {
+          this.$msg("分段传输失败");
+          return;
+        }
+
+        // 根据调试选项和长度选择合适的方案
+        let protocols;
+        if (segments.length > 0) {
+          protocols = ["chat", ...segments];
+        }
+
+        // 验证协议字符串的合法性
+        const isValid = this.validateProtocols(protocols);
+        console.log("协议验证结果:", isValid);
+        if (!isValid) {
+          this.$msg("协议验证失败");
+          return;
+        }
+
+        return protocols;
+      } catch (error) {
+        console.error("构建WebSocket protocols失败:", error);
+        return ["chat"]; // 返回基础协议作为fallback
+      }
+    },
+
+    // 验证协议字符串的合法性
+    validateProtocols(protocols) {
+      const MAX_SEGMENT_LENGTH = 64 + 30; // WebSocket 子协议名称按照 RFC 6455 规范,建议长度不超过 64 字符
+      if (!protocols || !Array.isArray(protocols)) {
+        return false;
+      }
+
+      for (const protocol of protocols) {
+        // 检查协议名称是否符合规范(RFC 6455)
+        // 只允许字母、数字、连字符、下划线、点
+        if (!/^[a-zA-Z0-9\-._]+$/.test(protocol)) {
+          console.error("协议包含非法字符:", protocol);
+          return false;
+        }
+
+        // 检查长度限制
+        if (protocol.length > MAX_SEGMENT_LENGTH) {
+          console.error("协议长度超限:", protocol.length);
+          return false;
+        }
+        console.log('protocal -- ', protocol, protocol.length)
+      }
+
+      return true;
+    },
+
+    // 获取平台信息
+    getPlatformInfo() {
+      // #ifdef H5
+      return "H5";
+      // #endif
+
+      // #ifdef MP-WEIXIN
+      return "MP-WEIXIN";
+      // #endif
+
+      // #ifdef MP-ALIPAY
+      return "MP-ALIPAY";
+      // #endif
+
+      // #ifdef APP-PLUS
+      return "APP-PLUS";
+      // #endif
+
+      return "UNKNOWN";
+    },
+
+    // Base64编码方法(跨平台兼容)
+    base64Encode(str) {
+      // #ifdef H5
+      // H5环境使用浏览器原生方法
+      try {
+        return btoa(str);
+      } catch (error) {
+        console.error("H5 Base64编码失败:", error);
+        return this.customBase64Encode(str);
+      }
+      // #endif
+
+      // #ifdef MP-WEIXIN || MP-ALIPAY || APP-PLUS
+      // 小程序和App环境使用自定义编码
+      return this.customBase64Encode(str);
+      // #endif
+    },
+
+    // 自定义Base64编码实现
+    customBase64Encode(str) {
+      const chars =
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+      let output = "";
+      let chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+      let i = 0;
+
+      // 转换为UTF-8字节
+      str = this.utf8Encode(str);
+
+      while (i < str.length) {
+        chr1 = str.charCodeAt(i++);
+        chr2 = str.charCodeAt(i++);
+        chr3 = str.charCodeAt(i++);
+
+        enc1 = chr1 >> 2;
+        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+        enc4 = chr3 & 63;
+
+        if (isNaN(chr2)) {
+          enc3 = enc4 = 64;
+        } else if (isNaN(chr3)) {
+          enc4 = 64;
+        }
+
+        output =
+          output +
+          chars.charAt(enc1) +
+          chars.charAt(enc2) +
+          chars.charAt(enc3) +
+          chars.charAt(enc4);
+      }
+
+      return output;
+    },
+
+    // UTF-8编码
+    utf8Encode(str) {
+      str = str.replace(/\r\n/g, "\n");
+      let utftext = "";
+
+      for (let n = 0; n < str.length; n++) {
+        const c = str.charCodeAt(n);
+
+        if (c < 128) {
+          utftext += String.fromCharCode(c);
+        } else if (c > 127 && c < 2048) {
+          utftext += String.fromCharCode((c >> 6) | 192);
+          utftext += String.fromCharCode((c & 63) | 128);
+        } else {
+          utftext += String.fromCharCode((c >> 12) | 224);
+          utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+          utftext += String.fromCharCode((c & 63) | 128);
+        }
+      }
+
+      return utftext;
+    },
+
+    // URL安全的Base64编码
+    urlSafeBase64Encode(str) {
+      // 先进行标准Base64编码
+      const base64 = this.base64Encode(str);
+
+      // 转换为URL安全格式:替换 +/= 字符
+      return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
+    },
+
+    // 创建分段协议(用于长参数)- 每次取两个键值对,将参数分成多个小块
+    createSegmentedProtocols(roomParams) {
+      const segments = [];
+      const MAX_SEGMENT_LENGTH = 64; // 每段最大长度
+
+      try {
+        // 将参数分成多个小段
+        const paramEntries = Object.entries(roomParams);
+        const chunks = [];
+
+        // 每次取两个键值对,将参数分成多个小块
+        for (let i = 0; i < paramEntries.length; i += 2) {
+          const chunk = Object.fromEntries(paramEntries.slice(i, i + 2));
+          chunks.push(chunk);
+        }
+
+        chunks.forEach((chunk, index) => {
+          const chunkStr = JSON.stringify(chunk);
+          const encoded = this.urlSafeBase64Encode(chunkStr);
+
+          if (encoded.length <= MAX_SEGMENT_LENGTH) {
+            segments.push(`p${index}-${encoded}`);
+          }
+        });
+
+        return segments;
+      } catch (error) {
+        console.error("创建分段协议失败:", error);
+        return [];
+      }
+    },
+
     // 断开WebSocket连接
     // 断开WebSocket连接
     disconnectWebSocket() {
     disconnectWebSocket() {
       if (this.socket) {
       if (this.socket) {
@@ -407,6 +615,7 @@ export default {
 
 
         // 构建要发送的消息数据
         // 构建要发送的消息数据
         const messageData = {
         const messageData = {
+          receiver: this.currentUser.shopId,
           message: messageContent,
           message: messageContent,
           username: this.currentUser.username,
           username: this.currentUser.username,
           customId: this.currentUser.customId, // 客户使用customId,门店使用shopId
           customId: this.currentUser.customId, // 客户使用customId,门店使用shopId