浏览代码

Merge branch 'master' of https://git.nanodreamtech.com/wkw/wallet_app

wkw 1 周之前
父节点
当前提交
19dc204300

+ 1 - 0
package.json

@@ -47,6 +47,7 @@
     "pinia": "^3.0.1",
     "pinia-plugin-persistedstate": "^4.4.1",
     "pinyin": "^4.0.0",
+    "protobufjs": "^7.5.3",
     "qrcode.vue": "^3.6.0",
     "semver": "^7.7.2",
     "sharp": "^0.34.2",

+ 18 - 0
src/common/constant/Constant.js

@@ -0,0 +1,18 @@
+export const AUDIO_ONLINE = 6; // 语音聊天
+export const VIDEO_ONLINE = 7; // 视频聊天
+
+export const DIAL_MEDIA_START = 10; // 拨打媒体开始占位符
+export const DIAL_AUDIO_ONLINE = 11; // 语音聊天拨号
+export const ACCEPT_AUDIO_ONLINE = 12; // 语音聊天接听
+export const CANCELL_AUDIO_ONLINE = 13; // 语音聊天取消
+export const REJECT_AUDIO_ONLINE = 14; // 语音聊天拒接
+
+export const DIAL_VIDEO_ONLINE = 15; // 视频聊天拨号
+export const ACCEPT_VIDEO_ONLINE = 16; // 视频聊天接听
+export const CANCELL_VIDEO_ONLINE = 17; // 视频聊天取消
+export const REJECT_VIDEO_ONLINE = 18; // 视频聊天拒接
+
+export const DIAL_MEDIA_END = 20; // 拨打媒体结束占位符
+
+
+export const MESSAGE_TRANS_TYPE = "webrtc"; // 消息传输类型:如果是心跳消息,该内容为heatbeat,在线视频或者音频为webrtc

+ 16 - 0
src/common/proto/message.proto

@@ -0,0 +1,16 @@
+syntax = "proto3";
+package protocol;
+
+message Message {
+    string avatar = 1;
+    string fromUsername = 2;
+    string from = 3;
+    string to = 4;
+    string content = 5;
+    int32 contentType = 6;
+    string type = 7;
+    int32 messageType = 8;
+    string url = 9;
+    string fileSuffix = 10;
+    bytes file = 11;
+}

+ 27 - 0
src/common/proto/proto.js

@@ -0,0 +1,27 @@
+import $protobuf from "protobufjs/light";
+// 定义 Protobuf 消息结构
+export const $root = (
+  $protobuf.roots["default"] || 
+  ($protobuf.roots["default"] = new $protobuf.Root())
+).addJSON({
+  protocol: {
+    nested: {
+      Message: {
+        fields: {
+          avatar: { type: "string", id: 1 },
+          fromUsername: { type: "string", id: 2 },
+          from: { type: "string", id: 3 },
+          to: { type: "string", id: 4 },
+          content: { type: "string", id: 5 },
+          contentType: { type: "int32", id: 6 },
+          type: { type: "string", id: 7 },
+          messageType: { type: "int32", id: 8 },
+          url: { type: "string", id: 9 },
+          fileSuffix: { type: "string", id: 10 },
+          file: { type: "bytes", id: 11 }
+        }
+      }
+    }
+  }
+});
+ 

+ 3 - 0
src/stores/modules/walletStore.js

