Files
leaudit-platform-frontend/app/api/auth/user-routes.ts
T

613 lines
15 KiB
TypeScript

import { toastService } from '~/components/ui';
import { postgrestGet } from '../postgrest-client';
// 路由数据接口
export interface RouteInfo {
id: number;
path: string;
name: string;
meta: {
title: string;
icon: string;
order: number;
requiredRole?: string;
};
parent_id: number;
is_menu: number;
}
// 用户路由权限接口
export interface UserRoutePermission {
route_id: number;
role_id: number;
permission: string;
route: RouteInfo;
}
// MenuItem结构接口
export interface MenuItem {
id: string;
title: string;
path: string;
icon: string;
order: number;
hideBreadcrumb?: boolean;
requiredRole?: string;
children?: MenuItem[];
}
// 静态菜单数据作为后备 (保留用于开发和紧急情况,当前不使用)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const FALLBACK_MENU_DATA: Record<string, MenuItem[]> = {
'admin': [
{
id: 'home',
title: '系统概览',
path: '/home',
icon: 'ri-home-line',
order: 1
},
{
id: 'chat-with-llm',
title: 'AI对话',
path: '/chat-with-llm',
icon: 'ri-chat-smile-2-line',
order: 2
},
{
id: 'file-management',
title: '文件管理',
path: '/files',
icon: 'ri-folder-line',
order: 3,
children: [
{
id: 'file-upload',
title: '文件上传',
path: '/files/upload',
icon: 'ri-upload-cloud-line',
order: 1
},
{
id: 'documents',
title: '文档列表',
path: '/documents',
icon: 'ri-file-list-3-line',
order: 2
}
]
},
{
id: 'rule-management',
title: '评查规则库',
path: '/rules',
icon: 'ri-book-3-line',
order: 4,
children: [
{
id: 'rule-groups',
title: '评查点分组',
path: '/rule-groups',
icon: 'ri-folder-open-line',
order: 1
},
{
id: 'rules-list',
title: '评查点列表',
path: '/rules',
icon: 'ri-list-check-3',
order: 2
},
{
id: 'rules-file',
title: '评查文件列表',
path: '/rules-files',
icon: 'ri-list-check-2',
order: 3
}
]
},
{
id: 'contract-template',
title: '合同模板',
path: '/contract-template',
icon: 'ri-file-search-line',
order: 5,
children: [
{
id: 'contract-search-ai',
title: '智能搜索',
path: '/contract-template/search',
icon: 'ri-search-line',
order: 1
},
{
id: 'contract-list',
title: '合同列表',
path: '/contract-template/list',
icon: 'ri-folder-line',
order: 2
}
]
},
{
id: 'system-settings',
title: '系统设置',
path: '/settings',
icon: 'ri-settings-4-line',
order: 6,
requiredRole: 'developer',
children: [
{
id: 'config-lists',
title: '配置列表',
path: '/config-lists',
icon: 'ri-list-check-3',
order: 1,
requiredRole: 'developer'
},
{
id: 'document-types',
title: '文档类型',
path: '/document-types',
icon: 'ri-file-list-line',
order: 2,
requiredRole: 'developer'
},
{
id: 'prompt-management',
title: '提示词管理',
path: '/prompts',
icon: 'ri-chat-1-line',
order: 3,
requiredRole: 'developer'
}
]
},
{
id: 'cross-checking',
title: '交叉评查',
path: '/cross-checking',
icon: 'ri-color-filter-line',
order: 7
}
],
'common': [
{
id: 'home',
title: '系统概览',
path: '/home',
icon: 'ri-home-line',
order: 1
},
{
id: 'file-management',
title: '文件管理',
path: '/files',
icon: 'ri-folder-line',
order: 3,
children: [
{
id: 'file-upload',
title: '文件上传',
path: '/files/upload',
icon: 'ri-upload-cloud-line',
order: 1
},
{
id: 'documents',
title: '文档列表',
path: '/documents',
icon: 'ri-file-list-3-line',
order: 2
}
]
},
{
id: 'rule-management',
title: '评查规则库',
path: '/rules',
icon: 'ri-book-3-line',
order: 4,
children: [
{
id: 'rule-groups',
title: '评查点分组',
path: '/rule-groups',
icon: 'ri-folder-open-line',
order: 1
},
{
id: 'rules-list',
title: '评查点列表',
path: '/rules',
icon: 'ri-list-check-3',
order: 2
},
{
id: 'rules-file',
title: '评查文件列表',
path: '/rules-files',
icon: 'ri-list-check-2',
order: 3
}
]
},
{
id: 'contract-template',
title: '合同模板',
path: '/contract-template',
icon: 'ri-file-search-line',
order: 5,
children: [
{
id: 'contract-search-ai',
title: '智能搜索',
path: '/contract-template/search',
icon: 'ri-search-line',
order: 1
},
{
id: 'contract-list',
title: '合同列表',
path: '/contract-template/list',
icon: 'ri-folder-line',
order: 2
}
]
},
{
id: 'cross-checking',
title: '交叉评查',
path: '/cross-checking',
icon: 'ri-color-filter-line',
order: 7
}
],
'deptLeader': [
{
id: 'home',
title: '系统概览',
path: '/home',
icon: 'ri-home-line',
order: 1
},
{
id: 'chat-with-llm',
title: 'AI对话',
path: '/chat-with-llm',
icon: 'ri-chat-smile-2-line',
order: 2
},
{
id: 'file-management',
title: '文件管理',
path: '/files',
icon: 'ri-folder-line',
order: 3,
children: [
{
id: 'file-upload',
title: '文件上传',
path: '/files/upload',
icon: 'ri-upload-cloud-line',
order: 1
},
{
id: 'documents',
title: '文档列表',
path: '/documents',
icon: 'ri-file-list-3-line',
order: 2
}
]
},
{
id: 'rule-management',
title: '评查规则库',
path: '/rules',
icon: 'ri-book-3-line',
order: 4,
children: [
{
id: 'rule-groups',
title: '评查点分组',
path: '/rule-groups',
icon: 'ri-folder-open-line',
order: 1
},
{
id: 'rules-list',
title: '评查点列表',
path: '/rules',
icon: 'ri-list-check-3',
order: 2
},
{
id: 'rules-file',
title: '评查文件列表',
path: '/rules-files',
icon: 'ri-list-check-2',
order: 3
}
]
},
{
id: 'contract-template',
title: '合同模板',
path: '/contract-template',
icon: 'ri-file-search-line',
order: 5,
children: [
{
id: 'contract-search-ai',
title: '智能搜索',
path: '/contract-template/search',
icon: 'ri-search-line',
order: 1
},
{
id: 'contract-list',
title: '合同列表',
path: '/contract-template/list',
icon: 'ri-folder-line',
order: 2
}
]
},
{
id: 'cross-checking',
title: '交叉评查',
path: '/cross-checking',
icon: 'ri-color-filter-line',
order: 7
}
],
'groupLeader': [
{
id: 'home',
title: '系统概览',
path: '/home',
icon: 'ri-home-line',
order: 1
},
{
id: 'file-management',
title: '文件管理',
path: '/files',
icon: 'ri-folder-line',
order: 3,
children: [
{
id: 'file-upload',
title: '文件上传',
path: '/files/upload',
icon: 'ri-upload-cloud-line',
order: 1
},
{
id: 'documents',
title: '文档列表',
path: '/documents',
icon: 'ri-file-list-3-line',
order: 2
}
]
},
{
id: 'rule-management',
title: '评查规则库',
path: '/rules',
icon: 'ri-book-3-line',
order: 4,
children: [
{
id: 'rule-groups',
title: '评查点分组',
path: '/rule-groups',
icon: 'ri-folder-open-line',
order: 1
},
{
id: 'rules-list',
title: '评查点列表',
path: '/rules',
icon: 'ri-list-check-3',
order: 2
},
{
id: 'rules-file',
title: '评查文件列表',
path: '/rules-files',
icon: 'ri-list-check-2',
order: 3
}
]
},
{
id: 'contract-template',
title: '合同模板',
path: '/contract-template',
icon: 'ri-file-search-line',
order: 5,
children: [
{
id: 'contract-search-ai',
title: '智能搜索',
path: '/contract-template/search',
icon: 'ri-search-line',
order: 1
},
{
id: 'contract-list',
title: '合同列表',
path: '/contract-template/list',
icon: 'ri-folder-line',
order: 2
}
]
},
{
id: 'cross-checking',
title: '交叉评查',
path: '/cross-checking',
icon: 'ri-color-filter-line',
order: 7
}
]
};
/**
* 根据角色获取用户可访问的路由
* @param roleKey 角色标识 (如: 'admin', 'common', 'deptLeader', 'groupLeader')
* @returns 用户可访问的路由列表
*/
export async function getUserRoutesByRole(roleKey: string): Promise<{ success: boolean; data?: MenuItem[]; error?: string; shouldRedirectToHome?: boolean }> {
try {
console.log(`获取角色 ${roleKey} 的路由权限`);
// 首先获取角色ID
const roleResult = await postgrestGet<Array<{id: number}>>("roles", {
filter: {
"role_key": `eq.${roleKey}`
}
});
if (roleResult.error || !roleResult.data || roleResult.data.length === 0) {
console.error("角色不存在:", roleKey);
toastService.error("角色不存在,请联系管理员配置权限后重新登录");
return { success: false, error: "角色不存在", shouldRedirectToHome: true };
}
const roleId = roleResult.data[0].id;
// 查询角色的路由权限
const roleRoutesResult = await postgrestGet<Array<{route_id: number}>>("role_route", {
filter: {
"role_id": `eq.${roleId}`
}
});
if (roleRoutesResult.error) {
console.error("查询角色路由关联失败:", roleRoutesResult.error);
toastService.error("查询角色路由关联失败,请稍后再试");
return { success: false, error: "查询角色路由关联失败", shouldRedirectToHome: true };
}
const roleRoutes = roleRoutesResult.data || [];
const routeIds = roleRoutes.map(item => item.route_id);
if (routeIds.length === 0) {
console.log(`角色 ${roleKey} 没有分配任何路由权限`);
toastService.error("您的角色没有分配任何路由权限,请联系管理员配置权限");
return { success: false, error: "角色没有分配任何路由权限", shouldRedirectToHome: true };
}
// 查询具体的路由信息
const routesResult = await postgrestGet<RouteInfo[]>("sys_routes", {
filter: {
"id": `in.(${routeIds.join(',')})`,
"is_menu": "eq.1"
},
order: "parent_id,meta->>order"
});
if (routesResult.error) {
console.error("查询路由信息失败:", routesResult.error);
toastService.error("查询路由信息失败,请稍后再试");
return { success: false, error: "查询路由信息失败", shouldRedirectToHome: true };
}
const routes = routesResult.data || [];
// 构建菜单树
const menuItems = buildMenuTreeFromRoutes(routes);
console.log(`角色 ${roleKey} 可访问 ${menuItems.length} 个路由`);
return { success: true, data: menuItems };
} catch (error) {
console.error("获取用户路由时发生错误:", error);
toastService.error("获取用户路由时发生错误,请稍后再试");
return {
success: false,
error: `获取用户路由失败: ${error instanceof Error ? error.message : String(error)}`,
shouldRedirectToHome: true
};
}
}
/**
* 从路由信息构建菜单树结构
* @param routes 路由信息数组
* @returns 菜单树结构
*/
function buildMenuTreeFromRoutes(routes: RouteInfo[]): MenuItem[] {
// 转换为MenuItem格式
const menuMap = new Map<number, MenuItem>();
routes.forEach(route => {
const menuItem: MenuItem = {
id: route.name,
title: route.meta.title,
path: route.path,
icon: route.meta.icon,
order: route.meta.order || 0,
requiredRole: route.meta.requiredRole
};
menuMap.set(route.id, menuItem);
});
// 构建父子关系
const rootItems: MenuItem[] = [];
const itemsWithParent: Array<{ item: MenuItem; parentId: number }> = [];
routes.forEach(route => {
const menuItem = menuMap.get(route.id);
if (!menuItem) return;
if (route.parent_id === 0) {
rootItems.push(menuItem);
} else {
itemsWithParent.push({ item: menuItem, parentId: route.parent_id });
}
});
// 添加子菜单
itemsWithParent.forEach(({ item, parentId }) => {
const parent = menuMap.get(parentId);
if (parent) {
if (!parent.children) {
parent.children = [];
}
parent.children.push(item);
}
});
// 排序
rootItems.sort((a, b) => a.order - b.order);
rootItems.forEach(item => {
if (item.children) {
item.children.sort((a, b) => a.order - b.order);
}
});
return rootItems;
}
/**
* 根据用户角色映射到权限系统的角色标识
* @param userRole 前端用户角色 ('common' | 'developer')
* @returns 数据库中的角色标识
*/
export function mapUserRoleToRoleKey(userRole: string): string {
const roleMapping: Record<string, string> = {
'common': 'common',
'developer': 'admin',
'deptLeader': 'deptLeader',
'groupLeader': 'groupLeader'
};
return roleMapping[userRole] || 'common';
}