webSocketStore.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. // src/stores/websocket.js
  2. import { defineStore } from "pinia";
  3. import { $root as protobuf } from "@/common/proto/proto";
  4. import { useWalletStore } from "@/stores/modules/walletStore";
  5. import { getMessageApi, friendRequest, groupList } from "@/api/path/im.api";
  6. import { showDialog, showNotify } from 'vant';
  7. import { useSystemStore } from "@/stores/modules/systemStore";
  8. import * as MsgType from "@/common/constant/msgType";
  9. import {
  10. setMessageHook,
  11. handleMessageHook,
  12. } from "@/views/im/hook/messagesHook";
  13. // 配置常量
  14. const WS_CONFIG = {
  15. HEARTBEAT_INTERVAL: 30000, // 30秒
  16. HEARTBEAT_TIMEOUT: 10000, // 10秒
  17. HEARTBEAT_RETRY_LIMIT: 3,
  18. RECONNECT_BASE_INTERVAL: 1000,
  19. RECONNECT_MAX_INTERVAL: 30000,
  20. RECONNECT_MAX_ATTEMPTS: Infinity,
  21. RECONNECT_MULTIPLIER: 1.5,
  22. MESSAGE_QUEUE_MAX_SIZE: 100,
  23. MESSAGE_QUEUE_FLUSH_INTERVAL: 5000,
  24. };
  25. export const useWebSocketStore = defineStore("webSocketStore", {
  26. state: () => ({
  27. socket: null,
  28. peer: null,
  29. connectionState: "disconnected", // 'connecting', 'connected', 'disconnecting', 'error'
  30. reconnectAttempts: 0,
  31. sessionId:null,//当前会话id
  32. messages: [],
  33. groupMembersList:{},//群成员列表
  34. toUserInfo: {}, // 会话目标信息
  35. toUserAudioInfo: {}, // 会话目标音频信息
  36. unreadMessages: [],
  37. uuid: null,
  38. onMessageCallbacks: [],
  39. messageQueue: [],
  40. lastActivityTime: 0,
  41. stats: {
  42. sentMessages: 0,
  43. receivedMessages: 0,
  44. failedMessages: 0,
  45. reconnects: 0,
  46. },
  47. heartbeat: {
  48. timer: null,
  49. timeout: null,
  50. retryCount: 0,
  51. },
  52. reconnect: {
  53. timer: null,
  54. currentAttempt: 0,
  55. },
  56. queueFlushTimer: null,
  57. loading:false,
  58. applyfriend:{},//好友申请数据
  59. unread:false,//判断有无好友申请
  60. chatDelAuth: {}, //控制会话权限
  61. chatGroupAuth: {}, // 群会话权限
  62. chatRefresh:0,//强制刷新会话列表
  63. needRefreshIm:0,//软刷新会话列表
  64. indexs:0,// 接收消息计数器
  65. isassign:'',//是否@自己
  66. }),
  67. persist: {
  68. key: "toUserInfo",
  69. storage: localStorage,
  70. paths: ["toUserInfo", "uuid"],
  71. serializer: {
  72. serialize: (state) =>
  73. JSON.stringify({
  74. toUserInfo: state.toUserInfo,
  75. uuid: state.uuid,
  76. }),
  77. deserialize: (str) => {
  78. const data = JSON.parse(str);
  79. return {
  80. toUserInfo: data.toUserInfo || {},
  81. uuid: data.uuid || null,
  82. };
  83. },
  84. },
  85. },
  86. getters: {
  87. isConnected(state) {
  88. return state.connectionState === "connected";
  89. },
  90. hasUnreadMessages(state) {
  91. return state.unreadMessages.length > 0;
  92. },
  93. connectionStatus(state) {
  94. return state.connectionState;
  95. },
  96. },
  97. actions: {
  98. // 监听前后台切换
  99. changeAppState(state) {
  100. // if(state){
  101. // // 前台
  102. // }
  103. // else {
  104. // // 后台
  105. // }
  106. // 检测是否断开连接
  107. if(!this.isConnected){
  108. console.warn("前后台切换Websocket 已断开重新连接");
  109. this.disconnect();
  110. this.connect(this.uuid);
  111. }
  112. },
  113. // 监听网络变化
  114. changeNetworkState(state) {
  115. // 检测是否断开连接
  116. if(!this.isConnected){
  117. console.warn("网络波动Websocket 已断开重新连接");
  118. this.disconnect();
  119. this.connect(this.uuid);
  120. }
  121. },
  122. // 初始化连接
  123. connect(userUuid, callback) {
  124. if (!userUuid) {
  125. console.error("连接失败: 缺少 userUuid");
  126. return;
  127. }
  128. // 如果正在连接或已连接,则先断开
  129. if (
  130. this.connectionState === "connected" ||
  131. this.connectionState === "connecting"
  132. ) {
  133. this.disconnect();
  134. }
  135. this.uuid = userUuid;
  136. this.connectionState = "connecting";
  137. this.stats.reconnects++;
  138. try {
  139. const wsUrl = `${import.meta.env.VITE_PRO_IM_WSS}?user=${userUuid}`;
  140. this.socket = new WebSocket(wsUrl);
  141. this.socket.onopen = () => {
  142. this.connectionState = "connected";
  143. this.reconnect.currentAttempt = 0;
  144. this.lastActivityTime = Date.now();
  145. this.startHeartbeat();
  146. this.startQueueFlush();
  147. if (callback && typeof callback === "function") {
  148. callback();
  149. }
  150. console.log("WebSocket 连接成功");
  151. showNotify({ type: 'success', message: '服务器连接成功' });
  152. this.flushMessageQueue(); // 连接成功后立即发送队列中的消息
  153. };
  154. this.socket.onmessage = (event) => {
  155. this.lastActivityTime = Date.now();
  156. this.handleMessage(event.data);
  157. };
  158. this.socket.onclose = () => {
  159. this.connectionState = "disconnected";
  160. showNotify({ type: 'warning', message: '服务器连接断开' });
  161. this.cleanupTimers();
  162. if (
  163. this.reconnect.currentAttempt < WS_CONFIG.RECONNECT_MAX_ATTEMPTS
  164. ) {
  165. console.error("WebSocket被关闭, 重新连接");
  166. this.scheduleReconnect();
  167. }
  168. };
  169. this.socket.onerror = (error) => {
  170. this.connectionState = "error";
  171. console.error("WebSocket 错误并 重新连接:", error);
  172. showNotify({ type: 'warning', message: '服务器连接断开' });
  173. this.cleanupTimers();
  174. this.scheduleReconnect();
  175. };
  176. } catch (error) {
  177. console.error("创建 WebSocket 失败并 重新连接:", error);
  178. this.connectionState = "error";
  179. this.scheduleReconnect();
  180. }
  181. },
  182. // 断开连接
  183. disconnect() {
  184. this.connectionState = "disconnecting";
  185. this.cleanupTimers();
  186. if (this.socket) {
  187. try {
  188. this.socket.close();
  189. } catch (e) {
  190. console.error("关闭 WebSocket 时出错:", e);
  191. }
  192. this.socket = null;
  193. }
  194. if (this.peer) {
  195. this.peer.close();
  196. this.peer = null;
  197. }
  198. this.connectionState = "disconnected";
  199. },
  200. // 清理所有定时器
  201. cleanupTimers() {
  202. if (this.heartbeat.timer) clearTimeout(this.heartbeat.timer);
  203. if (this.heartbeat.timeout) clearTimeout(this.heartbeat.timeout);
  204. if (this.reconnect.timer) clearTimeout(this.reconnect.timer);
  205. if (this.queueFlushTimer) clearInterval(this.queueFlushTimer);
  206. this.heartbeat.timer = null;
  207. this.heartbeat.timeout = null;
  208. this.reconnect.timer = null;
  209. this.queueFlushTimer = null;
  210. this.heartbeat.retryCount = 0;
  211. },
  212. // 启动心跳检测
  213. startHeartbeat() {
  214. this.cleanupHeartbeat();
  215. this.heartbeat.timer = setTimeout(() => {
  216. if (this.isConnected) {
  217. try {
  218. const pingMsg = {
  219. type: "heatbeat",
  220. content: "ping",
  221. timestamp: Date.now(),
  222. };
  223. this.sendRawMessage(pingMsg);
  224. // 设置响应超时检测
  225. this.heartbeat.timeout = setTimeout(() => {
  226. this.heartbeat.retryCount++;
  227. if (
  228. this.heartbeat.retryCount >= WS_CONFIG.HEARTBEAT_RETRY_LIMIT
  229. ) {
  230. console.error("心跳响应超时,关闭连接并重新连接");
  231. this.disconnect();
  232. } else {
  233. if(this.heartbeat.retryCount > 1){
  234. console.warn(
  235. `心跳无响应,第${this.heartbeat.retryCount - 1}次重试`
  236. );
  237. }
  238. this.startHeartbeat(); // 重新发送心跳
  239. }
  240. }, WS_CONFIG.HEARTBEAT_TIMEOUT);
  241. } catch (error) {
  242. console.error("心跳发送失败:", error);
  243. this.scheduleReconnect();
  244. }
  245. }
  246. }, WS_CONFIG.HEARTBEAT_INTERVAL);
  247. },
  248. // 清理心跳定时器
  249. cleanupHeartbeat() {
  250. if (this.heartbeat.timer) clearTimeout(this.heartbeat.timer);
  251. if (this.heartbeat.timeout) clearTimeout(this.heartbeat.timeout);
  252. this.heartbeat.timer = null;
  253. this.heartbeat.timeout = null;
  254. },
  255. // 安排重连
  256. scheduleReconnect() {
  257. if (this.reconnect.timer) return;
  258. this.reconnect.currentAttempt++;
  259. // 指数退避算法计算重连间隔
  260. const delay = Math.min(
  261. WS_CONFIG.RECONNECT_BASE_INTERVAL *
  262. Math.pow(
  263. WS_CONFIG.RECONNECT_MULTIPLIER,
  264. this.reconnect.currentAttempt - 1
  265. ),
  266. WS_CONFIG.RECONNECT_MAX_INTERVAL
  267. );
  268. console.log(
  269. `将在 ${delay}ms 后尝试第 ${this.reconnect.currentAttempt} 次重连`
  270. );
  271. this.reconnect.timer = setTimeout(() => {
  272. this.reconnect.timer = null;
  273. if (!this.isConnected && this.uuid) {
  274. this.connect(this.uuid);
  275. }
  276. }, delay);
  277. },
  278. // 发送原始消息(不经过队列)
  279. sendRawMessage(messageData) {
  280. if (!this.isConnected) {
  281. throw new Error("WebSocket 未连接");
  282. }
  283. try {
  284. const MessageType = protobuf.lookupType("protocol.Message");
  285. const messagePB = MessageType.create(messageData);
  286. const buffer = MessageType.encode(messagePB).finish();
  287. this.socket.send(buffer);
  288. this.stats.sentMessages++;
  289. return true;
  290. } catch (error) {
  291. console.error("消息编码错误:", error);
  292. this.stats.failedMessages++;
  293. return false;
  294. }
  295. },
  296. // 发送消息(自动排队)
  297. sendMessage(messageData) {
  298. const walletStore = useWalletStore();
  299. // 构造完整消息
  300. const fullMessage = {
  301. fromUsername: this.toUserInfo.nickname,
  302. avatar: walletStore.avatar,
  303. to: this.toUserInfo.uuid,
  304. from: walletStore.account,
  305. ...messageData,
  306. timestamp: Date.now(),
  307. };
  308. // 如果连接正常,直接发送
  309. if (this.isConnected) {
  310. const success = this.sendRawMessage(fullMessage);
  311. if (success) {
  312. setMessageHook(fullMessage, this);
  313. return true;
  314. }
  315. }
  316. // 如果发送失败或未连接,加入队列
  317. if (this.messageQueue.length < WS_CONFIG.MESSAGE_QUEUE_MAX_SIZE) {
  318. this.messageQueue.push(fullMessage);
  319. console.log("消息已加入队列,等待发送");
  320. return true;
  321. } else {
  322. console.error("消息队列已满,丢弃消息");
  323. return false;
  324. }
  325. },
  326. // 启动队列刷新定时器
  327. startQueueFlush() {
  328. if (this.queueFlushTimer) return;
  329. this.queueFlushTimer = setInterval(() => {
  330. this.flushMessageQueue();
  331. }, WS_CONFIG.MESSAGE_QUEUE_FLUSH_INTERVAL);
  332. },
  333. // 发送队列中的所有消息
  334. flushMessageQueue() {
  335. if (!this.isConnected || this.messageQueue.length === 0) return;
  336. while (this.messageQueue.length > 0) {
  337. const message = this.messageQueue.shift();
  338. try {
  339. const success = this.sendRawMessage(message);
  340. if (success) {
  341. setMessageHook(message, this);
  342. } else {
  343. // 发送失败,重新放回队列开头
  344. this.messageQueue.unshift(message);
  345. break;
  346. }
  347. } catch (error) {
  348. console.error("发送队列消息失败:", error);
  349. this.messageQueue.unshift(message);
  350. break;
  351. }
  352. }
  353. },
  354. // 处理接收到的消息
  355. handleMessage(data) {
  356. const MessageType = protobuf.lookupType("protocol.Message");
  357. const reader = new FileReader();
  358. reader.onload = (event) => {
  359. try {
  360. const messagePB = MessageType.decode(
  361. new Uint8Array(event.target.result)
  362. );
  363. const message = MessageType.toObject(messagePB, {
  364. longs: String,
  365. enums: String,
  366. bytes: String,
  367. });
  368. this.stats.receivedMessages++;
  369. this.lastActivityTime = Date.now();
  370. handleMessageHook(message, this);
  371. // 调用所有回调函数
  372. this.onMessageCallbacks.forEach((cb) => {
  373. try {
  374. cb(message);
  375. } catch (e) {
  376. console.error("消息回调错误:", e);
  377. }
  378. });
  379. if (message.type === "heatbeat") {
  380. this.heartbeat.retryCount = 0; // 重置心跳重试计数
  381. return;
  382. }
  383. } catch (error) {
  384. console.error("消息解码错误:", error);
  385. this.stats.failedMessages++;
  386. }
  387. };
  388. reader.readAsArrayBuffer(data);
  389. },
  390. // ************功能*************//
  391. // 添加消息回调
  392. addOnMessageCallback(cb) {
  393. if (typeof cb === "function" && !this.onMessageCallbacks.includes(cb)) {
  394. this.onMessageCallbacks.push(cb);
  395. }
  396. },
  397. // 移除消息回调
  398. removeOnMessageCallback(cb) {
  399. this.onMessageCallbacks = this.onMessageCallbacks.filter(
  400. (item) => item !== cb
  401. );
  402. },
  403. // 新消息入栈本地
  404. pushMessage(message, sessionId = null) {
  405. const systemStore = useSystemStore();
  406. if(!sessionId){
  407. sessionId = message.from;
  408. }
  409. if (!systemStore.messageList[sessionId]) {
  410. systemStore.messageList[sessionId] = [];
  411. if (sessionId == message.from){
  412. // this.getMessages({ uuid: message.from, friendUsername: message.to, messageType: message.messageType },true);
  413. }
  414. }
  415. systemStore.messageList[sessionId].push(message);
  416. // 检测当前消息
  417. if (this.toUserInfo.uuid === sessionId) {
  418. this.messages.push(message);
  419. this.indexs +=1;
  420. }
  421. // console.log('pushMessage', systemStore.messageList, this.messages)
  422. // 更新会话最新消息
  423. this.updateSessionNewMessage(message, sessionId)
  424. },
  425. // 更新本地真实消息id
  426. modifyMessageId(message, msg, sessionId = null) {
  427. // console.log(msg, message, sessionId)
  428. const systemStore = useSystemStore();
  429. if(!sessionId){
  430. sessionId = message.from;
  431. }
  432. if (!systemStore.messageList[sessionId]) {
  433. systemStore.messageList[sessionId] = [];
  434. }
  435. systemStore.messageList[sessionId] = [...systemStore.messageList[sessionId]].map(item => {
  436. // 消息发送成功确认
  437. if (!item.id && msg.msgId && item.msgId + '' === msg.msgId + '') return {...item, id: msg.id, msgId: undefined, err: !msg.id?true:undefined};
  438. // 阅后即焚
  439. // console.log(item.id, msg?.id)
  440. if (item.id + '' === msg?.id + '') return {...item, isTemp: true, content: "", url: ""};
  441. return item;
  442. });
  443. // 检测当前消息
  444. if (this.toUserInfo.uuid === sessionId) {
  445. this.messages = [...this.messages].map(item => {
  446. // 消息发送成功确认
  447. if (!item.id && msg.msgId && item.msgId + '' === msg?.msgId + '') return { ...item, id: msg.id, msgId: undefined, err: !msg.id?true:undefined};
  448. // 阅后即焚
  449. if (item.id + '' === msg?.id + '') return { ...item, isTemp: true, content: "", url: ""};
  450. return item;
  451. });
  452. }
  453. console.log("修改消息", message, sessionId, systemStore.messageList,this.messages);
  454. },
  455. // 更新本地消息
  456. modifyMessage(message, msgId, sessionId = null) {
  457. const systemStore = useSystemStore();
  458. if(!sessionId){
  459. sessionId = message.from;
  460. }
  461. if (!systemStore.messageList[sessionId]) {
  462. systemStore.messageList[sessionId] = [];
  463. }
  464. systemStore.messageList[sessionId] = [...systemStore.messageList[sessionId]].map(item => {
  465. if(typeof msgId === 'function'){
  466. let msg = {};
  467. if(msg = msgId(item, sessionId)){
  468. return { ...item, ...msg};
  469. }
  470. }
  471. else if (item.id + '' === msgId + '') return {...item, ...message};
  472. return item;
  473. });
  474. // 检测当前消息
  475. if (this.toUserInfo.uuid === sessionId) {
  476. this.messages = [...this.messages].map(item => {
  477. if(typeof msgId === 'function'){
  478. let msg = {};
  479. if(msg = msgId(item, sessionId)){
  480. return {...item,...msg};
  481. }
  482. }
  483. else if (item.id + '' === msgId + '') return {...item, ...message};
  484. return item;
  485. });
  486. }
  487. // console.log('this.messages',this.messages)
  488. },
  489. // 删除本地消息
  490. deleteMessage(message, msgId, sessionId = null) {
  491. const systemStore = useSystemStore();
  492. if(!sessionId){
  493. sessionId = message.from;
  494. }
  495. if (!systemStore.messageList[sessionId]) {
  496. systemStore.messageList[sessionId] = [];
  497. }
  498. // 删除所有消息
  499. if (msgId === true || msgId === 'all') {
  500. systemStore.messageList[sessionId] = [];
  501. if (this.toUserInfo.uuid === sessionId) {
  502. this.messages = [];
  503. }
  504. return;
  505. }
  506. // console.log("删除消息", message, sessionId, systemStore.messageList, this.messages);
  507. systemStore.messageList[sessionId] = systemStore.messageList[sessionId].filter(item => {
  508. if (item.id + '' === msgId + '') return false;
  509. return true;
  510. });
  511. // 检测当前消息
  512. if (this.toUserInfo.uuid === sessionId) {
  513. this.messages = this.messages.filter(item => {
  514. if (item.id + '' === msgId + '') return false;
  515. return true;
  516. });
  517. }
  518. },
  519. // 发送消息已收到
  520. systemReceiptMessage(message, msg){
  521. if(!msg || !msg.uniqueId){
  522. return;
  523. }
  524. const response = {
  525. from: message.to,
  526. to: message.from,
  527. messageType: MsgType.MESSAGE_SYSTEM_RECEIPT,
  528. content: JSON.stringify({
  529. uniqueId: msg.uniqueId,
  530. timestamp: Date.now(),
  531. }),
  532. };
  533. // console.log("systemReceiptMessage", response);
  534. this.sendMessage(response)
  535. },
  536. // 获取会话消息
  537. getSessionMessages(sessionId) {
  538. const systemStore = useSystemStore();
  539. if (!systemStore.messageList[sessionId]) {
  540. systemStore.messageList[sessionId] = [];
  541. }
  542. return [...systemStore.messageList[sessionId]];
  543. },
  544. // 设置当前会话消息
  545. setSessionMessages(sessionId, messages,all=false) {
  546. const systemStore = useSystemStore();
  547. let data = Array.isArray(messages) ? messages : (typeof messages === 'object' ? [messages] : []);
  548. if (!systemStore.messageList[sessionId]) {
  549. systemStore.messageList[sessionId] = [];
  550. }
  551. if(all){
  552. systemStore.messageList[sessionId] = data //.filter(val=>!systemStore.messageList[sessionId].find(v=>v.id == val.id))
  553. return;
  554. }
  555. systemStore.messageList[sessionId] = data;
  556. },
  557. // 更换会话
  558. changeSessionMessage(sessionId) {
  559. this.sessionId = sessionId;
  560. this.messages = this.getSessionMessages(sessionId);
  561. },
  562. // 获取历史消息
  563. async getMessages(params,all=false) {
  564. const router = useRouter();
  565. const systemStore = useSystemStore();
  566. this.loading = true // 开始加载
  567. this.changeSessionMessage(params.uuid);
  568. if (this.messages.length > 0 && !all) {
  569. this.loading = false;
  570. return;
  571. }
  572. try {
  573. const res = await getMessageApi({
  574. messageType: params.messageType,
  575. uuid: params.uuid,
  576. friendUsername: params.friendUsername,
  577. });
  578. if(res.code == 200){
  579. this.setSessionMessages(this.sessionId, (res.data || []), all);
  580. this.changeSessionMessage(this.sessionId);
  581. // return this.messages;
  582. }else{
  583. showDialog({
  584. title: '提示',
  585. message: res.msg,
  586. confirmButtonColor:"#4765dd",
  587. }).then(() => {
  588. systemStore.needRefreshIm = true;
  589. router.back();
  590. });
  591. }
  592. } catch (error) {
  593. console.error("获取消息失败:", error);
  594. throw error;
  595. } finally {
  596. this.loading = false // 结束加载
  597. }
  598. },
  599. // 更新会话列表消息
  600. updateSessionNewMessage(message, sessionId, msg = null) {
  601. console.log("更新会话列表消息:", message, sessionId, msg);
  602. // 过滤接收的系统消息
  603. if (MsgType.MSG_SYSTEM_GROUP.includes(message.messageType)){
  604. return;
  605. }
  606. const systemStore = useSystemStore();
  607. let index = systemStore.ImsessionList.findIndex(val=> val.uuid == sessionId);
  608. // 已读未读
  609. let unReadNum = 0;
  610. const ts = message.timestamp || Math.floor(Date.now());
  611. const time = new Date(ts).toLocaleString().replaceAll("/", "-");
  612. // 消息类型映射
  613. const mapSet = {
  614. [MsgType.MSG_TYPE.FILE]: '[文件]',
  615. [MsgType.MSG_TYPE.IMAGE]: '[图片]',
  616. [MsgType.MSG_TYPE.AUDIO]: '[音频]',
  617. [MsgType.MSG_TYPE.VIDEO]: '[视频]',
  618. [MsgType.MSG_TYPE.AUDIO_ONLINE]: '[语音通话]',
  619. [MsgType.MSG_TYPE.VIDEO_ONLINE]: '[视频通话]',
  620. // [Constant.CANCELL_AUDIO_ONLINE]:'[通话结束]',
  621. // [Constant.REJECT_AUDIO_ONLINE]: '[对方拒绝]',
  622. // [Constant.CANCELL_VIDEO_ONLINE]: '[通话结束]',
  623. // [Constant.REJECT_VIDEO_ONLINE]: '[对方拒绝]',
  624. };
  625. // 检测最新消息
  626. let lastMsg = message.contentType === MsgType.MSG_TYPE.TEXT || message.contentType === MsgType.MSG_TYPE.NOTICE ? (msg?msg.content : message.content) : (mapSet[message.contentType] || '[语音视频]');
  627. // let lastMsg = msg ? msg.content : message.content;
  628. // 阅后即焚消息
  629. if(message.isTemp){
  630. lastMsg = '[阅后即焚]';
  631. }
  632. // 额外数据
  633. const ext = {
  634. updatedAt: time,
  635. };
  636. // 修改群公告信息
  637. if (message.nickname) {
  638. ext.sessionName = message.nickname
  639. }
  640. if (message.notice) {
  641. ext.notice = message.notice
  642. }
  643. if (message.slience || message.slience === 0) {
  644. ext.slience = message.slience
  645. }
  646. if (message.stick || message.stick === 0) {
  647. ext.stick = message.stick
  648. }
  649. // 补充消息完整性
  650. // 更新并排序
  651. if(index >= 0){
  652. systemStore.ImsessionList = [...systemStore.ImsessionList].map((item)=>{
  653. if (item.uuid == sessionId){
  654. // console.log('111', item, lastMsg, lastMsg,item.message)
  655. return { ...item, ...ext, unReadNum: item.unReadNum + 1, message: lastMsg || item.message};
  656. }
  657. return item;
  658. }).sort((a, b) => b.stick - a.stick || new Date(b.updatedAt) - new Date(a.updatedAt));
  659. // 软刷新会话列表
  660. this.needRefreshIm += 1;
  661. return;
  662. }
  663. // 强制刷新会话列表
  664. this.chatRefresh += 1;
  665. },
  666. // 修改好友备注后,将好友会话名称更改
  667. updateNote(newName,uuid){
  668. const systemStore = useSystemStore();
  669. systemStore.ImsessionList = [...systemStore.ImsessionList].map((item) => {
  670. if (item.uuid == uuid) {
  671. return { ...item, sessionName: newName };
  672. }
  673. return item;
  674. })
  675. if (this.toUserInfo.uuid == uuid){
  676. this.toUserInfo.sessionName = newName;
  677. }
  678. },
  679. // ************ 接收方 方法 ************//
  680. // 监听好友系统消息
  681. listenFriendSystem(message) {
  682. // 好友申请
  683. if (message.messageType == MsgType.MESSAGE_APPLY_FRIEND) {
  684. this.addFriendApply(message.from);
  685. }
  686. // 好友通过
  687. if (message.messageType == MsgType.MESSAGE_PASS_FRIEND) {
  688. // 移除好友申请记录
  689. this.deleteFriendApply(message.from);
  690. // 恢复会话功能
  691. this.funRestoreDelAuth(message.from);
  692. // 需要更新会话或创建会话
  693. this.chatRefresh += 1;
  694. }
  695. // 好友删除
  696. if (message.messageType == MsgType.MESSAGE_DELETE_FRIEND) {
  697. // 禁用会话功能
  698. this.funDeleteDelAuth(message.from);
  699. // 需要删除会话
  700. // 删除本地消息记录
  701. this.deleteMessage(message, true);
  702. }
  703. },
  704. // 监控群系统消息
  705. listenGroupSystemMessage(message, msg = null) {
  706. console.log('群系统消息', message, msg);
  707. // 解散群/提出群/退群
  708. if (message.messageType == MsgType.DELETE_GROUP || message.messageType == MsgType.REMOVE_GROUP || message.messageType == MsgType.EXIT_GROUP) {
  709. // 如果存在于users中(被提出或退出的人)或者解散群
  710. if ((msg && msg.users && msg.users.includes(message.to)) || message.messageType == MsgType.DELETE_GROUP) {
  711. // 禁用会话功能
  712. this.funDeleteDelAuth(message.from);
  713. // 删除会话
  714. this.deleteSessionChat(message.from);
  715. }
  716. }
  717. // 创建群/加入群/邀请入群
  718. if (message.messageType == MsgType.CREATE_GROUP || message.messageType == MsgType.JION_GROUP || message.messageType == MsgType.INVITATION_GROUP) {
  719. // console.log('群系统消息', message, msg);
  720. if (message.messageType == MsgType.JION_GROUP || message.messageType == MsgType.INVITATION_GROUP) {
  721. // 恢复会话功能
  722. this.funRestoreDelAuth(message.from);
  723. }
  724. // 刷新会话列表
  725. this.chatRefresh += 1;
  726. }
  727. // 群通知
  728. if (message.messageType == MsgType.MESSAGE_NOTICE_GROUP) {
  729. // 更新群名称和群公告
  730. this.funGroupNotice(msg);
  731. }
  732. // 处理群置顶
  733. if (message.messageType === MsgType.MESSAGE_STICKY_GROUP) {
  734. }
  735. // 更新群成员列表
  736. this.fetchGroupMembers(message.from,true);
  737. // 处理@群成员
  738. if (message.messageType == MsgType.MESSAGE_CC_GROUP){
  739. let ccArr = msg?.cc.split(',');
  740. if (this.groupMembersList && this.groupMembersList[message.from]) {
  741. this.isassign = this.groupMembersList[message.from].filter(val => ccArr.includes(val.userId + '')).find(val => val.uuid == message.to)
  742. }
  743. }
  744. // 处理群个人昵称
  745. if (message.messageType == MsgType.MESSAGE_NICKNAME_GROUP){
  746. this.modifyMessage(message,(val)=>{
  747. return msg.uuid == val.fromUuid?{
  748. fromUsername: msg.name
  749. } : (msg.uuid == val.toUuid ? {
  750. toUsername: msg.name
  751. }:{})
  752. });
  753. // 个人群昵称
  754. if (msg.name) {
  755. this.updateSessionNewMessage({ name: msg.name }, msg.uuid);
  756. if (this.toUserInfo.uuid === msg.uuid) {
  757. this.toUserInfo.nickname = msg.name
  758. }
  759. }
  760. }
  761. },
  762. // 语音视频通知
  763. // 群通知更新群名称和公告
  764. funGroupNotice(msg){
  765. this.toUserInfo = {
  766. ...this.toUserInfo,
  767. // 群公告
  768. notice: msg && msg.notice?msg.notice: this.toUserInfo.notice,
  769. nickname: msg && msg.name?msg.name:this.toUserInfo.nickname,
  770. // 群名称
  771. sessionName: msg && msg.name?msg.name:this.toUserInfo.sessionName,
  772. }
  773. },
  774. // 群消息置顶
  775. funGroupTop(message = null) {
  776. },
  777. // ************ 发送方 方法 ************//
  778. // 同意或者拒绝好友操作
  779. funApprovalFriend(message){
  780. if (message.messageType == MsgType.MESSAGE_REJECT_FRIEND || message.messageType == MsgType.MESSAGE_PASS_FRIEND) {
  781. console.log(message, this.applyfriend);
  782. // 删除申请记录
  783. this.deleteFriendApply(message.to);
  784. // 恢复会话权限
  785. this.funRestoreDelAuth(message.to);
  786. // 刷新/更新会话列表
  787. this.chatRefresh += 1;
  788. }
  789. this.funDeleteFriend(message)
  790. },
  791. // 删除好友操作
  792. funDeleteFriend(message) {
  793. if (message.messageType == MsgType.MESSAGE_DELETE_FRIEND) {
  794. // 禁用会话功能
  795. this.funDeleteDelAuth(message.to);
  796. // 删除本地消息记录
  797. this.deleteMessage(message, true,message.to);
  798. // 更新会话列表
  799. }
  800. },
  801. // ************ 公用 ************//
  802. // 删除好友申请记录
  803. deleteFriendApply(uuid){
  804. if (this.applyfriend[uuid]) {
  805. delete this.applyfriend[uuid]
  806. }
  807. this.unread = Object.keys(this.applyfriend).length > 0;
  808. },
  809. // 更新好友申请记录
  810. updateFriendApply(list){
  811. Array.isArray(list) && list.forEach(val=>{
  812. if (typeof val == 'string'){
  813. this.applyfriend[val] = 1;
  814. return;
  815. }
  816. this.applyfriend[val.uuid] = 1
  817. });
  818. this.unread = Object.keys(this.applyfriend).length > 0;
  819. },
  820. // 添加好友申请记录
  821. addFriendApply(uuid){
  822. this.applyfriend[uuid] = 1;
  823. this.unread = true;
  824. },
  825. // 初始化好友申请记录
  826. async funInitfriend(uuid,force=false){
  827. console.log('好友申请数据',this.applyfriend)
  828. if (!force && Object.keys(this.applyfriend).length > 0){
  829. this.unread = true;
  830. return
  831. };
  832. this.applyfriend = {}
  833. const res = await friendRequest({ uuid, status: 1,state:1 });
  834. this.updateFriendApply(res.data || []);
  835. },
  836. // 初始化用户群权限
  837. async funInitGroupAuth(uuid){
  838. },
  839. // 更新用户群权限
  840. updateGroupAuth(data){
  841. Array.isArray(data) && data.forEach(item=>{
  842. if(typeof item === 'string'){
  843. this.chatGroupAuth[item] = 1;
  844. return;
  845. }
  846. this.chatGroupAuth[item.uuid] = 1
  847. });
  848. console.log(this.chatGroupAuth)
  849. },
  850. // 校验会话权限
  851. verifyChatAuth(uuid){
  852. if(this.chatDelAuth[uuid] && !this.chatGroupAuth[uuid]){
  853. return false;
  854. }
  855. return true;
  856. },
  857. //恢复会话权限
  858. funRestoreDelAuth(uuid) {
  859. if (this.chatDelAuth[uuid]) {
  860. delete this.chatDelAuth[uuid]
  861. }
  862. this.chatGroupAuth[uuid] = 1;
  863. },
  864. // 禁用会话权限
  865. funDeleteDelAuth(uuid){
  866. this.chatDelAuth[uuid] = 1;
  867. if(this.chatGroupAuth[uuid]){
  868. delete this.chatGroupAuth[uuid]
  869. }
  870. },
  871. // 更新会话未读状态
  872. funUpdateUnread(uuid) {
  873. const systemStore = useSystemStore();
  874. let index = systemStore.ImsessionList ? systemStore.ImsessionList.findIndex(s => (s.uuid || s.id) === uuid) : -1;
  875. if (index > -1) {
  876. systemStore.ImsessionList[index].unReadNum = 0;
  877. }
  878. },
  879. // 删除会话列表
  880. deleteSessionChat(uuid) {
  881. const systemStore = useSystemStore();
  882. let sessions = systemStore.ImsessionList ? [...systemStore.ImsessionList] : [];
  883. let index = sessions.findIndex(s => (s.uuid || s.id) === uuid);
  884. if (index > -1) {
  885. sessions.splice(index, 1);
  886. systemStore.ImsessionList = sessions;
  887. }
  888. },
  889. // 群成员列表
  890. async fetchGroupMembers(uuid,force = false) {
  891. // if (this.toUserInfo.type !== 'group') {
  892. // return []
  893. // }
  894. // 如果已有缓存且不强制刷新,直接返回
  895. if (!force && this.groupMembersList[uuid]) {
  896. return this.groupMembersList[uuid]
  897. }
  898. try {
  899. const res = await groupList(uuid)
  900. const members = res.data || []
  901. // 更新缓存
  902. this.groupMembersList[uuid] = members
  903. return members
  904. } catch (e) {
  905. console.error('获取群成员失败', e)
  906. return []
  907. }
  908. },
  909. // 清除数据
  910. deleteData(){
  911. const systemStore = useSystemStore();
  912. systemStore.messageList = {};
  913. this.messages = [];
  914. this.groupMembersList = {};
  915. this.toUserInfo = {};
  916. this.toUserAudioInfo = {};
  917. this.messageQueue = [];
  918. systemStore.ImsessionList = [];
  919. this.onMessageCallbacks = [];
  920. this.applyfriend = {};
  921. this.unread = false;
  922. this.chatDelAuth = {};
  923. this.chatGroupAuth = {};
  924. this.chatRefresh = 0;
  925. this.needRefreshIm = 0;
  926. this.indexs = 0;
  927. this.isassign = false;
  928. this.disconnect();
  929. }
  930. },
  931. });