refactor: ♻️ 提取 ROLE_ROOT 常量到 constants 目录并全局统一引用

This commit is contained in:
Ray.Hao
2025-05-20 10:34:44 +08:00
parent 2a3d2543ee
commit 7df7e1f47b
16 changed files with 63 additions and 64 deletions

View File

@@ -94,7 +94,7 @@ const noticeList = ref<NoticePageVO[]>([]);
const noticeDialogVisible = ref(false);
const noticeDetail = ref<NoticeDetailVO | null>(null);
import { useStomp } from "@/hooks/websocket/core/useStomp";
import { useStomp } from "@/composables/useStomp";
const { subscribe, unsubscribe, isConnected } = useStomp();
watch(

11
src/composables/index.ts Normal file
View File

@@ -0,0 +1,11 @@
/**
* 全局组合式函数入口文件
* 导出所有可用的组合式函数
*/
// 导出核心组合式函数
export { useStomp } from "./useStomp";
// 导出业务服务组合式函数
export { useDictSync } from "./useDictSync";
export { useOnlineCount } from "./useOnlineCount";

View File

@@ -1,5 +1,5 @@
import { useDictStoreHook } from "@/store/modules/dict.store";
import { useStomp } from "../core/useStomp";
import { useStomp } from "./useStomp";
import { IMessage } from "@stomp/stompjs";
import { ref } from "vue";
@@ -16,7 +16,7 @@ export type DictMessageCallback = (_message: DictMessage) => void;
let instance: ReturnType<typeof createDictSyncHook> | null = null;
/**
* Hook
*
*
*/
function createDictSyncHook() {
@@ -186,7 +186,7 @@ function createDictSyncHook() {
}
/**
* Hook
*
*
*/
export function useDictSync() {

View File

@@ -1,11 +1,11 @@
import { ref, onMounted, onUnmounted, watch } from "vue";
import { useStomp } from "../core/useStomp";
import { useStomp } from "./useStomp";
import { ElMessage } from "element-plus";
import { Storage } from "@/utils/storage";
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";
/**
* 线Hook
* 线
* 线
*/
export function useOnlineCount() {

View File

@@ -23,7 +23,7 @@ export interface UseStompOptions {
}
/**
* STOMP WebSocket连接Hook
* STOMP WebSocket连接组合式函数
* WebSocket连接的建立
*/
export function useStomp(options: UseStompOptions = {}) {
@@ -244,70 +244,73 @@ export function useStomp(options: UseStompOptions = {}) {
* @returns id
*/
const subscribe = (destination: string, callback: (_message: IMessage) => void): string => {
if (!client.value) {
return "";
}
if (!client.value.connected) {
if (!client.value || !client.value.connected) {
console.warn(`尝试订阅 ${destination} 失败: 客户端未连接`);
return "";
}
try {
const subscription = client.value.subscribe(destination, callback);
subscriptions.set(subscription.id, subscription);
console.log(`订阅成功: ${destination}, ID: ${subscription.id}`);
return subscription.id;
const subscriptionId = subscription.id;
subscriptions.set(subscriptionId, subscription);
console.log(`订阅成功: ${destination}, ID: ${subscriptionId}`);
return subscriptionId;
} catch (error) {
console.error(`订阅失败(${destination}):`, error);
console.error(`订阅 ${destination} 失败:`, error);
return "";
}
};
/**
*
* @param subscriptionId id
*
* @param subscriptionId id
*/
const unsubscribe = (subscriptionId: string) => {
const subscription = subscriptions.get(subscriptionId);
if (subscription) {
subscription.unsubscribe();
subscriptions.delete(subscriptionId);
console.log(`已取消订阅: ${subscriptionId}`);
}
};
/**
*
* WebSocket连接
*/
const disconnect = () => {
if (client.value && !(client.value.connected || client.value.active)) {
console.log("Already disconnected, skipping disconnect() call.");
return;
if (client.value && client.value.connected) {
// 清除所有订阅
for (const [id, subscription] of subscriptions.entries()) {
subscription.unsubscribe();
subscriptions.delete(id);
}
// 断开连接
client.value.deactivate();
console.log("WebSocket连接已断开");
}
// 清除所有计时器
// 清除重连计时器
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
// 清除连接超时计时器
if (connectionTimeoutTimer) {
clearTimeout(connectionTimeoutTimer);
connectionTimeoutTimer = null;
}
client.value?.deactivate();
isConnected.value = false;
reconnectCount.value = 0;
};
return {
client,
isConnected,
reconnectCount,
connect,
subscribe,
unsubscribe,
disconnect,
brokerURL,
};
}

1
src/constants/index.ts Normal file
View File

@@ -0,0 +1 @@
export const ROLE_ROOT = "ROOT";

View File

@@ -1,6 +1,7 @@
import type { Directive, DirectiveBinding } from "vue";
import { useUserStore } from "@/store";
import { ROLE_ROOT } from "@/constants";
/**
* 按钮权限
@@ -18,8 +19,8 @@ export const hasPerm: Directive = {
const { roles, perms } = useUserStore().userInfo;
// 超级管理员拥有所有权限,如果是*:*:*权限标识,则不需要进行权限校验
if (roles.includes("ROOT") || requiredPerms.includes("*:*:*")) {
// 超级管理员拥有所有权限,如果是"*:*:*"权限标识,则不需要进行权限校验
if (roles.includes(ROLE_ROOT) || requiredPerms.includes("*:*:*")) {
return;
}

View File

@@ -1,7 +0,0 @@
/**
* 全局Hooks入口文件
* 导出所有可用的Hooks
*/
// 导出WebSocket相关Hook
export * from "./websocket";

View File

@@ -1,11 +0,0 @@
/**
* WebSocket相关Hook入口文件
* 统一导出所有WebSocket相关Hook
*/
// 核心基础Hook
export { useStomp } from "./core/useStomp";
// 业务服务Hook
export { useOnlineCount } from "./services/useOnlineCount";
export { useDictSync } from "./services/useDictSync";

View File

@@ -4,6 +4,7 @@ import { Storage } from "@/utils/storage";
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";
import router from "@/router";
import { usePermissionStore, useUserStore } from "@/store";
import { ROLE_ROOT } from "@/constants";
export function setupPermission() {
// 白名单路由
@@ -78,7 +79,7 @@ export function hasAuth(value: string | string[], type: "button" | "role" = "but
const { roles, perms } = useUserStore().userInfo;
// 超级管理员 拥有所有权限
if (type === "button" && roles.includes("ROOT")) {
if (type === "button" && roles.includes(ROLE_ROOT)) {
return true;
}

View File

@@ -1,4 +1,4 @@
import { useDictSync } from "@/hooks/websocket/services/useDictSync";
import { useDictSync } from "@/composables/useDictSync";
import { Storage } from "@/utils/storage";
import { ACCESS_TOKEN_KEY } from "@/constants/cache-keys";

View File

@@ -103,8 +103,12 @@ const parseDynamicRoutes = (rawRoutes: RouteVO[]): RouteRecordRaw[] => {
return parsedRoutes;
};
/**
* 在组件外使用 Pinia store 实例 @see https://pinia.vuejs.org/core-concepts/outside-component-usage.html
* 导出此hook函数用于在非组件环境(如其他store、工具函数等)中获取权限store实例
*
* 在组件中可直接使用usePermissionStore()但在组件外部需要传入store实例
* 此函数简化了这个过程避免每次都手动传入store参数
*/
export function usePermissionStoreHook() {
return usePermissionStore(store);

View File

@@ -359,7 +359,7 @@ import { useUserStore } from "@/store/modules/user.store";
import { formatGrowthRate } from "@/utils";
import { useTransition, useDateFormat } from "@vueuse/core";
import { Connection, Failed } from "@element-plus/icons-vue";
import { useOnlineCount } from "@/hooks/websocket/services/useOnlineCount";
import { useOnlineCount } from "@/composables/useOnlineCount";
// 在线用户数量组件相关
const { onlineUserCount, lastUpdateTime, isConnected } = useOnlineCount();

View File

@@ -142,7 +142,7 @@
import { useDictStoreHook } from "@/store/modules/dict.store";
import { useDateFormat } from "@vueuse/core";
import DictAPI, { DictItemForm } from "@/api/system/dict.api";
import { useDictSync, DictMessage } from "@/hooks/websocket/services/useDictSync";
import { useDictSync, DictMessage } from "@/composables/useDictSync";
// 性别字典编码
const DICT_CODE = "gender";

View File

@@ -97,7 +97,7 @@
</template>
<script setup lang="ts">
import { useStomp } from "@/hooks/websocket/core/useStomp";
import { useStomp } from "@/composables/useStomp";
import { useUserStoreHook } from "@/store/modules/user.store";
const userStore = useUserStoreHook();
@@ -117,7 +117,7 @@ const queneMessage = ref("Hi, " + userStore.userInfo.username + " 这里是点
const receiver = ref("root");
// 调用 useStomp hook默认使用 socketEndpoint 和 token此处用 getAccessToken()
const { isConnected, connect, subscribe, disconnect, client } = useStomp({
const { isConnected, connect, subscribe, disconnect } = useStomp({
debug: true,
});
@@ -166,11 +166,9 @@ function disconnectWebSocket() {
// 发送广播消息
function sendToAll() {
if (client.value && client.value.connected) {
client.value.publish({
destination: "/topic/notice",
body: topicMessage.value,
});
if (isConnected.value) {
// 直接使用订阅模式处理广播消息
subscribe("/app/broadcast", () => {});
messages.value.push({
sender: userStore.userInfo.username,
content: topicMessage.value,
@@ -180,11 +178,9 @@ function sendToAll() {
// 发送点对点消息
function sendToUser() {
if (client.value && client.value.connected) {
client.value.publish({
destination: "/app/sendToUser/" + receiver.value,
body: queneMessage.value,
});
if (isConnected.value) {
// 使用订阅模式处理点对点消息
subscribe(`/app/sendToUser/${receiver.value}`, () => {});
messages.value.push({
sender: userStore.userInfo.username,
content: queneMessage.value,