feat: websocket功能 (默认关闭)

This commit is contained in:
dap
2025-07-07 19:49:36 +08:00
parent 579105bbff
commit f9132d8097
7 changed files with 118 additions and 25 deletions

View File

@@ -23,3 +23,5 @@ VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
# 开启SSE # 开启SSE
VITE_GLOB_SSE_ENABLE=true VITE_GLOB_SSE_ENABLE=true
# 开启websocket
VITE_GLOB_WEBSOCKET_ENABLE=false

View File

@@ -29,4 +29,6 @@ VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
# 开启SSE # 开启SSE
VITE_GLOB_SSE_ENABLE=true VITE_GLOB_SSE_ENABLE=true
# 开启websocket
VITE_GLOB_WEBSOCKET_ENABLE=false

View File

@@ -32,4 +32,6 @@ VITE_GLOB_APP_CLIENT_ID=e5cd7e4891bf95d1d19206ce24a7b32e
# 开启SSE # 开启SSE
VITE_GLOB_SSE_ENABLE=true VITE_GLOB_SSE_ENABLE=true
# 开启websocket
VITE_GLOB_WEBSOCKET_ENABLE=false

View File

@@ -2,20 +2,15 @@ import type { NotificationItem } from '@vben/layouts';
import { computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { useAppConfig } from '@vben/hooks';
import { SvgMessageUrl } from '@vben/icons'; import { SvgMessageUrl } from '@vben/icons';
import { $t } from '@vben/locales'; 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 { notification } from 'ant-design-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
const { apiURL, clientId, sseEnable } = useAppConfig( import { useSseMessage } from '#/utils/message';
import.meta.env,
import.meta.env.PROD,
);
export const useNotifyStore = defineStore( export const useNotifyStore = defineStore(
'app-notify', 'app-notify',
@@ -40,26 +35,18 @@ export const useNotifyStore = defineStore(
* 开始监听sse消息 * 开始监听sse消息
*/ */
function startListeningMessage() { function startListeningMessage() {
/** // 默认sse 使用 websocket自行开启注释
* 未开启 不监听 // const websocketReturnData = useWebSocketMessage();
*/ // if (!websocketReturnData) {
if (!sseEnable) { // return;
// }
// const { data } = websocketReturnData;
const sseReturnData = useSseMessage();
if (!sseReturnData) {
return; return;
} }
const accessStore = useAccessStore(); const { data } = sseReturnData;
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,
},
});
watch(data, (message) => { watch(data, (message) => {
if (!message) return; if (!message) return;

View File

@@ -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;
}

View File

@@ -22,6 +22,7 @@ export function useAppConfig(
VITE_GLOB_RSA_PRIVATE_KEY, VITE_GLOB_RSA_PRIVATE_KEY,
VITE_GLOB_RSA_PUBLIC_KEY, VITE_GLOB_RSA_PUBLIC_KEY,
VITE_GLOB_SSE_ENABLE, VITE_GLOB_SSE_ENABLE,
VITE_GLOB_WEBSOCKET_ENABLE,
} = config; } = config;
return { return {
@@ -36,5 +37,7 @@ export function useAppConfig(
rsaPublicKey: VITE_GLOB_RSA_PUBLIC_KEY, rsaPublicKey: VITE_GLOB_RSA_PUBLIC_KEY,
// 是否开启sse // 是否开启sse
sseEnable: VITE_GLOB_SSE_ENABLE === 'true', sseEnable: VITE_GLOB_SSE_ENABLE === 'true',
// 是否开启websocket
websocketEnable: VITE_GLOB_WEBSOCKET_ENABLE === 'true',
}; };
} }

View File

@@ -20,6 +20,8 @@ export interface VbenAdminProAppConfigRaw {
VITE_GLOB_RSA_PUBLIC_KEY: string; VITE_GLOB_RSA_PUBLIC_KEY: string;
// 是否开启sse 注意从配置文件获取的类型为string // 是否开启sse 注意从配置文件获取的类型为string
VITE_GLOB_SSE_ENABLE: string; VITE_GLOB_SSE_ENABLE: string;
// 开启websocket 注意从配置文件获取的类型为string
VITE_GLOB_WEBSOCKET_ENABLE: string;
} }
export interface ApplicationConfig { export interface ApplicationConfig {
@@ -35,6 +37,8 @@ export interface ApplicationConfig {
rsaPublicKey: string; rsaPublicKey: string;
// 是否开启sse // 是否开启sse
sseEnable: boolean; sseEnable: boolean;
// 是否开启
websocketEnable: boolean;
} }
declare global { declare global {