feat: 字典实时同步和首页添加在线用户统计

This commit is contained in:
Ray.Hao
2025-04-22 22:15:15 +08:00
parent cad57b3dc0
commit fdf66164d8
14 changed files with 1275 additions and 70 deletions

View File

@@ -0,0 +1,176 @@
import { ref, onMounted, onUnmounted, watch } from "vue";
import { useStomp } from "./useStomp";
import { ElMessage } from "element-plus";
export interface OnlineUserStats {
type: string; // 事件类型
count: number; // 当前在线用户数量
users?: any[]; // 用户列表(可选)
timestamp: number; // 时间戳
}
/**
* 在线用户WebSocket Hook
* 用于订阅后端推送的在线用户数量变化
*/
export function useWebSocketOnlineUsers() {
// 在线用户数量
const onlineUserCount = ref(0);
// 最后更新时间戳
const lastUpdateTime = ref(0);
// 连接状态
const isConnected = ref(false);
// 连接正在尝试中
const isConnecting = ref(false);
// 使用Stomp客户端
const { connect, subscribe, unsubscribe, disconnect, isConnected: stompConnected } = useStomp();
// 订阅ID
let subscriptionId = "";
// 重连次数
let reconnectCount = 0;
// 最大重连次数
const maxReconnectAttempts = 5;
// 重连计时器
let reconnectTimer: any = null;
// 监听Stomp连接状态
watch(stompConnected, (connected) => {
if (connected && isConnecting.value) {
isConnected.value = true;
isConnecting.value = false;
reconnectCount = 0;
// 一旦连接成功,立即订阅主题
subscribeToOnlineUsers();
console.log("WebSocket连接成功已订阅在线用户主题");
}
});
/**
* 订阅在线用户主题
*/
const subscribeToOnlineUsers = () => {
if (!stompConnected.value) return;
// 如果已经订阅,先取消订阅
if (subscriptionId) {
unsubscribe(subscriptionId);
}
// 订阅在线用户主题
subscriptionId = subscribe("/topic/online-users", (message) => {
try {
const data: OnlineUserStats = JSON.parse(message.body);
// 只有在消息类型为ONLINE_USERS_CHANGE时更新数据
if (data.type === "ONLINE_USERS_CHANGE") {
onlineUserCount.value = data.count || 0;
lastUpdateTime.value = data.timestamp || Date.now();
}
} catch (error) {
console.error("解析在线用户数据失败:", error);
}
});
};
/**
* 初始化WebSocket连接并订阅在线用户主题
*/
const initWebSocket = () => {
if (isConnecting.value) return;
isConnecting.value = true;
// 连接WebSocket
connect();
// 设置连接超时
const connectionTimeout = setTimeout(() => {
if (!isConnected.value) {
console.warn("WebSocket连接超时尝试重连");
attemptReconnect();
}
}, 5000);
// 监听连接状态变化,连接成功后清除超时计时器
const unwatch = watch(stompConnected, (connected) => {
if (connected) {
clearTimeout(connectionTimeout);
unwatch();
}
});
};
/**
* 尝试重新连接
*/
const attemptReconnect = () => {
if (reconnectCount >= maxReconnectAttempts) {
console.error(`已达到最大重连次数(${maxReconnectAttempts}),停止重连`);
isConnecting.value = false;
ElMessage.error("WebSocket连接失败请稍后刷新页面重试");
return;
}
reconnectCount++;
console.log(`尝试重连(${reconnectCount}/${maxReconnectAttempts})...`);
// 使用指数退避策略增加重连间隔
const delay = Math.min(1000 * Math.pow(2, reconnectCount), 30000);
// 清除之前的计时器
if (reconnectTimer) {
clearTimeout(reconnectTimer);
}
// 设置重连计时器
reconnectTimer = setTimeout(() => {
connect();
}, delay);
};
/**
* 关闭WebSocket连接
*/
const closeWebSocket = () => {
if (subscriptionId) {
unsubscribe(subscriptionId);
subscriptionId = "";
}
// 清除重连计时器
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
disconnect();
isConnected.value = false;
isConnecting.value = false;
};
// 组件挂载时初始化WebSocket
onMounted(() => {
initWebSocket();
});
// 组件卸载时关闭WebSocket
onUnmounted(() => {
closeWebSocket();
});
return {
onlineUserCount,
lastUpdateTime,
isConnected,
isConnecting,
initWebSocket,
closeWebSocket,
};
}