diff --git a/apps/web-antd/.env.development b/apps/web-antd/.env.development index dcb7fbc0..944d0ff8 100644 --- a/apps/web-antd/.env.development +++ b/apps/web-antd/.env.development @@ -23,3 +23,5 @@ VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e # 开启SSE VITE_GLOB_SSE_ENABLE=true +# 开启websocket +VITE_GLOB_WEBSOCKET_ENABLE=false diff --git a/apps/web-antd/.env.production b/apps/web-antd/.env.production index 0c309145..f7c9e8f2 100644 --- a/apps/web-antd/.env.production +++ b/apps/web-antd/.env.production @@ -29,4 +29,6 @@ VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e # 开启SSE VITE_GLOB_SSE_ENABLE=true +# 开启websocket +VITE_GLOB_WEBSOCKET_ENABLE=false diff --git a/apps/web-antd/.env.test b/apps/web-antd/.env.test index f66a206f..63bbb581 100644 --- a/apps/web-antd/.env.test +++ b/apps/web-antd/.env.test @@ -32,4 +32,6 @@ VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e # 开启SSE VITE_GLOB_SSE_ENABLE=true +# 开启websocket +VITE_GLOB_WEBSOCKET_ENABLE=false diff --git a/apps/web-antd/src/store/notify.ts b/apps/web-antd/src/store/notify.ts index 4fd0a951..66db5c16 100644 --- a/apps/web-antd/src/store/notify.ts +++ b/apps/web-antd/src/store/notify.ts @@ -2,20 +2,15 @@ import type { NotificationItem } from '@vben/layouts'; import { computed, ref, watch } from 'vue'; -import { useAppConfig } from '@vben/hooks'; import { SvgMessageUrl } from '@vben/icons'; import { $t } from '@vben/locales'; -import { useAccessStore, useUserStore } from '@vben/stores'; +import { useUserStore } from '@vben/stores'; -import { useEventSource } from '@vueuse/core'; import { notification } from 'ant-design-vue'; import dayjs from 'dayjs'; import { defineStore } from 'pinia'; -const { apiURL, clientId, sseEnable } = useAppConfig( - import.meta.env, - import.meta.env.PROD, -); +import { useSseMessage } from '#/utils/message'; export const useNotifyStore = defineStore( 'app-notify', @@ -40,26 +35,18 @@ export const useNotifyStore = defineStore( * 开始监听sse消息 */ function startListeningMessage() { - /** - * 未开启 不监听 - */ - if (!sseEnable) { + // 默认sse 使用 websocket自行开启注释 + // const websocketReturnData = useWebSocketMessage(); + // if (!websocketReturnData) { + // return; + // } + // const { data } = websocketReturnData; + + const sseReturnData = useSseMessage(); + if (!sseReturnData) { return; } - const accessStore = useAccessStore(); - const token = accessStore.accessToken; - - const sseAddr = `${apiURL}/resource/sse?clientid=${clientId}&Authorization=Bearer ${token}`; - - const { data } = useEventSource(sseAddr, [], { - autoReconnect: { - delay: 1000, - onFailed() { - console.error('sse重连失败.'); - }, - retries: 3, - }, - }); + const { data } = sseReturnData; watch(data, (message) => { if (!message) return; diff --git a/apps/web-antd/src/utils/message.ts b/apps/web-antd/src/utils/message.ts new file mode 100644 index 00000000..114e63f8 --- /dev/null +++ b/apps/web-antd/src/utils/message.ts @@ -0,0 +1,93 @@ +import { useAppConfig } from '@vben/hooks'; +import { useAccessStore } from '@vben/stores'; + +import { useEventSource, useWebSocket } from '@vueuse/core'; + +const { apiURL, clientId, sseEnable, websocketEnable } = useAppConfig( + import.meta.env, + import.meta.env.PROD, +); + +export function useSseMessage() { + /** + * 未开启 不监听 + */ + if (!sseEnable) { + console.warn('当前未开启sse.'); + return; + } + const accessStore = useAccessStore(); + const token = accessStore.accessToken; + + const sseAddr = `${apiURL}/resource/sse?clientid=${clientId}&Authorization=Bearer ${token}`; + + const sseReturnData = useEventSource(sseAddr, [], { + autoReconnect: { + delay: 1000, + onFailed() { + console.error('sse重连失败.'); + }, + retries: 3, + }, + }); + + return sseReturnData; +} + +function isUrl(path?: string) { + return /^https?:\/\//.test(path || ''); +} + +export function useWebSocketMessage() { + if (!websocketEnable) { + console.warn('当前未开启websocket.'); + return; + } + let apiUrlStr = String(apiURL); + /** + * 这里可能有两种情况 兼容dev模式的proxy或者prod模式但是没有用全路径比如http://xxx/xxx + * 1. apiUrl为https://xxx.com/xxx + * 2. apiUrl为/xxx + * 转换后为http链接形式 + */ + if (!isUrl(apiURL)) { + // 协议+域名 + apiUrlStr = `${window.location.protocol}//${window.location.host}${apiURL}`; + } + const accessStore = useAccessStore(); + const token = accessStore.accessToken; + // 这里是http链接形式 + let websocketAddr = `${apiUrlStr}/resource/websocket?clientid=${clientId}&Authorization=Bearer ${token}`; + // http/https处理 + websocketAddr = window.location.protocol.includes('https') + ? websocketAddr.replace('https://', 'wss://') + : websocketAddr.replace('http://', 'ws://'); + // console.log('websocketUrl: ' + websocketAddr); + + const websocketResponse = useWebSocket(websocketAddr, { + autoReconnect: { + // 重连最大次数 + retries: 3, + // 重连间隔 + delay: 1000, + onFailed() { + console.error('websocket重连失败.'); + }, + }, + heartbeat: { + message: JSON.stringify({ type: 'ping' }), + // 发送心跳的间隔 + interval: 10_000, + // 接收到心跳response的超时时间 + pongTimeout: 2000, + }, + onConnected() { + console.info('websocket已经连接'); + }, + onDisconnected() { + console.warn('websocket已经断开'); + }, + }); + + return websocketResponse; +} diff --git a/packages/effects/hooks/src/use-app-config.ts b/packages/effects/hooks/src/use-app-config.ts index 6cf497ec..b7de2052 100644 --- a/packages/effects/hooks/src/use-app-config.ts +++ b/packages/effects/hooks/src/use-app-config.ts @@ -22,6 +22,7 @@ export function useAppConfig( VITE_GLOB_RSA_PRIVATE_KEY, VITE_GLOB_RSA_PUBLIC_KEY, VITE_GLOB_SSE_ENABLE, + VITE_GLOB_WEBSOCKET_ENABLE, } = config; return { @@ -36,5 +37,7 @@ export function useAppConfig( rsaPublicKey: VITE_GLOB_RSA_PUBLIC_KEY, // 是否开启sse sseEnable: VITE_GLOB_SSE_ENABLE === 'true', + // 是否开启websocket + websocketEnable: VITE_GLOB_WEBSOCKET_ENABLE === 'true', }; } diff --git a/packages/types/global.d.ts b/packages/types/global.d.ts index 5cf2d382..50405bb5 100644 --- a/packages/types/global.d.ts +++ b/packages/types/global.d.ts @@ -20,6 +20,8 @@ export interface VbenAdminProAppConfigRaw { VITE_GLOB_RSA_PUBLIC_KEY: string; // 是否开启sse 注意从配置文件获取的类型为string VITE_GLOB_SSE_ENABLE: string; + // 开启websocket 注意从配置文件获取的类型为string + VITE_GLOB_WEBSOCKET_ENABLE: string; } export interface ApplicationConfig { @@ -35,6 +37,8 @@ export interface ApplicationConfig { rsaPublicKey: string; // 是否开启sse sseEnable: boolean; + // 是否开启 + websocketEnable: boolean; } declare global {