@@ -41,6 +41,9 @@ export const useWalletStore = defineStore("useWalletStore", {
     getWalletList() {
       return this.walletList;
     },
+    getAccount(){
+      return this.account
+    }
   },
   actions: {
     async initWeb3() {

+ 181 - 0
src/stores/modules/webSocketStore.js

@@ -0,0 +1,181 @@
+// src/stores/websocket.ts
+import { defineStore } from 'pinia'
+import { ref } from 'vue' 
+import { $root as protobuf } from '@/common/proto/proto'
+import * as Constant from '@/common/constant/Constant'
+
+//  MessageType = $root.lookupType("protocol.Message");
+
+export const useWebSocketStore = defineStore('webSocketStore', () => {
+  const socket = ref(null) // 创建WebSocket对象
+  const peer = ref(null) // 创建RTCPeerConnection对象
+  const lockConnection = ref(false)
+  const reconnectAttempts = ref(0)
+  const maxReconnectAttempts = 5
+  const reconnectInterval = 3000
+
+  // 心跳检测
+  const heartCheck = {
+    timeout: 10000,
+    timeoutObj: null,
+    serverTimeoutObj: null,
+    num: 3,
+    start: function () {
+      const self = this
+      const _num = this.num
+      
+      this.timeoutObj && clearTimeout(this.timeoutObj)
+      this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj)
+      
+      this.timeoutObj = setTimeout(() => {
+        if (socket.value?.readyState === 1) {
+          const data = {
+            type: "heatbeat",
+            content: "ping",
+          }
+          const message = protobuf.lookup("protocol.Message")
+          const messagePB = message.create(data)
+          socket.value.send(message.encode(messagePB).finish())
+        }
+
+        self.serverTimeoutObj = setTimeout(() => {
+          _num--
+          if (_num <= 0) {
+            console.log("the ping num is more then 3, close socket!")
+            socket.value?.close()
+          }
+        }, self.timeout)
+      }, this.timeout)
+    }
+  }
+
+  // 连接WebSocket
+  const connect = (userUuid) => {
+    console.log("开始连接...")
+    peer.value = new RTCPeerConnection()
+    socket.value = new WebSocket(`ws://192.168.0.59:8888/api/v1/socket.io?user=${userUuid}`)
+
+    socket.value.onopen = () => {
+      heartCheck.start()
+      console.log("链接")
+      webrtcConnection()
+    }
+
+    socket.value.onmessage = (event) => {
+      heartCheck.start()
+      handleMessage(event.data)
+    }
+
+    socket.value.onclose = () => {
+      console.log("关闭并重新连接-->--->")
+      reconnect()
+    }
+
+    socket.value.onerror = () => {
+      console.log("error----->>>>")
+      reconnect()
+    }
+  }
+
+  // 处理接收到的消息
+  const handleMessage = (data) => {
+    const messageProto = protobuf.lookupType("protocol.Message")
+    const reader = new FileReader()
+    
+    reader.readAsArrayBuffer(data)
+    reader.onload = (event) => {
+      const messagePB = messageProto.decode(new Uint8Array(event.target?.result))
+      // 单独解码 
+      console.log(messagePB)
+      
+      if (messagePB.type === "heatbeat") return
+      
+      if (messagePB.type === Constant.MESSAGE_TRANS_TYPE) {
+        dealWebRtcMessage(messagePB)
+        return
+      }
+      
+      // 其他消息处理...
+    }
+  }
+
+  // WebRTC 连接
+  const webrtcConnection = () => {
+    if (!peer.value) return
+    
+    peer.value.onicecandidate = (e) => {
+      if (e.candidate && socket.value) {
+        const candidate = {
+          type: 'answer_ice',
+          iceCandidate: e.candidate
+        }
+        const message = {
+          content: JSON.stringify(candidate),
+          type: Constant.MESSAGE_TRANS_TYPE,
+        }
+        sendMessage(message)
+      }
+    }
+
+    peer.value.ontrack = (e) => {
+      if (e && e.streams) {
+        const remoteVideo = document.getElementById("remoteVideoReceiver")
+        const remoteAudio = document.getElementById("audioPhone")
+        
+        if (remoteVideo) remoteVideo.srcObject = e.streams[0]
+        if (remoteAudio) remoteAudio.srcObject = e.streams[0]
+      }
+    }
+  }
+
+  // 处理WebRTC消息
+  const dealWebRtcMessage = (messagePB) => {
+    // 实现与React版本相同的逻辑
+  }
+
+  // 重新连接
+  const reconnect = () => {
+    if (lockConnection.value) return
+    lockConnection.value = true
+
+    setTimeout(() => {
+      if (socket.value?.readyState !== 1) {
+        connect(localStorage.uuid)
+      }
+      lockConnection.value = false
+    }, 10000)
+  }
+
+  // 发送消息
+  const sendMessage = (messageData) => {
+    if (!socket.value) return
+    
+    const data = {
+      ...messageData,
+      fromUsername: localStorage.username,
+      from: localStorage.uuid,
+    }
+    
+    const message = protobuf.lookup("protocol.Message")
+    const messagePB = message.create(data)
+    socket.value.send(message.encode(messagePB).finish())
+  }
+
+  // 关闭连接
+  const close = () => {
+    socket.value?.close()
+    peer.value?.close()
+    socket.value = null
+    peer.value = null
+  }
+
+  return {
+    socket,
+    peer,
+    connect,
+    sendMessage,
+    close,
+    dealWebRtcMessage,
+    webrtcConnection
+  }
+})

+ 0 - 13
src/utils/websocket.js

@@ -1,13 +0,0 @@
-let socket;
-
-export function initWebSocket() {
-  socket = new WebSocket('wss://your-im-server.com');
-
-  socket.onmessage = (event) => {
-    const message = JSON.parse(event.data);
-    showLocalNotification(message);
-  };
-
-  // 断线重连逻辑
-  socket.onclose = () => setTimeout(initWebSocket, 5000);
-}

+ 10 - 1
src/views/im/index.vue

@@ -79,9 +79,12 @@ import { userList } from "@/api/path/im.api";
 import { ref } from 'vue'
 import { useRouter } from 'vue-router'
 import Discover from './components/Discover/Discover.vue'
-import { onMounted } from "vue"
+import { useWebSocketStore } from "@/stores/modules/webSocketStore.js"
+
+
 const walletStore = useWalletStore();
 const router = useRouter();
+const wsStore = useWebSocketStore()
 
 
 const activeTab = ref(0)
@@ -106,7 +109,13 @@ const goToPage = (url) => {
 }
 onMounted(()=>{
   getuserList();
+
+  wsStore.connect(walletStore.getAccount)
 })
+onUnmounted(() => {
+  wsStore.close()
+})
+
 </script>
 
 <style lang="less" scoped>