Files
leaudit-platform-frontend/app/api/role-permissions/role-permissions.ts
T
LiangShiyong d4000cd292 fix: 1. 继续对齐交叉评查的接口,完善创建交叉评查的逻辑 和 相关组件的渲染布局。
2. 文档的基本信息修改改用接口。      3. 重新完善角色权限管理的页面逻辑。     4.将评查点列表中的返回逻辑改用浏览器的记忆返回。
2025-12-12 12:00:36 +08:00

1311 lines
39 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 角色权限管理 API
* 用于角色、路由权限、用户角色的管理
*/
import { get, post, put, del } from '~/api/axios-client';
// ==================== 类型定义 ====================
/**
* API 响应类型
*/
interface ApiResponse<T> {
data?: T;
error?: string;
status?: number;
headers?: Record<string, string>;
}
// ==================== 常量定义 ====================
/**
* RBAC API 基础路径
* 注意:使用相对路径,会命中Remix API路由而不是后端服务器
*/
const RBAC_API_BASE = '/api/v3/rbac';
/**
* 统一响应处理函数
* 处理后端返回的统一格式响应
*/
function handleApiResponse<T>(response: ApiResponse<any>): T {
if (response.error) {
throw new Error(response.error);
}
if (response.data && 'code' in response.data) {
if (response.data.code !== 200) {
throw new Error(response.data.message || '请求失败');
}
return response.data.data as T;
}
// 如果没有code字段,直接返回data
return response.data as T;
}
// ==================== 类型定义 ====================
/**
* API权限信息(关联到路由的权限)
* v3.0新增:每个路由可以关联多个API操作权限
* v3.6新增:支持通用权限(related_routes 字段)
* v3.7新增:is_shared 字段由后端直接返回
*/
export interface ApiPermission {
id: number;
permission_key: string; // 权限标识,如 "evaluation_group:create:write"
display_name: string; // 显示名称,如 "创建评查点分组"
api_method: string; // HTTP方法:GET | POST | PUT | DELETE
api_path: string; // API路径,如 "/api/v3/evaluation-point-groups"
route_id?: number | null; // v3.6: 关联的路由ID(独立权限使用)
related_routes?: number[] | null; // v3.6: 关联的多个路由ID(通用权限使用)
is_shared?: boolean; // v3.7: 是否为通用权限(后端返回)
}
/**
* 路由信息
*/
export interface RouteInfo {
id: number;
route_path: string;
route_name: string;
route_title: string;
component?: string;
parent_id?: number | null;
icon?: string;
sort_order: number;
is_hidden: boolean;
is_cache: boolean;
status: number;
permissions?: ApiPermission[]; // v3.0新增:关联的API权限列表
children?: RouteInfo[];
}
/**
* 角色信息
*/
export interface RoleInfo {
id: number;
role_key: string;
role_name: string;
data_scope: string;
description: string;
parent_role_id?: number | null;
priority: number;
is_system_role: boolean;
created_at: string;
updated_at: string;
}
/**
* 角色-路由权限关联
*/
export interface RoleRoutePermission {
id: number;
role_id: number;
route_id: number;
permission: string; // 'R' | 'RW' | 'NONE'
created_at: string;
}
/**
* 用户信息
*/
export interface UserInfo {
id: number;
username: string;
nick_name: string;
phone_number?: string;
email?: string;
area: string; // v3.3: 地区字段,用于权限隔离(省/市级别)
ou_name: string; // 部门名称,用于组织显示(部门级别)
status: number;
is_leader: boolean;
}
/**
* 用户-角色关联
*/
export interface UserRoleRelation {
id: number;
user_id: number;
role_id: number;
created_at: string;
}
/**
* RBAC权限信息
*/
export interface Permission {
id: number;
permission_key: string; // 格式: module:resource:action
module: string; // 模块名
resource: string; // 资源名
action: string; // 操作名
display_name: string; // 显示名称
description: string | null;
permission_type: 'API' | 'MENU' | 'BUTTON';
is_system: boolean;
parent_id: number | null;
sort_order: number;
children?: Permission[]; // 树形结构
}
/**
* 角色权限配置
*/
export interface RolePermissionConfig {
permission_id: number;
grant_type?: 'GRANT' | 'DENY';
data_scope?: 'ALL' | 'DEPT' | 'SELF';
}
/**
* 角色权限详情
*/
export interface RolePermissionDetail {
id: number;
permission_id: number;
permission_key: string;
display_name: string;
grant_type: 'GRANT' | 'DENY';
data_scope: 'ALL' | 'DEPT' | 'SELF';
}
/**
* 获取所有角色列表
* @param params 查询参数
*/
export async function getRoles(params?: {
page?: number;
page_size?: number;
role_key?: string;
role_name?: string;
include_system?: boolean;
}): Promise<RoleInfo[]> {
try {
// 使用 axios-client 的 get 函数调用真实后端API
const response = await get<any>(`/api/v3/rbac/roles`, params || {});
if (response.error) {
throw new Error(response.error);
}
// 后端响应格式: { code: 200, message: "success", data: { total, page, page_size, items: [...] } }
let items: any[] = [];
if (response.data && response.data.data && Array.isArray(response.data.data.items)) {
items = response.data.data.items;
} else if (response.data && Array.isArray(response.data.items)) {
items = response.data.items;
}
// 数据格式转换(后端字段 -> 前端字段)
const roles = items.map(role => ({
id: role.id,
role_key: role.role_key,
role_name: role.role_name,
data_scope: role.data_scope,
description: role.description || '',
parent_role_id: role.parent_role_id || null,
priority: role.priority || 0,
is_system_role: (role.role_key == 'admin' || role.role_key == 'common') ? true : role.is_system || false,
created_at: role.created_at,
updated_at: role.updated_at
}));
return roles;
} catch (error) {
console.error('❌ [getRoles] 获取角色列表失败:', error);
// 失败时返回空数组
return [];
}
}
/**
* 获取角色详情
* @param roleId 角色ID
*/
export async function getRoleDetail(roleId: number): Promise<RoleInfo | null> {
try {
const response = await get<any>(`${RBAC_API_BASE}/roles/${roleId}`);
const role = handleApiResponse<any>(response);
return {
id: role.id,
role_key: role.role_key,
role_name: role.role_name,
data_scope: role.data_scope,
description: role.description || '',
parent_role_id: role.parent_role_id || null,
priority: role.priority || 0,
is_system_role: role.is_system,
created_at: role.created_at,
updated_at: role.updated_at
};
} catch (error) {
console.error('获取角色详情失败:', error);
return null;
}
}
/**
* 获取所有路由(树形结构)
* 从后端API获取当前用户可访问的所有路由
*/
export async function getRoutes(): Promise<RouteInfo[]> {
try {
// 调用后端API获取当前用户的路由(provincial_admin应该有所有路由权限)
const response = await get<any>('/rbac/user/routes');
if (response.error) {
console.error('❌ [getRoutes] API调用失败:', response.error);
throw new Error(response.error);
}
// 后端响应格式: { code: 200, msg: "success", data: { user_id, username, routes: [...], routes_flat: [...] } }
let routes: any[] = [];
if (response.data && response.data.data && Array.isArray(response.data.data.routes)) {
routes = response.data.data.routes;
} else if (response.data && Array.isArray(response.data.routes)) {
// 兼容可能的响应格式
routes = response.data.routes;
}
// 将后端数据转换为前端RouteInfo格式
const mapRouteData = (route: any): RouteInfo => ({
id: route.id,
route_path: route.route_path,
route_name: route.route_name,
route_title: route.route_title,
icon: route.icon || '',
sort_order: route.sort_order || 0,
is_hidden: route.is_hidden || false,
is_cache: route.is_cache !== false, // 默认true
status: route.status || 1,
parent_id: route.parent_id || null,
component: route.component,
children: route.children ? route.children.map(mapRouteData) : undefined
});
return routes.map(mapRouteData);
} catch (error) {
console.error('❌ [getRoutes] 获取路由数据失败:', error);
// 失败时返回空数组,让前端显示错误提示
return [];
}
}
/**
* v3.7: 获取所有路由列表(管理员接口)
* 使用 /api/v3/routes 接口获取所有路由
* @param format 返回格式(tree/flat
* @param includeHidden 是否包含隐藏路由
*/
export async function getAllRoutes(
format: 'tree' | 'flat' = 'tree',
includeHidden = false
): Promise<RouteInfo[]> {
try {
const response = await get<any>('/api/v3/routes', {
format,
include_hidden: includeHidden
});
if (response.error) {
console.error('❌ [getAllRoutes] API调用失败:', response.error);
throw new Error(response.error);
}
// 后端响应格式: 直接返回路由数组(树形或平铺)
let routes: any[] = [];
if (response.data) {
// 如果是包装格式 { code, message, data: [...] }
if (response.data.code === 200 && Array.isArray(response.data.data)) {
routes = response.data.data;
}
// 如果直接是数组
else if (Array.isArray(response.data)) {
routes = response.data;
}
}
// 将后端数据转换为前端RouteInfo格式
const mapRouteData = (route: any): RouteInfo => ({
id: route.id,
route_path: route.route_path,
route_name: route.route_name,
route_title: route.route_title,
icon: route.icon || '',
sort_order: route.sort_order || 0,
is_hidden: route.is_hidden || false,
is_cache: route.is_cache !== false,
status: route.status || 1,
parent_id: route.parent_id || null,
component: route.component,
children: route.children ? route.children.map(mapRouteData) : undefined
});
return routes.map(mapRouteData);
} catch (error) {
console.error('❌ [getAllRoutes] 获取路由数据失败:', error);
return [];
}
}
/**
* 获取指定角色的路由权限
* @param roleId 角色ID
*/
export async function getRoleRoutePermissions(roleId: number): Promise<RoleRoutePermission[]> {
try {
// 使用 axios-client 的 get 函数调用真实后端API
const response = await get<any>(`/rbac/roles/${roleId}/routes`);
if (response.error) {
throw new Error(response.error);
}
// 后端响应格式: { code: 200, msg: "success", data: { role_id, routes: [...] } }
let routes: any[] = [];
if (response.data && response.data.data && Array.isArray(response.data.data.routes)) {
routes = response.data.data.routes;
}
// 将路由数据转换为RoleRoutePermission格式
const permissions = routes.map((route, index) => ({
id: index + 1,
role_id: roleId,
route_id: route.id,
permission: 'RW', // 默认读写权限
created_at: new Date().toISOString()
}));
console.log("路由权限数据", permissions)
return permissions;
} catch (error) {
console.error('❌ [getRoleRoutePermissions] 获取角色路由权限失败:', error);
// 失败时返回空数组,避免页面崩溃
return [];
}
}
/**
* 获取角色的路由权限(含API权限)- v3.0新增
* 返回树形结构的路由,每个路由包含关联的API权限
* @param roleId 角色ID
*/
export async function getRoleRoutesWithPermissions(roleId: number): Promise<{
routes: RouteInfo[];
selectedRouteIds: number[];
selectedPermissionIds: number[];
}> {
try {
const response = await get<any>(`/rbac/roles/${roleId}/routes`);
if (response.error) {
throw new Error(response.error);
}
// 后端响应格式: { code: 200, msg: "success", data: { role_id, routes: [...] } }
let routes: any[] = [];
if (response.data && response.data.data && Array.isArray(response.data.data.routes)) {
routes = response.data.data.routes;
} else if (response.data && Array.isArray(response.data.routes)) {
routes = response.data.routes;
}
// 递归转换路由数据格式
const mapRouteData = (route: any): any => ({
id: route.id,
route_path: route.route_path,
route_name: route.route_name,
route_title: route.route_title,
icon: route.icon || '',
sort_order: route.sort_order || 0,
is_hidden: route.is_hidden || false,
is_cache: route.is_cache !== false,
status: route.status || 1,
parent_id: route.parent_id || null,
component: route.component,
// v3.2: 添加 enabled 字段
enabled: route.enabled !== undefined ? route.enabled : true,
// v3.0: 转换permissions数组
permissions: Array.isArray(route.permissions) ? route.permissions.map((p: any) => ({
id: p.id,
permission_key: p.permission_key,
display_name: p.display_name,
api_method: p.api_method,
api_path: p.api_path
})) : [],
children: route.children ? route.children.map(mapRouteData) : undefined
});
const mappedRoutes = routes.map(mapRouteData);
// v3.2: 收集已启用的路由IDenabled=true
const collectRouteIds = (routes: any[]): number[] => {
let ids: number[] = [];
routes.forEach(route => {
// v3.2: 只收集 enabled=true 的路由
if (route.enabled) {
ids.push(route.id);
}
if (route.children) {
ids = ids.concat(collectRouteIds(route.children));
}
});
return ids;
};
// v3.5: 修复BUG - 删除了 collectPermissionIds 函数
// BUG说明:从路由元数据收集权限ID是错误的,因为路由的permissions数组包含的仅仅是权限定义,
// 而不是实际授权状态。实际授权状态应该从 `/api/v3/rbac/role-permissions` 接口获取。
//
// 之前的错误实现:
// const collectPermissionIds = (routes: RouteInfo[]): number[] => {
// let ids: number[] = [];
// routes.forEach(route => {
// if (route.permissions) {
// ids = ids.concat(route.permissions.map(p => p.id));
// }
// });
// return ids;
// };
//
// 修复方案:返回空数组,由调用方使用 getRolePermissions() 获取实际授权数据
const selectedRouteIds = collectRouteIds(mappedRoutes);
const selectedPermissionIds: number[] = []; // v3.5: 修复BUG - 返回空数组
return {
routes: mappedRoutes,
selectedRouteIds,
selectedPermissionIds
};
} catch (error) {
console.error('❌ [getRoleRoutesWithPermissions] 获取角色路由权限失败:', error);
return {
routes: [],
selectedRouteIds: [],
selectedPermissionIds: []
};
}
}
/**
* 保存角色的API权限 - v3.0新增
* @param roleId 角色ID
* @param permissionIds 权限ID数组
*/
export async function saveRoleApiPermissions(
roleId: number,
permissionIds: number[]
): Promise<{ success: boolean; message: string }> {
try {
// 构建权限配置
const permissions = permissionIds.map(id => ({
permission_id: id,
grant_type: 'GRANT',
data_scope: 'ALL'
}));
// v3.4: 使用文档规范的API路径(查询参数方式)
const response = await post<any>('/api/v3/rbac/role-permissions', {
role_id: roleId,
permissions,
replace: true // 替换模式:先删除现有权限,再插入新权限
});
if (response.error) {
throw new Error(response.error);
}
let message = 'API权限保存成功';
if (response.data && response.data.message) {
message = response.data.message;
} else if (response.data && response.data.data) {
const { assigned_count } = response.data.data;
message = `成功分配 ${assigned_count} 个API权限`;
}
return { success: true, message };
} catch (error) {
console.error('❌ [saveRoleApiPermissions] 保存API权限失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '保存API权限失败'
};
}
}
/**
* 更新角色的路由权限 - v3.2更新
* @param roleId 角色ID
* @param routeIds 路由ID数组
*/
export async function updateRoleRoutePermissions(
roleId: number,
routeIds: number[]
): Promise<{ success: boolean; message: string; code?: number }> {
try {
// 使用 axios-client 的 put 函数调用真实后端API
const response = await put<any>(`/rbac/roles/${roleId}/routes`, {
route_ids: routeIds,
permission: 'RW'
});
if (response.error) {
throw new Error(response.error);
}
// v3.3: 处理权限不足错误
if (response.data && response.data.code === 4003) {
return {
success: false,
message: response.data.msg || '权限不足:仅省级管理员可以修改角色路由权限',
code: 4003
};
}
// v3.2: 新响应格式: { code: 200, msg: "success", data: { role_id, enabled_count, disabled_count, inserted_count, route_ids } }
let message = '角色权限更新成功';
if (response.data && response.data.msg) {
message = response.data.msg;
} else if (response.data && response.data.data) {
const { enabled_count, disabled_count, inserted_count } = response.data.data;
if (enabled_count !== undefined && disabled_count !== undefined) {
message = `成功启用 ${enabled_count} 个路由,禁用 ${disabled_count} 个路由`;
if (inserted_count && inserted_count > 0) {
message += `,新增 ${inserted_count} 个路由关联`;
}
} else {
// 兼容旧版本响应格式
const { assigned_count, removed_count } = response.data.data;
if (assigned_count !== undefined && removed_count !== undefined) {
message = `成功分配 ${assigned_count} 个路由,移除了 ${removed_count} 个旧路由`;
}
}
}
return { success: true, message };
} catch (error) {
console.error('❌ [updateRoleRoutePermissions] 更新角色权限失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '更新角色权限失败'
};
}
}
// ==================== 用户角色管理 API ====================
/**
* 获取指定角色的用户列表
* @param roleId 角色ID
* @param params 查询参数
*/
export async function getRoleUsers(
roleId: number,
params?: {
page?: number;
page_size?: number;
area?: string;
username?: string;
}
): Promise<UserInfo[]> {
try {
// 导入 axios-client 的 get 函数
// 构建查询参数对象
const queryParams: Record<string, any> = {};
if (params?.page) queryParams.page = params.page;
if (params?.page_size) queryParams.page_size = params.page_size;
if (params?.area) queryParams.area = params.area;
if (params?.username) queryParams.username = params.username;
// 使用 axios-client 的 get 函数调用真实后端API
const response = await get<any>(`/api/v3/rbac/roles/${roleId}/users`, queryParams);
if (response.error) {
throw new Error(response.error);
}
// 后端响应格式: { code: 200, message: "success", data: { total, page, page_size, items: [...] } }
let items: any[] = [];
if (response.data && response.data.data && Array.isArray(response.data.data.items)) {
items = response.data.data.items;
} else if (response.data && Array.isArray(response.data.items)) {
items = response.data.items;
}
const users = items.map((user: any) => ({
id: user.user_id || user.id,
username: user.username,
nick_name: user.nick_name,
phone_number: user.phone_number || '',
email: user.email || '',
area: user.area || '', // v3.3: 地区字段,用于权限隔离
ou_name: user.ou_name,
status: user.status || 1,
is_leader: user.is_leader || false
}));
return users;
} catch (error) {
console.error('❌ [getRoleUsers] 获取角色用户列表失败:', error);
return [];
}
}
/**
* 获取所有用户列表
* @param params 查询参数
*/
export async function getAllUsers(params?: {
page?: number;
page_size?: number;
area?: string;
username?: string;
}): Promise<UserInfo[]> {
try {
// 导入 axios-client 的 get 函数
// 构建查询参数对象
const queryParams: Record<string, any> = {};
if (params?.page) queryParams.page = params.page;
if (params?.page_size) queryParams.page_size = params.page_size;
if (params?.username) queryParams.search = params.username;
// v3.3: 使用标准 RBAC API,后端会自动根据用户角色进行地区过滤
// 省级管理员: 返回所有地区用户
// 市级管理员: 只返回同地区用户
const response = await get<any>('/api/v2/users', queryParams);
if (response.error) {
throw new Error(response.error);
}
// axios-client 返回格式: { data: { users: [...], total: number }, status: 200 }
// 后端实际返回: { users: [...], total: number }
let users: any[] = [];
if (response.data) {
// 如果 response.data 是对象且包含 users 字段
if (response.data.users && Array.isArray(response.data.users)) {
users = response.data.users;
}
// 如果 response.data 本身就是数组
else if (Array.isArray(response.data)) {
users = response.data;
}
}
console.log('获取的用户列表', users )
const userList = users.map(user => ({
id: user.user_id || user.id, // 优先使用 user_id,兼容不同的后端响应格式
username: user.username,
nick_name: user.nick_name,
phone_number: user.phone_number || '',
email: user.email || '',
area: user.area || '', // v3.3: 地区字段,用于权限隔离
ou_name: user.ou_name,
status: user.status || 1,
is_leader: user.is_leader || false
}));
return userList;
} catch (error) {
console.error('❌ [getAllUsers] 获取用户列表失败:', error);
return [];
}
}
/**
* 为用户分配角色
* @param userId 用户ID
* @param roleIds 角色ID数组
*/
export async function assignUserRoles(
userId: number,
roleIds: number[]
): Promise<{ success: boolean; message: string }> {
try {
// 导入 axios-client 的 post 函数
// 使用 axios-client 的 post 函数调用真实后端API
const response = await post<any>(`/api/v3/rbac/users/${userId}/roles`, {
role_ids: roleIds
});
if (response.error) {
throw new Error(response.error);
}
// v3.4: 检查后端返回的code200表示成功)
if (response.data && response.data.code && response.data.code !== 200) {
// 后端返回错误,如权限不足(403)等
return {
success: false,
message: response.data.message || '分配失败'
};
}
// 后端响应格式: { code: 200, message: "角色分配成功", data: { user_id, roles: [...] } }
let message = '用户角色分配成功';
if (response.data && response.data.message) {
message = response.data.message;
}
return { success: true, message };
} catch (error) {
console.error('❌ [assignUserRoles] 分配用户角色失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '分配失败'
};
}
}
/**
* 移除用户角色
* @param userId 用户ID
* @param roleId 角色ID
*/
export async function revokeUserRole(
userId: number,
roleId: number
): Promise<{ success: boolean; message: string }> {
try {
// 导入 axios-client 的 del 函数
// 使用 axios-client 的 del 函数调用真实后端API
const response = await del<any>(`/api/v3/rbac/users/${userId}/roles/${roleId}`);
if (response.error) {
throw new Error(response.error);
}
// 后端响应格式: { code: 200, message: "角色移除成功" }
let message = '用户角色移除成功';
if (response.data && response.data.message) {
message = response.data.message;
}
return { success: true, message };
} catch (error) {
console.error('❌ [revokeUserRole] 移除用户角色失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '移除失败'
};
}
}
/**
* 创建新角色
* @param roleData 角色数据
*/
export async function createRole(
roleData: Omit<RoleInfo, 'id' | 'created_at' | 'updated_at'>
): Promise<{ success: boolean; message: string; data?: RoleInfo }> {
try {
// 导入 axios-client 的 post 函数
// 使用 axios-client 的 post 函数调用真实后端API
const response = await post<any>(`/api/v3/rbac/roles`, {
role_key: roleData.role_key,
role_name: roleData.role_name,
description: roleData.description || '',
data_scope: roleData.data_scope || 'SELF',
metadata: {}
});
if (response.error) {
throw new Error(response.error);
}
// 后端响应格式: { code: 200, message: "角色创建成功", data: { id, role_key, ... } }
const data = response.data?.data || response.data;
return {
success: true,
message: response.data?.message || '角色创建成功',
data: {
id: data.id,
role_key: data.role_key,
role_name: data.role_name,
data_scope: data.data_scope,
description: data.description || '',
parent_role_id: data.parent_role_id || null,
priority: data.priority || 0,
is_system_role: data.is_system || false,
created_at: data.created_at,
updated_at: data.updated_at
}
};
} catch (error) {
console.error('❌ [createRole] 创建角色失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '创建角色失败'
};
}
}
/**
* 更新角色信息
* @param roleId 角色ID
* @param roleData 角色数据
*/
export async function updateRole(
roleId: number,
roleData: Partial<Omit<RoleInfo, 'id' | 'created_at' | 'updated_at'>>
): Promise<{ success: boolean; message: string }> {
try {
// 导入 axios-client 的 put 函数
const updatePayload: any = {};
if (roleData.role_name !== undefined) updatePayload.role_name = roleData.role_name;
if (roleData.description !== undefined) updatePayload.description = roleData.description;
if (roleData.data_scope !== undefined) updatePayload.data_scope = roleData.data_scope;
if (roleData.priority !== undefined) updatePayload.priority = roleData.priority;
if (roleData.parent_role_id !== undefined) updatePayload.parent_role_id = roleData.parent_role_id;
const response = await put<any>(`/api/v3/rbac/roles/${roleId}`, updatePayload);
if (response.error) {
throw new Error(response.error);
}
return { success: true, message: response.data?.message || '角色更新成功' };
} catch (error) {
console.error('❌ [updateRole] 更新角色失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '更新角色失败'
};
}
}
/**
* 删除角色
* @param roleId 角色ID
* @param force 是否强制删除(会自动解除用户关联)
*/
export async function deleteRole(
roleId: number,
force = false
): Promise<{ success: boolean; message: string }> {
try {
// 导入 axios-client 的 del 函数
const url = `/api/v3/rbac/roles/${roleId}${force ? '?force=true' : ''}`;
const response = await del<any>(url);
if (response.error) {
throw new Error(response.error);
}
return { success: true, message: response.data?.message || '角色删除成功' };
} catch (error) {
console.error('❌ [deleteRole] 删除角色失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '删除角色失败'
};
}
}
// ==================== 权限管理 API ====================
/**
* 获取权限列表(树形或平铺)
* @param format 格式:tree(树形)或 flat(平铺)
* @param params 查询参数
*/
export async function getPermissions(
format: 'tree' | 'flat' = 'tree',
params?: {
module?: string;
permission_type?: 'API' | 'MENU' | 'BUTTON';
include_system?: boolean;
}
): Promise<Permission[]> {
try {
const response = await get<any>(`${RBAC_API_BASE}/permissions`, {
format,
...params
});
const data = handleApiResponse<Permission[]>(response);
return data;
} catch (error) {
console.error('获取权限列表失败:', error);
return [];
}
}
/**
* 获取权限详情
* @param permissionId 权限ID
*/
export async function getPermissionDetail(permissionId: number): Promise<Permission | null> {
try {
const response = await get<any>(`${RBAC_API_BASE}/permissions/${permissionId}`);
const data = handleApiResponse<Permission>(response);
return data;
} catch (error) {
console.error('获取权限详情失败:', error);
return null;
}
}
/**
* 创建权限
* @param permissionData 权限数据
*/
export async function createPermission(
permissionData: Omit<Permission, 'id' | 'children'>
): Promise<{ success: boolean; message: string; data?: Permission }> {
try {
// 从 permission_key 解析 module, resource, action
const [module, resource, action] = permissionData.permission_key.split(':');
const response = await post<any>(`${RBAC_API_BASE}/permissions`, {
permission_key: permissionData.permission_key,
display_name: permissionData.display_name,
description: permissionData.description || '',
module,
resource,
action,
permission_type: permissionData.permission_type || 'API',
parent_id: permissionData.parent_id || null,
sort_order: permissionData.sort_order || 0,
metadata: {}
});
const data = handleApiResponse<Permission>(response);
return {
success: true,
message: '权限创建成功',
data
};
} catch (error) {
console.error('创建权限失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '创建权限失败'
};
}
}
/**
* 更新权限
* @param permissionId 权限ID
* @param permissionData 权限数据
*/
export async function updatePermission(
permissionId: number,
permissionData: Partial<Omit<Permission, 'id' | 'children'>>
): Promise<{ success: boolean; message: string }> {
try {
const response = await put<any>(`${RBAC_API_BASE}/permissions/${permissionId}`, permissionData);
handleApiResponse<any>(response);
return { success: true, message: '权限更新成功' };
} catch (error) {
console.error('更新权限失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '更新权限失败'
};
}
}
/**
* 删除权限
* @param permissionId 权限ID
*/
export async function deletePermission(
permissionId: number
): Promise<{ success: boolean; message: string }> {
try {
const response = await del<any>(`${RBAC_API_BASE}/permissions/${permissionId}`);
handleApiResponse<any>(response);
return { success: true, message: '权限删除成功' };
} catch (error) {
console.error('删除权限失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '删除权限失败'
};
}
}
// ==================== 角色权限关联 API ====================
/**
* 获取角色的所有权限(已分配的API权限)
* @param roleId 角色ID
*/
export async function getRolePermissions(roleId: number): Promise<RolePermissionDetail[]> {
try {
// v3.4: 使用文档规范的API路径(查询参数方式)
const response = await get<any>('/api/v3/rbac/role-permissions', {
role_id: roleId
});
if (response.error) {
throw new Error(response.error);
}
// 解析响应数据
let permissions: any[] = [];
if (response.data?.data?.permissions) {
permissions = response.data.data.permissions;
} else if (response.data?.permissions) {
permissions = response.data.permissions;
} else if (Array.isArray(response.data?.data)) {
permissions = response.data.data;
} else if (Array.isArray(response.data)) {
permissions = response.data;
}
return permissions.map(perm => ({
id: perm.id,
permission_id: perm.permission_id || perm.id, // 兼容:如果没有 permission_id,使用 id
permission_key: perm.permission_key,
display_name: perm.display_name,
grant_type: perm.grant_type || 'GRANT',
data_scope: perm.data_scope || 'ALL'
}));
} catch (error) {
console.error('❌ [getRolePermissions] 获取角色权限失败:', error);
return [];
}
}
/**
* 批量分配权限给角色
* @param roleId 角色ID
* @param permissions 权限配置列表
* @param replace 是否替换全部(true=替换,false=追加)
*/
export async function assignPermissionsToRole(
roleId: number,
permissions: RolePermissionConfig[],
replace = false
): Promise<{ success: boolean; message: string }> {
try {
// v3.4: 使用文档规范的API路径(查询参数方式)
const response = await post<any>(`${RBAC_API_BASE}/role-permissions`, {
role_id: roleId,
permissions: permissions.map(p => ({
permission_id: p.permission_id,
grant_type: p.grant_type || 'GRANT',
data_scope: p.data_scope || 'ALL'
})),
replace
});
handleApiResponse<any>(response);
return { success: true, message: '权限分配成功' };
} catch (error) {
console.error('分配权限失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '分配权限失败'
};
}
}
/**
* 更新单个权限配置
* @param roleId 角色ID
* @param permissionId 权限ID
* @param config 权限配置
*/
export async function updateRolePermission(
roleId: number,
permissionId: number,
config: Partial<RolePermissionConfig>
): Promise<{ success: boolean; message: string }> {
try {
// v3.4: 使用文档规范的API路径(查询参数方式)
const response = await put<any>(
`${RBAC_API_BASE}/role-permissions?role_id=${roleId}&permission_id=${permissionId}`,
config
);
handleApiResponse<any>(response);
return { success: true, message: '权限配置更新成功' };
} catch (error) {
console.error('更新权限配置失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '更新权限配置失败'
};
}
}
/**
* 移除角色权限
* @param roleId 角色ID
* @param permissionId 权限ID
*/
export async function revokeRolePermission(
roleId: number,
permissionId: number
): Promise<{ success: boolean; message: string }> {
try {
// v3.4: 使用文档规范的API路径(查询参数方式)
const response = await del<any>(`${RBAC_API_BASE}/role-permissions?role_id=${roleId}&permission_id=${permissionId}`);
handleApiResponse<any>(response);
return { success: true, message: '权限移除成功' };
} catch (error) {
console.error('移除权限失败:', error);
return {
success: false,
message: error instanceof Error ? error.message : '移除权限失败'
};
}
}
/**
* 获取指定用户的所有角色
* @param userId 用户ID
* @returns 用户的角色列表
*/
export async function getUserRoles(userId: number): Promise<RoleInfo[]> {
try {
const response = await get<any>(`/api/v3/rbac/users/${userId}/roles`);
if (response.error) {
console.error('❌ [getUserRoles] API调用失败:', response.error);
throw new Error(response.error);
}
// 后端响应格式: { code: 200, msg: "success", data: { user_id, username, roles: [...] } }
let roles: any[] = [];
if (response.data && response.data.data && Array.isArray(response.data.data.roles)) {
roles = response.data.data.roles;
} else if (response.data && Array.isArray(response.data.roles)) {
// 兼容可能的响应格式
roles = response.data.roles;
}
// 将后端数据转换为RoleInfo格式
return roles.map(role => ({
id: role.id || role.role_id,
role_key: role.role_key,
role_name: role.role_name,
data_scope: role.data_scope,
description: role.description || '',
priority: role.priority || 0,
is_system_role: role.is_system || false,
created_at: role.created_at || '',
updated_at: role.updated_at || ''
}));
} catch (error) {
console.error('❌ [getUserRoles] 获取用户角色失败:', error);
// 失败时返回空数组
return [];
}
}
/**
* v3.7: 获取指定路由的所有权限(包含通用权限)
* 使用专用 API 接口 /api/v3/routes/{route_id}/permissions
* @param routeId 路由ID
* @returns 权限列表(包含独立权限和通用权限,带 is_shared 标识)
*/
export async function getRoutePermissions(routeId: number): Promise<ApiPermission[]> {
try {
// v3.7: 使用专用 API 接口获取路由权限(包含通用权限)
const response = await get<any>(`/api/v3/routes/${routeId}/permissions`);
if (response.error) {
console.error('❌ [getRoutePermissions] API调用失败:', response.error);
return [];
}
// 处理响应数据
// 后端响应格式: { route_id, route_path, route_title, permissions: [...] }
let permissions: any[] = [];
if (response.data) {
// 如果是包装格式 { code, message, data: { permissions: [...] } }
if (response.data.code === 200 && response.data.data?.permissions) {
permissions = response.data.data.permissions;
}
// 如果直接返回 { route_id, permissions: [...] }
else if (response.data.permissions) {
permissions = response.data.permissions;
}
// 如果直接是数组
else if (Array.isArray(response.data)) {
permissions = response.data;
}
}
// 转换为 ApiPermission 格式,保留 is_shared 字段
return permissions.map(p => ({
id: p.id,
permission_key: p.permission_key,
display_name: p.display_name,
api_method: p.api_method || '',
api_path: p.api_path || '',
route_id: p.route_id,
related_routes: p.related_routes,
is_shared: p.is_shared || false // v3.7: 使用后端返回的 is_shared 字段
}));
} catch (error) {
console.error('❌ [getRoutePermissions] 获取路由权限失败:', error);
return [];
}
}
/**
* v3.7: 判断权限是否为通用权限
* 优先使用后端返回的 is_shared 字段,降级时使用 related_routes 判断
* @param permission 权限对象
* @returns 是否为通用权限
*/
export function isSharedPermission(permission: ApiPermission): boolean {
// v3.7: 优先使用后端返回的 is_shared 字段
if (permission.is_shared !== undefined) {
return permission.is_shared === true;
}
// 降级:通过 related_routes 字段判断
return permission.related_routes !== null &&
permission.related_routes !== undefined &&
Array.isArray(permission.related_routes) &&
permission.related_routes.length > 1;
}
/**
* v3.6: 获取通用权限关联的所有路由ID
* @param permission 权限对象
* @returns 路由ID数组
*/
export function getRelatedRouteIds(permission: ApiPermission): number[] {
if (isSharedPermission(permission)) {
return permission.related_routes || [];
}
return permission.route_id ? [permission.route_id] : [];
}