123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- // src/stores/websocket.ts
- import { defineStore } from "pinia";
- import { $root as protobuf } from "@/common/proto/proto";
- import * as Constant from "@/common/constant/Constant";
- import { useWalletStore } from "@/stores/modules/walletStore";
- import { getMessageApi } from "@/api/path/im.api";
- import { MSG_TYPE, MSG_TYPE_MAP } from "@/common/constant/msgType";
- import {
- setMessageHook,
- handleMessageHook,
- } from "@/views/im/hook/messagesHook";
- const IM_PATH = import.meta.env.VITE_IM_PATH_FIlE;
- export const useWebSocketStore = defineStore("webSocketStore", {
- // 状态定义
- state: () => ({
- socket: null, // socket实例
- peer: null, // peer实例
- lockConnection: false, // 锁定连接
- reconnectAttempts: 0, // 重连次数
- maxReconnectAttempts: 10, // 最大重连次数
- reconnectInterval: 3000, // 重连间隔
- messages: [], // 消息列表
- toUserInfo: {},
- unreadMessages: [], // 未读消息列表
- uuid: null,
- onMessageCallbacks: [],
- // 心跳检测配置
- heartCheck: {
- timeout: 10000, // 连接丢失后,多长时间内没有收到服务端的消息,则认为连接已断开
- timeoutObj: null, // 定时器对象
- serverTimeoutObj: null, // 服务器定时器对象
- num: 3, // 重连次数
- },
- }),
- persist: {
- key: "toUserInfo",
- storage: localStorage,
- paths: ["toUserInfo"], // 正确的简单路径格式
- serializer: {
- serialize: (state) => JSON.stringify(state.toUserInfo),
- deserialize: (str) => ({ toUserInfo: JSON.parse(str) }),
- },
- },
- // 计算属性
- getters: {
- isConnected: (state) => state.socket?.readyState === WebSocket.OPEN,
- hasUnreadMessages: (state) => state.unreadMessages.length > 0,
- },
- // 方法
- actions: {
- // 获取消息
- async getMessages(params) {
- const { data } = await getMessageApi({
- messageType: params.messageType,
- uuid: params.uuid,
- friendUsername: params.friendUsername,
- });
- this.messages =
- data?.map((item) => {
- item.avatar = item.avatar ? IM_PATH + item.avatar : item.avatar;
- return item;
- }) || [];
- },
- // 发送消息
- sendMessage(messageData) {
- if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
- console.error("WebSocket未连接");
- return false;
- }
-
- // 获取url上uuid参数
- const walletStore = useWalletStore();
- let data = {
- ...messageData,
- fromUsername: walletStore.username,
- from: walletStore.account,
- to: this.toUserInfo.uuid,
- };
- console.log("发送消息=", data)
- try {
- const MessageType = protobuf.lookupType("protocol.Message");
- const messagePB = MessageType.create(data);
- const buffer = MessageType.encode(messagePB).finish();
- this.socket.send(buffer);
- // 发送完成后:添加头像
- data.avatar = walletStore.avatar;
- setMessageHook(data, this);
- return true;
- } catch (error) {
- console.error("消息编码错误:", error);
- return false;
- }
- },
- // 初始化socket
- startHeartbeat() {
- const self = this;
- const _num = this.heartCheck.num;
- this.heartCheck.timeoutObj && clearTimeout(this.heartCheck.timeoutObj);
- this.heartCheck.serverTimeoutObj &&
- clearTimeout(this.heartCheck.serverTimeoutObj);
- this.heartCheck.timeoutObj = setTimeout(() => {
- if (this.socket?.readyState === WebSocket.OPEN) {
- const data = {
- type: "heatbeat",
- content: "ping",
- };
- const MessageType = protobuf.lookupType("protocol.Message");
- const messagePB = MessageType.create(data);
- const buffer = MessageType.encode(messagePB).finish();
- this.socket.send(buffer);
- }
- self.heartCheck.serverTimeoutObj = setTimeout(() => {
- _num--;
- if (_num <= 0) {
- console.log("the ping num is more then 3, close socket!");
- this.socket?.close();
- }
- }, self.heartCheck.timeout);
- }, this.heartCheck.timeout);
- },
- resetHeartbeat() {
- this.heartCheck.timeoutObj && clearTimeout(this.heartCheck.timeoutObj);
- this.heartCheck.serverTimeoutObj &&
- clearTimeout(this.heartCheck.serverTimeoutObj);
- this.heartCheck.num = 3;
- },
- // 链接ws
- connect(userUuid) {
- if (!userUuid) return;
- console.log("开始连接...");
- this.disconnect(); // 确保先断开现有连接
- this.peer = new RTCPeerConnection();
- this.socket = new WebSocket(
- `${import.meta.env.VITE_PRO_IM_WSS}?user=${userUuid}`
- );
- // 连接成功
- this.socket.onopen = () => {
- this.startHeartbeat();
- console.log("WebSocket连接成功");
- };
- this.socket.onmessage = (event) => {
- this.startHeartbeat();
- this.handleMessage(event.data);
- };
- this.socket.onclose = () => {
- console.log("连接关闭,尝试重新连接...");
- this.resetHeartbeat();
- this.reconnect();
- };
- this.socket.onerror = (error) => {
- console.error("WebSocket错误:", error);
- this.resetHeartbeat();
- this.reconnect();
- };
- },
- // 处理消息
- handleMessage(data) {
- const MessageType = protobuf.lookupType("protocol.Message");
- const reader = new FileReader();
- reader.onload = (event) => {
- try {
- const messagePB = MessageType.decode(
- new Uint8Array(event.target?.result)
- );
- const message = MessageType.toObject(messagePB, {
- longs: String,
- enums: String,
- bytes: String,
- });
- console.log("收到消息:", message);
- // 处理消息
- handleMessageHook(message, this);
- if (message.type === "heatbeat") return;
- if (message.type === Constant.MESSAGE_TRANS_TYPE) {
- this.dealWebRtcMessage(message);
- return;
- }
- // 其他消息处理逻辑...
- } catch (error) {
- console.error("消息解码错误:", error);
- }
- };
- reader.readAsArrayBuffer(data);
- },
- // 断线重连
- reconnect() {
- if (
- this.lockConnection ||
- this.reconnectAttempts >= this.maxReconnectAttempts
- ) {
- return;
- }
- this.lockConnection = true;
- this.reconnectAttempts++;
- console.log(
- `重新连接中... (尝试 ${this.reconnectAttempts}/${this.maxReconnectAttempts})`
- );
- setTimeout(() => {
- this.connect(localStorage.uuid);
- this.lockConnection = false;
- }, this.reconnectInterval);
- },
- // 重连
- disconnect() {
- if (this.socket) {
- this.resetHeartbeat();
- this.socket.close();
- this.socket = null;
- }
- if (this.peer) {
- this.peer.close();
- this.peer = null;
- }
- this.reconnectAttempts = 0;
- },
- // 发送WebRTC消息
- dealWebRtcMessage(message) {
- // 实现WebRTC消息处理逻辑
- },
- handleMessage(data) {
- const MessageType = protobuf.lookupType("protocol.Message");
- const reader = new FileReader();
- reader.onload = (event) => {
- try {
- const messagePB = MessageType.decode(new Uint8Array(event.target?.result));
- const message = MessageType.toObject(messagePB, {
- longs: String,
- enums: String,
- bytes: String,
- });
- console.log("收到消息:", message);
- handleMessageHook(message, this);
- // 调用所有回调函数
- this.onMessageCallbacks.forEach(cb => {
- try {
- cb(message);
- } catch (e) {
- console.error("消息回调错误:", e);
- }
- });
- if (message.type === "heatbeat") return;
- if (message.type === Constant.MESSAGE_TRANS_TYPE) {
- this.dealWebRtcMessage(message);
- return;
- }
- // 其他消息处理逻辑...
- } catch (error) {
- console.error("消息解码错误:", error);
- }
- };
- reader.readAsArrayBuffer(data);
- },
- addOnMessageCallback(cb) {
- if (typeof cb === "function") {
- this.onMessageCallbacks.push(cb);
- }
- },
- removeOnMessageCallback(cb) {
- this.onMessageCallbacks = this.onMessageCallbacks.filter(item => item !== cb);
- },
- },
- });
|