modify: 权限认证完善
This commit is contained in:
+3
-2
@@ -1,7 +1,8 @@
|
||||
FROM debian:bookworm-slim AS builder
|
||||
# FROM debian:bookworm-slim AS builder
|
||||
FROM ubuntu:24.04 AS builder
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends ca-certificates && \
|
||||
apt-get install -y --no-install-recommends ca-certificates tzdata && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY build/chongming /tmp/chongming
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
||||
|
||||
import { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client';
|
||||
import { client } from './client.gen';
|
||||
import type { CreateTodoApiV1TodosPostData, CreateTodoApiV1TodosPostErrors, CreateTodoApiV1TodosPostResponses, DeleteTodoApiV1TodosTodoIdDeleteData, DeleteTodoApiV1TodosTodoIdDeleteErrors, DeleteTodoApiV1TodosTodoIdDeleteResponses, GetCurrentUserInfoApiV1AuthMeGetData, GetCurrentUserInfoApiV1AuthMeGetErrors, GetCurrentUserInfoApiV1AuthMeGetResponses, GetSessionsApiV1AuthSessionsGetData, GetSessionsApiV1AuthSessionsGetResponses, LoginApiV1AuthLoginPostData, LoginApiV1AuthLoginPostErrors, LoginApiV1AuthLoginPostResponses, LogoutAllApiV1AuthLogoutAllPostData, LogoutAllApiV1AuthLogoutAllPostResponses, LogoutApiV1AuthLogoutPostData, LogoutApiV1AuthLogoutPostResponses, ReadTodoApiV1TodosTodoIdGetData, ReadTodoApiV1TodosTodoIdGetErrors, ReadTodoApiV1TodosTodoIdGetResponses, ReadTodosApiV1TodosGetData, ReadTodosApiV1TodosGetErrors, ReadTodosApiV1TodosGetResponses, RefreshTokenApiV1AuthRefreshPostData, RefreshTokenApiV1AuthRefreshPostErrors, RefreshTokenApiV1AuthRefreshPostResponses, RegisterApiV1AuthRegisterPostData, RegisterApiV1AuthRegisterPostErrors, RegisterApiV1AuthRegisterPostResponses, RootGetData, RootGetResponses, UpdateTodoApiV1TodosTodoIdPatchData, UpdateTodoApiV1TodosTodoIdPatchErrors, UpdateTodoApiV1TodosTodoIdPatchResponses, UploadImageApiV1UploadImagePostData, UploadImageApiV1UploadImagePostErrors, UploadImageApiV1UploadImagePostResponses } from './types.gen';
|
||||
import type { AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostData, AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostErrors, AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostResponses, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostData, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostErrors, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostResponses, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostData, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostErrors, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostResponses, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostData, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostErrors, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostResponses, CreatePermissionApiV1PermissionsPostData, CreatePermissionApiV1PermissionsPostErrors, CreatePermissionApiV1PermissionsPostResponses, CreateRoleApiV1RolesPostData, CreateRoleApiV1RolesPostErrors, CreateRoleApiV1RolesPostResponses, CreateTodoApiV1TodosPostData, CreateTodoApiV1TodosPostErrors, CreateTodoApiV1TodosPostResponses, DeletePermissionApiV1PermissionsPermissionIdDeleteData, DeletePermissionApiV1PermissionsPermissionIdDeleteErrors, DeletePermissionApiV1PermissionsPermissionIdDeleteResponses, DeleteRoleApiV1RolesRoleIdDeleteData, DeleteRoleApiV1RolesRoleIdDeleteErrors, DeleteRoleApiV1RolesRoleIdDeleteResponses, DeleteTodoApiV1TodosTodoIdDeleteData, DeleteTodoApiV1TodosTodoIdDeleteErrors, DeleteTodoApiV1TodosTodoIdDeleteResponses, GetCurrentUserInfoApiV1AuthMeGetData, GetCurrentUserInfoApiV1AuthMeGetErrors, GetCurrentUserInfoApiV1AuthMeGetResponses, GetPermissionApiV1PermissionsPermissionIdGetData, GetPermissionApiV1PermissionsPermissionIdGetErrors, GetPermissionApiV1PermissionsPermissionIdGetResponses, GetRoleApiV1RolesRoleIdGetData, GetRoleApiV1RolesRoleIdGetErrors, GetRoleApiV1RolesRoleIdGetResponses, GetRolePermissionsApiV1RolesRoleIdPermissionsGetData, GetRolePermissionsApiV1RolesRoleIdPermissionsGetErrors, GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponses, GetSessionsApiV1AuthSessionsGetData, GetSessionsApiV1AuthSessionsGetResponses, ListPermissionsApiV1PermissionsGetData, ListPermissionsApiV1PermissionsGetErrors, ListPermissionsApiV1PermissionsGetResponses, ListRolesApiV1RolesGetData, ListRolesApiV1RolesGetErrors, ListRolesApiV1RolesGetResponses, LoginApiV1AuthLoginPostData, LoginApiV1AuthLoginPostErrors, LoginApiV1AuthLoginPostResponses, LogoutAllApiV1AuthLogoutAllPostData, LogoutAllApiV1AuthLogoutAllPostResponses, LogoutApiV1AuthLogoutPostData, LogoutApiV1AuthLogoutPostResponses, ReadTodoApiV1TodosTodoIdGetData, ReadTodoApiV1TodosTodoIdGetErrors, ReadTodoApiV1TodosTodoIdGetResponses, ReadTodosApiV1TodosGetData, ReadTodosApiV1TodosGetErrors, ReadTodosApiV1TodosGetResponses, RefreshTokenApiV1AuthRefreshPostData, RefreshTokenApiV1AuthRefreshPostErrors, RefreshTokenApiV1AuthRefreshPostResponses, RegisterApiV1AuthRegisterPostData, RegisterApiV1AuthRegisterPostErrors, RegisterApiV1AuthRegisterPostResponses, RootGetData, RootGetResponses, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostData, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostErrors, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostResponses, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostData, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostErrors, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostResponses, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostData, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostErrors, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostResponses, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostData, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostErrors, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostResponses, UpdatePermissionApiV1PermissionsPermissionIdPutData, UpdatePermissionApiV1PermissionsPermissionIdPutErrors, UpdatePermissionApiV1PermissionsPermissionIdPutResponses, UpdateRoleApiV1RolesRoleIdPutData, UpdateRoleApiV1RolesRoleIdPutErrors, UpdateRoleApiV1RolesRoleIdPutResponses, UpdateTodoApiV1TodosTodoIdPatchData, UpdateTodoApiV1TodosTodoIdPatchErrors, UpdateTodoApiV1TodosTodoIdPatchResponses, UploadImageApiV1UploadImagePostData, UploadImageApiV1UploadImagePostErrors, UploadImageApiV1UploadImagePostResponses } from './types.gen';
|
||||
|
||||
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean, TResponse = unknown> = Options2<TData, ThrowOnError, TResponse> & {
|
||||
/**
|
||||
@@ -20,6 +20,16 @@ export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends
|
||||
|
||||
/**
|
||||
* Read Todos
|
||||
*
|
||||
* 获取待办事项列表
|
||||
*
|
||||
* Args:
|
||||
* offset: 分页偏移量,默认为0,用于指定从第几条记录开始查询
|
||||
* limit: 每页返回的最大记录数,默认为100,用于限制返回结果的数量
|
||||
* session: 数据库会话对象,用于执行数据库操作
|
||||
*
|
||||
* Returns:
|
||||
* List[TodoRead]: 待办事项列表,包含指定范围内的所有待办事项
|
||||
*/
|
||||
export const readTodosApiV1TodosGet = <ThrowOnError extends boolean = false>(options?: Options<ReadTodosApiV1TodosGetData, ThrowOnError>) => (options?.client ?? client).get<ReadTodosApiV1TodosGetResponses, ReadTodosApiV1TodosGetErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -29,6 +39,15 @@ export const readTodosApiV1TodosGet = <ThrowOnError extends boolean = false>(opt
|
||||
|
||||
/**
|
||||
* Create Todo
|
||||
*
|
||||
* 创建新的待办事项
|
||||
*
|
||||
* Args:
|
||||
* todo: 待办事项创建请求体,包含待办事项的详细信息
|
||||
* session: 数据库会话对象,用于执行数据库操作
|
||||
*
|
||||
* Returns:
|
||||
* TodoRead: 创建成功的待办事项对象,包含完整的待办事项信息
|
||||
*/
|
||||
export const createTodoApiV1TodosPost = <ThrowOnError extends boolean = false>(options: Options<CreateTodoApiV1TodosPostData, ThrowOnError>) => (options.client ?? client).post<CreateTodoApiV1TodosPostResponses, CreateTodoApiV1TodosPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -42,6 +61,18 @@ export const createTodoApiV1TodosPost = <ThrowOnError extends boolean = false>(o
|
||||
|
||||
/**
|
||||
* Delete Todo
|
||||
*
|
||||
* 删除待办事项
|
||||
*
|
||||
* Args:
|
||||
* todo_id: 待办事项的唯一标识符
|
||||
* session: 数据库会话对象,用于执行数据库操作
|
||||
*
|
||||
* Returns:
|
||||
* dict: 包含操作结果的字典,{"ok": True} 表示删除成功
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当待办事项不存在时抛出404错误
|
||||
*/
|
||||
export const deleteTodoApiV1TodosTodoIdDelete = <ThrowOnError extends boolean = false>(options: Options<DeleteTodoApiV1TodosTodoIdDeleteData, ThrowOnError>) => (options.client ?? client).delete<DeleteTodoApiV1TodosTodoIdDeleteResponses, DeleteTodoApiV1TodosTodoIdDeleteErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -51,6 +82,18 @@ export const deleteTodoApiV1TodosTodoIdDelete = <ThrowOnError extends boolean =
|
||||
|
||||
/**
|
||||
* Read Todo
|
||||
*
|
||||
* 根据ID获取单个待办事项
|
||||
*
|
||||
* Args:
|
||||
* todo_id: 待办事项的唯一标识符
|
||||
* session: 数据库会话对象,用于执行数据库操作
|
||||
*
|
||||
* Returns:
|
||||
* TodoRead: 查询到的待办事项对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当待办事项不存在时抛出404错误
|
||||
*/
|
||||
export const readTodoApiV1TodosTodoIdGet = <ThrowOnError extends boolean = false>(options: Options<ReadTodoApiV1TodosTodoIdGetData, ThrowOnError>) => (options.client ?? client).get<ReadTodoApiV1TodosTodoIdGetResponses, ReadTodoApiV1TodosTodoIdGetErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -60,6 +103,16 @@ export const readTodoApiV1TodosTodoIdGet = <ThrowOnError extends boolean = false
|
||||
|
||||
/**
|
||||
* Update Todo
|
||||
*
|
||||
* 更新待办事项信息
|
||||
*
|
||||
* Args:
|
||||
* todo_id: 待办事项的唯一标识符
|
||||
* todo_update: 待办事项更新请求体,包含需要更新的字段信息
|
||||
* session: 数据库会话对象,用于执行数据库操作
|
||||
*
|
||||
* Returns:
|
||||
* TodoRead: 更新后的待办事项对象,包含最新的待办事项信息
|
||||
*/
|
||||
export const updateTodoApiV1TodosTodoIdPatch = <ThrowOnError extends boolean = false>(options: Options<UpdateTodoApiV1TodosTodoIdPatchData, ThrowOnError>) => (options.client ?? client).patch<UpdateTodoApiV1TodosTodoIdPatchResponses, UpdateTodoApiV1TodosTodoIdPatchErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -73,6 +126,20 @@ export const updateTodoApiV1TodosTodoIdPatch = <ThrowOnError extends boolean = f
|
||||
|
||||
/**
|
||||
* Register
|
||||
*
|
||||
* 用户注册接口
|
||||
*
|
||||
* 创建新用户账户,检查邮箱是否已被注册,如果邮箱未被注册则创建新用户。
|
||||
*
|
||||
* Args:
|
||||
* user_data: 用户注册数据,包含邮箱、密码、用户名和全名等信息
|
||||
* session: 数据库会话对象,用于执行数据库操作
|
||||
*
|
||||
* Returns:
|
||||
* UserRead: 创建成功的用户信息,包含用户ID、邮箱、用户名等字段
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当邮箱已被注册时抛出400错误
|
||||
*/
|
||||
export const registerApiV1AuthRegisterPost = <ThrowOnError extends boolean = false>(options: Options<RegisterApiV1AuthRegisterPostData, ThrowOnError>) => (options.client ?? client).post<RegisterApiV1AuthRegisterPostResponses, RegisterApiV1AuthRegisterPostErrors, ThrowOnError>({
|
||||
url: '/api/v1/auth/register',
|
||||
@@ -85,6 +152,24 @@ export const registerApiV1AuthRegisterPost = <ThrowOnError extends boolean = fal
|
||||
|
||||
/**
|
||||
* Login
|
||||
*
|
||||
* 用户登录接口
|
||||
*
|
||||
* 验证用户凭据并生成访问令牌和刷新令牌。依次检查邮箱是否存在、密码是否正确、用户是否被禁用。
|
||||
*
|
||||
* Args:
|
||||
* user_data: 用户登录数据,包含邮箱和密码
|
||||
* request: FastAPI请求对象,用于获取客户端信息
|
||||
* session: 数据库会话对象,用于查询用户信息
|
||||
*
|
||||
* Returns:
|
||||
* TokenResponse: 包含访问令牌(access_token)、刷新令牌(refresh_token)和令牌类型(token_type)的响应对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException:
|
||||
* - 400错误:当邮箱不存在时
|
||||
* - 401错误:当密码错误时
|
||||
* - 402错误:当用户账户被禁用时
|
||||
*/
|
||||
export const loginApiV1AuthLoginPost = <ThrowOnError extends boolean = false>(options: Options<LoginApiV1AuthLoginPostData, ThrowOnError>) => (options.client ?? client).post<LoginApiV1AuthLoginPostResponses, LoginApiV1AuthLoginPostErrors, ThrowOnError>({
|
||||
url: '/api/v1/auth/login',
|
||||
@@ -97,6 +182,21 @@ export const loginApiV1AuthLoginPost = <ThrowOnError extends boolean = false>(op
|
||||
|
||||
/**
|
||||
* 刷新访问令牌
|
||||
*
|
||||
* 刷新访问令牌接口
|
||||
*
|
||||
* 使用有效的刷新令牌获取新的访问令牌。验证刷新令牌的有效性,如果有效则生成新的访问令牌。
|
||||
*
|
||||
* Args:
|
||||
* request: FastAPI请求对象,用于获取客户端信息
|
||||
* credentials: HTTP认证凭证,从请求头中提取的刷新令牌
|
||||
* session: 数据库会话对象,用于验证令牌状态
|
||||
*
|
||||
* Returns:
|
||||
* RefreshTokenResponse: 包含新的访问令牌(access_token)和令牌类型(token_type)的响应对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当刷新令牌无效或已过期时抛出401错误
|
||||
*/
|
||||
export const refreshTokenApiV1AuthRefreshPost = <ThrowOnError extends boolean = false>(options?: Options<RefreshTokenApiV1AuthRefreshPostData, ThrowOnError>) => (options?.client ?? client).post<RefreshTokenApiV1AuthRefreshPostResponses, RefreshTokenApiV1AuthRefreshPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -106,6 +206,16 @@ export const refreshTokenApiV1AuthRefreshPost = <ThrowOnError extends boolean =
|
||||
|
||||
/**
|
||||
* Logout
|
||||
*
|
||||
* 用户登出接口
|
||||
*
|
||||
* 使当前的访问令牌失效,实现单设备登出功能。
|
||||
*
|
||||
* Args:
|
||||
* credentials: HTTP认证凭证,从请求头中提取的访问令牌
|
||||
*
|
||||
* Returns:
|
||||
* Userlogout: 登出成功响应,包含成功消息和状态码200
|
||||
*/
|
||||
export const logoutApiV1AuthLogoutPost = <ThrowOnError extends boolean = false>(options?: Options<LogoutApiV1AuthLogoutPostData, ThrowOnError>) => (options?.client ?? client).post<LogoutApiV1AuthLogoutPostResponses, unknown, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -115,6 +225,16 @@ export const logoutApiV1AuthLogoutPost = <ThrowOnError extends boolean = false>(
|
||||
|
||||
/**
|
||||
* 登出所有设备
|
||||
*
|
||||
* 登出所有设备接口
|
||||
*
|
||||
* 使指定用户的所有活跃会话令牌失效,实现多设备同时登出功能。
|
||||
*
|
||||
* Args:
|
||||
* current_user: 当前登录用户的令牌数据,包含用户ID等信息
|
||||
*
|
||||
* Returns:
|
||||
* Userlogout: 登出成功响应,包含成功消息和状态码200
|
||||
*/
|
||||
export const logoutAllApiV1AuthLogoutAllPost = <ThrowOnError extends boolean = false>(options?: Options<LogoutAllApiV1AuthLogoutAllPostData, ThrowOnError>) => (options?.client ?? client).post<LogoutAllApiV1AuthLogoutAllPostResponses, unknown, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -125,7 +245,15 @@ export const logoutAllApiV1AuthLogoutAllPost = <ThrowOnError extends boolean = f
|
||||
/**
|
||||
* 获取用户会话
|
||||
*
|
||||
* 获取用户的所有活跃会话
|
||||
* 获取用户会话列表接口
|
||||
*
|
||||
* 查询并返回指定用户的所有活跃会话信息,包括每个会话的设备信息和创建时间等。
|
||||
*
|
||||
* Args:
|
||||
* current_user: 当前登录用户的令牌数据,包含用户ID等信息
|
||||
*
|
||||
* Returns:
|
||||
* UserSession: 用户会话信息对象,包含用户ID、会话列表和会话总数
|
||||
*/
|
||||
export const getSessionsApiV1AuthSessionsGet = <ThrowOnError extends boolean = false>(options?: Options<GetSessionsApiV1AuthSessionsGetData, ThrowOnError>) => (options?.client ?? client).get<GetSessionsApiV1AuthSessionsGetResponses, unknown, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -136,7 +264,19 @@ export const getSessionsApiV1AuthSessionsGet = <ThrowOnError extends boolean = f
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*
|
||||
* 获取当前登录用户的信息
|
||||
* 获取当前用户信息接口
|
||||
*
|
||||
* 根据当前登录用户的令牌信息,从数据库中查询并返回完整的用户资料。
|
||||
*
|
||||
* Args:
|
||||
* session: 数据库会话对象,用于查询用户信息
|
||||
* current_user: 当前登录用户的令牌数据,包含用户ID等信息
|
||||
*
|
||||
* Returns:
|
||||
* UserRead: 用户详细信息,包含用户ID、邮箱、用户名、全名、激活状态等字段
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当用户不存在时抛出404错误
|
||||
*/
|
||||
export const getCurrentUserInfoApiV1AuthMeGet = <ThrowOnError extends boolean = false>(options?: Options<GetCurrentUserInfoApiV1AuthMeGetData, ThrowOnError>) => (options?.client ?? client).get<GetCurrentUserInfoApiV1AuthMeGetResponses, GetCurrentUserInfoApiV1AuthMeGetErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
@@ -146,6 +286,30 @@ export const getCurrentUserInfoApiV1AuthMeGet = <ThrowOnError extends boolean =
|
||||
|
||||
/**
|
||||
* Upload Image
|
||||
*
|
||||
* 上传图片文件接口
|
||||
*
|
||||
* 该接口用于接收用户上传的图片文件,进行格式验证后保存到服务器,
|
||||
* 并返回文件的访问URL、文件名和文件类型信息。
|
||||
*
|
||||
* Args:
|
||||
* file (UploadFile): 上传的文件对象,必须为图片格式
|
||||
*
|
||||
* Returns:
|
||||
* UploadResponse: 包含以下字段的响应对象:
|
||||
* - url (str): 上传文件的访问URL
|
||||
* - filename (str): 保存后的文件名
|
||||
* - file_type (str): 文件扩展名(如 .jpg, .png 等)
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException:
|
||||
* - 400: 当文件类型不是图片格式时
|
||||
* - 400: 当文件名为空时
|
||||
* - 400: 当文件扩展名不在允许列表中时
|
||||
*
|
||||
* Note:
|
||||
* 支持的图片格式包括:.jpg, .jpeg, .png, .gif, .webp
|
||||
* 需要用户认证才能访问此接口
|
||||
*/
|
||||
export const uploadImageApiV1UploadImagePost = <ThrowOnError extends boolean = false>(options: Options<UploadImageApiV1UploadImagePostData, ThrowOnError>) => (options.client ?? client).post<UploadImageApiV1UploadImagePostResponses, UploadImageApiV1UploadImagePostErrors, ThrowOnError>({
|
||||
...formDataBodySerializer,
|
||||
@@ -158,9 +322,484 @@ export const uploadImageApiV1UploadImagePost = <ThrowOnError extends boolean = f
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取权限列表
|
||||
*
|
||||
* 获取权限列表
|
||||
*
|
||||
* 返回系统中的所有权限项,支持分页和按资源名称筛选。
|
||||
*
|
||||
* Args:
|
||||
* skip: 跳过的记录数,用于分页
|
||||
* limit: 返回的最大记录数,用于分页
|
||||
* resource: 资源名称筛选条件,可选
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* list[Permission]: 权限对象列表
|
||||
*/
|
||||
export const listPermissionsApiV1PermissionsGet = <ThrowOnError extends boolean = false>(options?: Options<ListPermissionsApiV1PermissionsGetData, ThrowOnError>) => (options?.client ?? client).get<ListPermissionsApiV1PermissionsGetResponses, ListPermissionsApiV1PermissionsGetErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建权限
|
||||
*
|
||||
* 创建权限
|
||||
*
|
||||
* 创建新的权限项,权限名称格式应为"资源:操作",如"user:create"。
|
||||
*
|
||||
* Args:
|
||||
* permission_data: 权限创建数据,包含名称、资源、操作和描述等信息
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* Permission: 创建成功的权限对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当权限名称已存在时抛出400错误
|
||||
*/
|
||||
export const createPermissionApiV1PermissionsPost = <ThrowOnError extends boolean = false>(options: Options<CreatePermissionApiV1PermissionsPostData, ThrowOnError>) => (options.client ?? client).post<CreatePermissionApiV1PermissionsPostResponses, CreatePermissionApiV1PermissionsPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 删除权限
|
||||
*
|
||||
* 删除权限
|
||||
*
|
||||
* 删除指定权限。如果权限已被分配给角色,则无法删除。
|
||||
*
|
||||
* Args:
|
||||
* permission_id: 权限ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 删除成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当权限被角色使用时抛出400错误,权限不存在时抛出404错误
|
||||
*/
|
||||
export const deletePermissionApiV1PermissionsPermissionIdDelete = <ThrowOnError extends boolean = false>(options: Options<DeletePermissionApiV1PermissionsPermissionIdDeleteData, ThrowOnError>) => (options.client ?? client).delete<DeletePermissionApiV1PermissionsPermissionIdDeleteResponses, DeletePermissionApiV1PermissionsPermissionIdDeleteErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions/{permission_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取权限详情
|
||||
*
|
||||
* 获取权限详情
|
||||
*
|
||||
* 根据权限ID获取指定权限的详细信息。
|
||||
*
|
||||
* Args:
|
||||
* permission_id: 权限ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* Permission: 权限对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当权限不存在时抛出404错误
|
||||
*/
|
||||
export const getPermissionApiV1PermissionsPermissionIdGet = <ThrowOnError extends boolean = false>(options: Options<GetPermissionApiV1PermissionsPermissionIdGetData, ThrowOnError>) => (options.client ?? client).get<GetPermissionApiV1PermissionsPermissionIdGetResponses, GetPermissionApiV1PermissionsPermissionIdGetErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions/{permission_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 更新权限
|
||||
*
|
||||
* 更新权限信息
|
||||
*
|
||||
* 更新指定权限的名称、描述、资源或操作等信息。
|
||||
*
|
||||
* Args:
|
||||
* permission_id: 权限ID
|
||||
* permission_update: 权限更新数据,所有字段可选
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* Permission: 更新后的权限对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当权限不存在时抛出404错误
|
||||
*/
|
||||
export const updatePermissionApiV1PermissionsPermissionIdPut = <ThrowOnError extends boolean = false>(options: Options<UpdatePermissionApiV1PermissionsPermissionIdPutData, ThrowOnError>) => (options.client ?? client).put<UpdatePermissionApiV1PermissionsPermissionIdPutResponses, UpdatePermissionApiV1PermissionsPermissionIdPutErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions/{permission_id}',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 将权限分配给角色
|
||||
*
|
||||
* 将权限分配给角色
|
||||
*
|
||||
* 将指定权限分配给指定角色。
|
||||
*
|
||||
* Args:
|
||||
* permission_id: 权限ID
|
||||
* role_id: 角色ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 分配成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当权限已分配给角色或角色/权限不存在时抛出错误
|
||||
*/
|
||||
export const assignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPost = <ThrowOnError extends boolean = false>(options: Options<AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostData, ThrowOnError>) => (options.client ?? client).post<AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostResponses, AssignPermissionToRoleApiV1PermissionsPermissionIdAssignToRoleRoleIdPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions/{permission_id}/assign-to-role/{role_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 从角色解除权限绑定
|
||||
*
|
||||
* 从角色解除权限绑定
|
||||
*
|
||||
* 将指定权限从指定角色中移除。
|
||||
*
|
||||
* Args:
|
||||
* permission_id: 权限ID
|
||||
* role_id: 角色ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 解绑成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当角色不存在时抛出404错误
|
||||
*/
|
||||
export const unbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPost = <ThrowOnError extends boolean = false>(options: Options<UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostData, ThrowOnError>) => (options.client ?? client).post<UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostResponses, UnbindPermissionFromRoleApiV1PermissionsPermissionIdUnbindFromRoleRoleIdPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions/{permission_id}/unbind-from-role/{role_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 将权限分配给用户
|
||||
*
|
||||
* 将权限分配给用户
|
||||
*
|
||||
* 将指定权限分配给指定用户。如果用户没有专属角色,则会自动创建一个专属角色。
|
||||
*
|
||||
* Args:
|
||||
* permission_id: 权限ID
|
||||
* user_id: 用户UUID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 分配成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当用户或权限不存在时抛出404错误
|
||||
*/
|
||||
export const assignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPost = <ThrowOnError extends boolean = false>(options: Options<AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostData, ThrowOnError>) => (options.client ?? client).post<AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostResponses, AssignPermissionToUserApiV1PermissionsPermissionIdAssignToUserUserIdPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions/{permission_id}/assign-to-user/{user_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 从用户解除权限绑定
|
||||
*
|
||||
* 从用户解除权限绑定
|
||||
*
|
||||
* 将指定权限从指定用户中移除。
|
||||
*
|
||||
* Args:
|
||||
* permission_id: 权限ID
|
||||
* user_id: 用户UUID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 解绑成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当用户或角色不存在时抛出404错误
|
||||
*/
|
||||
export const unbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPost = <ThrowOnError extends boolean = false>(options: Options<UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostData, ThrowOnError>) => (options.client ?? client).post<UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostResponses, UnbindPermissionFromUserApiV1PermissionsPermissionIdUnbindFromUserUserIdPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/permissions/{permission_id}/unbind-from-user/{user_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取角色列表
|
||||
*
|
||||
* 获取角色列表
|
||||
*
|
||||
* 返回系统中的所有角色,支持分页。
|
||||
*
|
||||
* Args:
|
||||
* skip: 跳过的记录数,用于分页
|
||||
* limit: 返回的最大记录数,用于分页
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* list[Role]: 角色对象列表
|
||||
*/
|
||||
export const listRolesApiV1RolesGet = <ThrowOnError extends boolean = false>(options?: Options<ListRolesApiV1RolesGetData, ThrowOnError>) => (options?.client ?? client).get<ListRolesApiV1RolesGetResponses, ListRolesApiV1RolesGetErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 创建角色
|
||||
*
|
||||
* 创建角色
|
||||
*
|
||||
* 创建新的角色,如 admin、manager、operator 等。
|
||||
*
|
||||
* Args:
|
||||
* name: 角色名称
|
||||
* description: 角色描述,可选
|
||||
* is_only_user: 是否仅用于单个用户,默认为False
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* Role: 创建成功的角色对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当角色名称已存在时抛出400错误
|
||||
*/
|
||||
export const createRoleApiV1RolesPost = <ThrowOnError extends boolean = false>(options: Options<CreateRoleApiV1RolesPostData, ThrowOnError>) => (options.client ?? client).post<CreateRoleApiV1RolesPostResponses, CreateRoleApiV1RolesPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
*
|
||||
* 删除角色
|
||||
*
|
||||
* 删除指定角色。如果角色已被分配给用户,则无法删除。
|
||||
*
|
||||
* Args:
|
||||
* role_id: 角色ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 删除成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当角色被用户使用时抛出400错误,角色不存在时抛出404错误
|
||||
*/
|
||||
export const deleteRoleApiV1RolesRoleIdDelete = <ThrowOnError extends boolean = false>(options: Options<DeleteRoleApiV1RolesRoleIdDeleteData, ThrowOnError>) => (options.client ?? client).delete<DeleteRoleApiV1RolesRoleIdDeleteResponses, DeleteRoleApiV1RolesRoleIdDeleteErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles/{role_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取角色详情
|
||||
*
|
||||
* 获取角色详情
|
||||
*
|
||||
* 根据角色ID获取指定角色的详细信息。
|
||||
*
|
||||
* Args:
|
||||
* role_id: 角色ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* Role: 角色对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当角色不存在时抛出404错误
|
||||
*/
|
||||
export const getRoleApiV1RolesRoleIdGet = <ThrowOnError extends boolean = false>(options: Options<GetRoleApiV1RolesRoleIdGetData, ThrowOnError>) => (options.client ?? client).get<GetRoleApiV1RolesRoleIdGetResponses, GetRoleApiV1RolesRoleIdGetErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles/{role_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 更新角色
|
||||
*
|
||||
* 更新角色信息
|
||||
*
|
||||
* 更新指定角色的名称或描述等信息。
|
||||
*
|
||||
* Args:
|
||||
* role_id: 角色ID
|
||||
* role_update: 角色更新数据,所有字段可选
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* Role: 更新后的角色对象
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当角色不存在时抛出404错误
|
||||
*/
|
||||
export const updateRoleApiV1RolesRoleIdPut = <ThrowOnError extends boolean = false>(options: Options<UpdateRoleApiV1RolesRoleIdPutData, ThrowOnError>) => (options.client ?? client).put<UpdateRoleApiV1RolesRoleIdPutResponses, UpdateRoleApiV1RolesRoleIdPutErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles/{role_id}',
|
||||
...options,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 将角色分配给用户
|
||||
*
|
||||
* 将角色分配给用户
|
||||
*
|
||||
* 将指定角色分配给指定用户。
|
||||
*
|
||||
* Args:
|
||||
* role_id: 角色ID
|
||||
* user_id: 用户UUID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 分配成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当角色或用户不存在时抛出404错误
|
||||
*/
|
||||
export const assignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPost = <ThrowOnError extends boolean = false>(options: Options<AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostData, ThrowOnError>) => (options.client ?? client).post<AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostResponses, AssignRoleToUserApiV1RolesRoleIdAssignToUserUserIdPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles/{role_id}/assign-to-user/{user_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 从用户解除角色绑定
|
||||
*
|
||||
* 从用户解除角色绑定
|
||||
*
|
||||
* 将指定角色从指定用户中移除。
|
||||
*
|
||||
* Args:
|
||||
* role_id: 角色ID
|
||||
* user_id: 用户UUID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 解绑成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当用户不存在时抛出404错误
|
||||
*/
|
||||
export const unbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPost = <ThrowOnError extends boolean = false>(options: Options<UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostData, ThrowOnError>) => (options.client ?? client).post<UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostResponses, UnbindRoleFromUserApiV1RolesRoleIdUnbindFromUserUserIdPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles/{role_id}/unbind-from-user/{user_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取角色的权限列表
|
||||
*
|
||||
* 获取指定角色的所有权限
|
||||
*
|
||||
* Args:
|
||||
* role_id: 角色ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* list: 权限对象列表
|
||||
*/
|
||||
export const getRolePermissionsApiV1RolesRoleIdPermissionsGet = <ThrowOnError extends boolean = false>(options: Options<GetRolePermissionsApiV1RolesRoleIdPermissionsGetData, ThrowOnError>) => (options.client ?? client).get<GetRolePermissionsApiV1RolesRoleIdPermissionsGetResponses, GetRolePermissionsApiV1RolesRoleIdPermissionsGetErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles/{role_id}/permissions',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 为角色分配权限
|
||||
*
|
||||
* 为角色分配权限
|
||||
*
|
||||
* 将指定权限分配给指定角色。
|
||||
*
|
||||
* Args:
|
||||
* role_id: 角色ID
|
||||
* permission_id: 权限ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 分配成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当权限已分配给角色或角色/权限不存在时抛出错误
|
||||
*/
|
||||
export const assignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPost = <ThrowOnError extends boolean = false>(options: Options<AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostData, ThrowOnError>) => (options.client ?? client).post<AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostResponses, AssignPermissionToRoleApiV1RolesRoleIdAssignPermissionPermissionIdPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles/{role_id}/assign-permission/{permission_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 从角色解除权限绑定
|
||||
*
|
||||
* 从角色解除权限绑定
|
||||
*
|
||||
* 将指定权限从指定角色中移除。
|
||||
*
|
||||
* Args:
|
||||
* role_id: 角色ID
|
||||
* permission_id: 权限ID
|
||||
* session: 数据库会话对象
|
||||
* current_user: 当前登录用户
|
||||
*
|
||||
* Returns:
|
||||
* dict: 解绑成功响应消息
|
||||
*
|
||||
* Raises:
|
||||
* HTTPException: 当角色不存在时抛出404错误
|
||||
*/
|
||||
export const unbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPost = <ThrowOnError extends boolean = false>(options: Options<UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostData, ThrowOnError>) => (options.client ?? client).post<UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostResponses, UnbindPermissionFromRoleApiV1RolesRoleIdUnbindPermissionPermissionIdPostErrors, ThrowOnError>({
|
||||
security: [{ scheme: 'bearer', type: 'http' }],
|
||||
url: '/api/v1/roles/{role_id}/unbind-permission/{permission_id}',
|
||||
...options
|
||||
});
|
||||
|
||||
/**
|
||||
* 根路径
|
||||
*
|
||||
* 根路径,返回应用信息
|
||||
* 根路径端点,返回应用的基本信息。
|
||||
*
|
||||
* 返回:
|
||||
* dict: 包含应用名称、版本、文档地址和健康检查地址的字典
|
||||
*/
|
||||
export const rootGet = <ThrowOnError extends boolean = false>(options?: Options<RootGetData, ThrowOnError>) => (options?.client ?? client).get<RootGetResponses, unknown, ThrowOnError>({ url: '/', ...options });
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,8 @@ API 路由
|
||||
|
||||
from fastapi import APIRouter
|
||||
|
||||
from .routers import todo, auth, upload
|
||||
from .routers import todo, auth, upload, permission, role
|
||||
|
||||
|
||||
# 创建主路由
|
||||
api_router = APIRouter()
|
||||
@@ -13,6 +14,8 @@ api_router = APIRouter()
|
||||
api_router.include_router(todo.router, prefix="/todos", tags=["todo"])
|
||||
api_router.include_router(auth.router, prefix="/auth", tags=["authentication"])
|
||||
api_router.include_router(upload.router, prefix="/upload", tags=["upload"])
|
||||
api_router.include_router(permission.router, prefix="/permissions", tags=["permission"])
|
||||
api_router.include_router(role.router, prefix="/roles", tags=["role"])
|
||||
|
||||
|
||||
# 导出路由
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
from typing import List, Set, Union
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ..service.auth import get_auth_service
|
||||
from ..service.permission import PermissionService
|
||||
from ..service.user import UserService
|
||||
from ..core.database import get_session
|
||||
|
||||
from plugins.jwt.jwt_cache import TokenData
|
||||
|
||||
|
||||
@@ -40,3 +47,177 @@ async def get_current_user(
|
||||
)
|
||||
|
||||
return token_data
|
||||
|
||||
|
||||
class PermissionChecker:
|
||||
"""
|
||||
权限校验依赖类
|
||||
|
||||
用于校验当前用户是否拥有访问特定接口所需的权限。
|
||||
支持以下特性:
|
||||
- 超级用户自动拥有所有权限(需要额外注入 RequireSuperuser 或由业务层检查)
|
||||
- 支持单权限校验
|
||||
- 支持多权限校验(all模式:需要拥有全部权限;any模式:拥有任一权限即可)
|
||||
|
||||
使用示例:
|
||||
```python
|
||||
# 单权限校验
|
||||
@router.get("/orders")
|
||||
async def list_orders(
|
||||
_: bool = Depends(PermissionChecker("sales_order:read")),
|
||||
):
|
||||
...
|
||||
|
||||
# 多权限 all 模式(需要同时拥有创建和审核权限)
|
||||
@router.post("/outbounds")
|
||||
async def create_outbound(
|
||||
_: bool = Depends(PermissionChecker(
|
||||
["sales_outbound:create", "sales_outbound:approve"],
|
||||
require_all=True
|
||||
)),
|
||||
):
|
||||
...
|
||||
|
||||
# 多权限 any 模式(拥有任一权限即可)
|
||||
@router.get("/reports/sales")
|
||||
async def view_sales_report(
|
||||
_: bool = Depends(PermissionChecker(
|
||||
["report_sales:view", "sales_order:read"],
|
||||
require_all=False
|
||||
)),
|
||||
):
|
||||
...
|
||||
```
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
permissions: Union[str, List[str]],
|
||||
require_all: bool = True,
|
||||
require_superuser: bool = False,
|
||||
):
|
||||
"""
|
||||
初始化权限校验器
|
||||
|
||||
Args:
|
||||
permissions: 所需的权限标识符,可以是单个字符串或字符串列表
|
||||
- 字符串格式:resource:action(如 "sales_order:read")
|
||||
- 列表格式:["perm1:action1", "perm2:action2"]
|
||||
require_all: 多权限校验模式
|
||||
- True: 用户必须拥有所有指定权限
|
||||
- False: 用户只需拥有任一指定权限
|
||||
require_superuser: 是否要求超级用户身份(超级用户自动拥有所有权限)
|
||||
"""
|
||||
self.permissions = [permissions] if isinstance(permissions, str) else permissions
|
||||
self.require_all = require_all
|
||||
self.require_superuser = require_superuser
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
current_user: TokenData = Depends(get_current_user),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
) -> bool:
|
||||
"""
|
||||
执行权限校验
|
||||
|
||||
Args:
|
||||
current_user: 当前登录用户的Token数据
|
||||
session: 数据库会话对象
|
||||
|
||||
Returns:
|
||||
bool: 权限校验通过返回True
|
||||
|
||||
Raises:
|
||||
HTTPException: 权限不足时抛出403禁止访问异常
|
||||
"""
|
||||
from uuid import UUID
|
||||
|
||||
# 1. 如果配置了超级用户要求,先检查超级用户
|
||||
if self.require_superuser:
|
||||
user = await UserService.get_user_by_id(UUID(current_user.user_id), session)
|
||||
if user and user.is_superuser:
|
||||
return True
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail={
|
||||
"message": "仅超级管理员可执行此操作",
|
||||
"code": status.HTTP_403_FORBIDDEN,
|
||||
},
|
||||
)
|
||||
|
||||
# 2. 如果没有需要校验的权限,直接放行
|
||||
if not self.permissions:
|
||||
return True
|
||||
|
||||
# 3. 获取用户的权限列表(通过角色链汇总)
|
||||
user_permissions = await PermissionService.get_permissions_by_user(
|
||||
session=session,
|
||||
user_id=UUID(current_user.user_id),
|
||||
)
|
||||
|
||||
# 构建用户拥有的权限名称集合
|
||||
user_perm_names: Set[str] = {perm.name for perm in user_permissions}
|
||||
|
||||
# 4. 权限匹配检查
|
||||
if self.require_all:
|
||||
# all模式:必须拥有所有指定权限
|
||||
missing = [perm for perm in self.permissions if perm not in user_perm_names]
|
||||
if missing:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail={
|
||||
"message": "权限不足",
|
||||
"code": status.HTTP_403_FORBIDDEN,
|
||||
"missing_permissions": missing,
|
||||
},
|
||||
)
|
||||
else:
|
||||
# any模式:拥有任一权限即可
|
||||
has_any = any(perm in user_perm_names for perm in self.permissions)
|
||||
if not has_any:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail={
|
||||
"message": "权限不足",
|
||||
"code": status.HTTP_403_FORBIDDEN,
|
||||
"required_permissions": self.permissions,
|
||||
},
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class RequireSuperuser:
|
||||
"""
|
||||
超级用户校验依赖
|
||||
|
||||
要求当前用户必须是超级用户(is_superuser=True),否则返回403。
|
||||
通常用于系统管理类接口。
|
||||
|
||||
使用示例:
|
||||
```python
|
||||
@router.delete("/users/{user_id}")
|
||||
async def delete_user(
|
||||
_: bool = Depends(RequireSuperuser()),
|
||||
):
|
||||
...
|
||||
```
|
||||
"""
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
current_user: TokenData = Depends(get_current_user),
|
||||
session: AsyncSession = Depends(get_session),
|
||||
) -> bool:
|
||||
from uuid import UUID
|
||||
|
||||
user = await UserService.get_user_by_id(UUID(current_user.user_id), session)
|
||||
if not user or not user.is_superuser:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail={
|
||||
"message": "仅超级管理员可执行此操作",
|
||||
"code": status.HTTP_403_FORBIDDEN,
|
||||
},
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ...core.database import get_session
|
||||
from ...model.permission import Permission, PermissionCreate, PermissionUpdate
|
||||
from ...model.http_error import HTTPError, Detail
|
||||
from ...service.permission import PermissionService
|
||||
from ..deps import get_current_user, PermissionChecker
|
||||
from ...constant.permission import PermissionMgtConstant, RoleConstant
|
||||
|
||||
from plugins.jwt.jwt_cache import TokenData
|
||||
|
||||
|
||||
router = APIRouter(tags=["permission"])
|
||||
|
||||
|
||||
@router.post(
|
||||
"",
|
||||
response_model=Permission,
|
||||
responses={
|
||||
400: {"model": HTTPError, "description": "权限名称已存在"},
|
||||
},
|
||||
summary="创建权限",
|
||||
)
|
||||
async def create_permission(
|
||||
permission_data: PermissionCreate,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_CREATE, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
创建权限
|
||||
|
||||
创建新的权限项,权限名称格式应为"资源:操作",如"user:create"。
|
||||
|
||||
Args:
|
||||
permission_data: 权限创建数据,包含名称、资源、操作和描述等信息
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
Permission: 创建成功的权限对象
|
||||
|
||||
Raises:
|
||||
HTTPException: 当权限名称已存在时抛出400错误
|
||||
"""
|
||||
existing = await PermissionService.get_permission_by_name(
|
||||
session, permission_data.name
|
||||
)
|
||||
if existing:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=Detail(
|
||||
message="权限名称已存在", code=status.HTTP_400_BAD_REQUEST
|
||||
).model_dump(),
|
||||
)
|
||||
|
||||
permission = await PermissionService.create_permission(
|
||||
session=session,
|
||||
name=permission_data.name,
|
||||
resource=permission_data.resource,
|
||||
action=permission_data.action,
|
||||
description=permission_data.description,
|
||||
)
|
||||
return permission
|
||||
|
||||
|
||||
@router.get(
|
||||
"",
|
||||
response_model=list[Permission],
|
||||
summary="获取权限列表",
|
||||
)
|
||||
async def list_permissions(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
resource: Optional[str] = None,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker([PermissionMgtConstant.PERMISSION_READ, RoleConstant.ROLE_READ], require_all=False)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
获取权限列表
|
||||
|
||||
返回系统中的所有权限项,支持分页和按资源名称筛选。
|
||||
|
||||
Args:
|
||||
skip: 跳过的记录数,用于分页
|
||||
limit: 返回的最大记录数,用于分页
|
||||
resource: 资源名称筛选条件,可选
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
list[Permission]: 权限对象列表
|
||||
"""
|
||||
permissions = await PermissionService.list_permissions(
|
||||
session=session,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
resource=resource,
|
||||
)
|
||||
return list(permissions)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{permission_id}",
|
||||
response_model=Permission,
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "权限不存在"},
|
||||
},
|
||||
summary="获取权限详情",
|
||||
)
|
||||
async def get_permission(
|
||||
permission_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_READ)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
获取权限详情
|
||||
|
||||
根据权限ID获取指定权限的详细信息。
|
||||
|
||||
Args:
|
||||
permission_id: 权限ID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
Permission: 权限对象
|
||||
|
||||
Raises:
|
||||
HTTPException: 当权限不存在时抛出404错误
|
||||
"""
|
||||
permission = await PermissionService.get_permission_by_id(session, permission_id)
|
||||
if not permission:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=Detail(
|
||||
message="权限不存在", code=status.HTTP_404_NOT_FOUND
|
||||
).model_dump(),
|
||||
)
|
||||
return permission
|
||||
|
||||
|
||||
@router.put(
|
||||
"/{permission_id}",
|
||||
response_model=Permission,
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "权限不存在"},
|
||||
},
|
||||
summary="更新权限",
|
||||
)
|
||||
async def update_permission(
|
||||
permission_id: int,
|
||||
permission_update: PermissionUpdate,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_UPDATE, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
更新权限信息
|
||||
|
||||
更新指定权限的名称、描述、资源或操作等信息。
|
||||
|
||||
Args:
|
||||
permission_id: 权限ID
|
||||
permission_update: 权限更新数据,所有字段可选
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
Permission: 更新后的权限对象
|
||||
|
||||
Raises:
|
||||
HTTPException: 当权限不存在时抛出404错误
|
||||
"""
|
||||
permission = await PermissionService.update_permission(
|
||||
session=session,
|
||||
permission_id=permission_id,
|
||||
permission_update=permission_update,
|
||||
)
|
||||
return permission
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{permission_id}",
|
||||
responses={
|
||||
400: {"model": HTTPError, "description": "权限已被角色使用,无法删除"},
|
||||
404: {"model": HTTPError, "description": "权限不存在"},
|
||||
},
|
||||
summary="删除权限",
|
||||
)
|
||||
async def delete_permission(
|
||||
permission_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_DELETE, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
删除权限
|
||||
|
||||
删除指定权限。如果权限已被分配给角色,则无法删除。
|
||||
|
||||
Args:
|
||||
permission_id: 权限ID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
dict: 删除成功响应消息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当权限被角色使用时抛出400错误,权限不存在时抛出404错误
|
||||
"""
|
||||
await PermissionService.delete_permission(session=session, permission_id=permission_id)
|
||||
return {"message": "权限删除成功", "code": 200}
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{permission_id}/assign-to-role/{role_id}",
|
||||
summary="将权限分配给角色",
|
||||
responses={
|
||||
400: {"model": HTTPError, "description": "权限已分配给角色"},
|
||||
404: {"model": HTTPError, "description": "角色或权限不存在"},
|
||||
},
|
||||
)
|
||||
async def assign_permission_to_role(
|
||||
permission_id: int,
|
||||
role_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_BIND, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
将权限分配给角色
|
||||
|
||||
将指定权限分配给指定角色。
|
||||
|
||||
Args:
|
||||
permission_id: 权限ID
|
||||
role_id: 角色ID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
dict: 分配成功响应消息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当权限已分配给角色或角色/权限不存在时抛出错误
|
||||
"""
|
||||
await PermissionService.assign_permission_to_role(
|
||||
session=session, role_id=role_id, permission_id=permission_id
|
||||
)
|
||||
return {"message": "权限分配成功", "code": 200}
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{permission_id}/unbind-from-role/{role_id}",
|
||||
summary="从角色解除权限绑定",
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "角色不存在"},
|
||||
},
|
||||
)
|
||||
async def unbind_permission_from_role(
|
||||
permission_id: int,
|
||||
role_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_UNBIND, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
从角色解除权限绑定
|
||||
|
||||
将指定权限从指定角色中移除。
|
||||
|
||||
Args:
|
||||
permission_id: 权限ID
|
||||
role_id: 角色ID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
dict: 解绑成功响应消息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当角色不存在时抛出404错误
|
||||
"""
|
||||
await PermissionService.unbind_permission_from_role(
|
||||
session=session, role_id=role_id, permission_id=permission_id
|
||||
)
|
||||
return {"message": "权限解除绑定成功", "code": 200}
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{permission_id}/assign-to-user/{user_id}",
|
||||
summary="将权限分配给用户",
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "用户或权限不存在"},
|
||||
},
|
||||
)
|
||||
async def assign_permission_to_user(
|
||||
permission_id: int,
|
||||
user_id: UUID,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_BIND, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
将权限分配给用户
|
||||
|
||||
将指定权限分配给指定用户。如果用户没有专属角色,则会自动创建一个专属角色。
|
||||
|
||||
Args:
|
||||
permission_id: 权限ID
|
||||
user_id: 用户UUID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
dict: 分配成功响应消息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当用户或权限不存在时抛出404错误
|
||||
"""
|
||||
await PermissionService.assign_permission_to_user(
|
||||
session=session, user_id=user_id, permission_id=permission_id
|
||||
)
|
||||
return {"message": "权限分配成功", "code": 200}
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{permission_id}/unbind-from-user/{user_id}",
|
||||
summary="从用户解除权限绑定",
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "用户或权限不存在"},
|
||||
},
|
||||
)
|
||||
async def unbind_permission_from_user(
|
||||
permission_id: int,
|
||||
user_id: UUID,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(PermissionMgtConstant.PERMISSION_UNBIND, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
从用户解除权限绑定
|
||||
|
||||
将指定权限从指定用户中移除。
|
||||
|
||||
Args:
|
||||
permission_id: 权限ID
|
||||
user_id: 用户UUID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
dict: 解绑成功响应消息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当用户或角色不存在时抛出404错误
|
||||
"""
|
||||
await PermissionService.unbind_permission_from_user(
|
||||
session=session, user_id=user_id, permission_id=permission_id
|
||||
)
|
||||
return {"message": "权限解除绑定成功", "code": 200}
|
||||
@@ -0,0 +1,351 @@
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ...core.database import get_session
|
||||
from ...model.role import Role, RoleUpdate
|
||||
from ...model.http_error import HTTPError, Detail
|
||||
from ...service.role import RoleService
|
||||
from ...service.permission import PermissionService
|
||||
from ..deps import get_current_user, PermissionChecker
|
||||
from ...constant.permission import RoleConstant
|
||||
|
||||
from plugins.jwt.jwt_cache import TokenData
|
||||
|
||||
|
||||
router = APIRouter(tags=["role"])
|
||||
|
||||
|
||||
@router.post(
|
||||
"",
|
||||
response_model=Role,
|
||||
responses={
|
||||
400: {"model": HTTPError, "description": "角色名称已存在"},
|
||||
},
|
||||
summary="创建角色",
|
||||
)
|
||||
async def create_role(
|
||||
name: str,
|
||||
description: Optional[str] = None,
|
||||
is_only_user: bool = False,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(RoleConstant.ROLE_CREATE, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
创建角色
|
||||
|
||||
创建新的角色,如 admin、manager、operator 等。
|
||||
|
||||
Args:
|
||||
name: 角色名称
|
||||
description: 角色描述,可选
|
||||
is_only_user: 是否仅用于单个用户,默认为False
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
Role: 创建成功的角色对象
|
||||
|
||||
Raises:
|
||||
HTTPException: 当角色名称已存在时抛出400错误
|
||||
"""
|
||||
existing = await RoleService.get_role_by_name(session, name)
|
||||
if existing:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=Detail(
|
||||
message="角色名称已存在", code=status.HTTP_400_BAD_REQUEST
|
||||
).model_dump(),
|
||||
)
|
||||
|
||||
role = await RoleService.create_role(
|
||||
session=session,
|
||||
name=name,
|
||||
description=description,
|
||||
is_only_user=is_only_user,
|
||||
)
|
||||
return role
|
||||
|
||||
|
||||
@router.get(
|
||||
"",
|
||||
response_model=list[Role],
|
||||
summary="获取角色列表",
|
||||
)
|
||||
async def list_roles(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(RoleConstant.ROLE_READ)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
获取角色列表
|
||||
|
||||
返回系统中的所有角色,支持分页。
|
||||
|
||||
Args:
|
||||
skip: 跳过的记录数,用于分页
|
||||
limit: 返回的最大记录数,用于分页
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
list[Role]: 角色对象列表
|
||||
"""
|
||||
roles = await RoleService.list_roles(
|
||||
session=session,
|
||||
skip=skip,
|
||||
limit=limit,
|
||||
)
|
||||
return list(roles)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{role_id}",
|
||||
response_model=Role,
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "角色不存在"},
|
||||
},
|
||||
summary="获取角色详情",
|
||||
)
|
||||
async def get_role(
|
||||
role_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(RoleConstant.ROLE_READ)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
获取角色详情
|
||||
|
||||
根据角色ID获取指定角色的详细信息。
|
||||
|
||||
Args:
|
||||
role_id: 角色ID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
Role: 角色对象
|
||||
|
||||
Raises:
|
||||
HTTPException: 当角色不存在时抛出404错误
|
||||
"""
|
||||
role = await RoleService.get_role_by_id(session, role_id)
|
||||
if not role:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=Detail(
|
||||
message="角色不存在", code=status.HTTP_404_NOT_FOUND
|
||||
).model_dump(),
|
||||
)
|
||||
return role
|
||||
|
||||
|
||||
@router.put(
|
||||
"/{role_id}",
|
||||
response_model=Role,
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "角色不存在"},
|
||||
},
|
||||
summary="更新角色",
|
||||
)
|
||||
async def update_role(
|
||||
role_id: int,
|
||||
role_update: RoleUpdate,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(RoleConstant.ROLE_UPDATE, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
更新角色信息
|
||||
|
||||
更新指定角色的名称或描述等信息。
|
||||
|
||||
Args:
|
||||
role_id: 角色ID
|
||||
role_update: 角色更新数据,所有字段可选
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
Role: 更新后的角色对象
|
||||
|
||||
Raises:
|
||||
HTTPException: 当角色不存在时抛出404错误
|
||||
"""
|
||||
role = await RoleService.update_role(
|
||||
session=session,
|
||||
role_id=role_id,
|
||||
role_update=role_update,
|
||||
)
|
||||
return role
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{role_id}",
|
||||
responses={
|
||||
400: {"model": HTTPError, "description": "角色不为空,无法删除"},
|
||||
404: {"model": HTTPError, "description": "角色不存在"},
|
||||
},
|
||||
summary="删除角色",
|
||||
)
|
||||
async def delete_role(
|
||||
role_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(RoleConstant.ROLE_DELETE, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
删除角色
|
||||
|
||||
删除指定角色。如果角色已被分配给用户,则无法删除。
|
||||
|
||||
Args:
|
||||
role_id: 角色ID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
dict: 删除成功响应消息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当角色被用户使用时抛出400错误,角色不存在时抛出404错误
|
||||
"""
|
||||
await RoleService.delete_role(
|
||||
role_id=role_id,
|
||||
session=session,
|
||||
)
|
||||
return {"message": "角色删除成功", "code": 200}
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{role_id}/assign-to-user/{user_id}",
|
||||
summary="将角色分配给用户",
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "角色或用户不存在"},
|
||||
},
|
||||
)
|
||||
async def assign_role_to_user(
|
||||
role_id: int,
|
||||
user_id: UUID,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(RoleConstant.ROLE_BIND, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
将角色分配给用户
|
||||
|
||||
将指定角色分配给指定用户。
|
||||
|
||||
Args:
|
||||
role_id: 角色ID
|
||||
user_id: 用户UUID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
dict: 分配成功响应消息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当角色或用户不存在时抛出404错误
|
||||
"""
|
||||
# 检查角色是否存在
|
||||
role = await RoleService.get_role_by_id(session, role_id)
|
||||
if not role:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=Detail(
|
||||
message="角色不存在", code=status.HTTP_404_NOT_FOUND
|
||||
).model_dump(),
|
||||
)
|
||||
|
||||
# 检查用户是否存在
|
||||
from ...service.user import UserService
|
||||
user = await UserService.get_user_by_id(user_id, session)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=Detail(
|
||||
message="用户不存在", code=status.HTTP_404_NOT_FOUND
|
||||
).model_dump(),
|
||||
)
|
||||
|
||||
await RoleService.assign_role_to_user(
|
||||
session=session,
|
||||
user_id=user_id,
|
||||
role_id=role_id,
|
||||
)
|
||||
return {"message": "角色分配成功", "code": 200}
|
||||
|
||||
|
||||
@router.post(
|
||||
"/{role_id}/unbind-from-user/{user_id}",
|
||||
summary="从用户解除角色绑定",
|
||||
responses={
|
||||
404: {"model": HTTPError, "description": "用户不存在"},
|
||||
},
|
||||
)
|
||||
async def unbind_role_from_user(
|
||||
role_id: int,
|
||||
user_id: UUID,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(RoleConstant.ROLE_UNBIND, require_superuser=True)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
从用户解除角色绑定
|
||||
|
||||
将指定角色从指定用户中移除。
|
||||
|
||||
Args:
|
||||
role_id: 角色ID
|
||||
user_id: 用户UUID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
dict: 解绑成功响应消息
|
||||
|
||||
Raises:
|
||||
HTTPException: 当用户不存在时抛出404错误
|
||||
"""
|
||||
await RoleService.unbind_role_from_user(
|
||||
session=session,
|
||||
user_id=user_id,
|
||||
role_id=role_id,
|
||||
)
|
||||
return {"message": "角色解除绑定成功", "code": 200}
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{role_id}/permissions",
|
||||
response_model=list,
|
||||
summary="获取角色的权限列表",
|
||||
)
|
||||
async def get_role_permissions(
|
||||
role_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(RoleConstant.ROLE_READ)),
|
||||
__: TokenData = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
获取指定角色的所有权限
|
||||
|
||||
Args:
|
||||
role_id: 角色ID
|
||||
session: 数据库会话对象
|
||||
current_user: 当前登录用户
|
||||
|
||||
Returns:
|
||||
list: 权限对象列表
|
||||
"""
|
||||
permissions = await PermissionService.get_permissions_by_role(
|
||||
session=session,
|
||||
role_id=role_id,
|
||||
)
|
||||
return list(permissions)
|
||||
|
||||
@@ -5,8 +5,9 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ...model.todo import TodoCreate, TodoUpdate, TodoRead
|
||||
from ...core.database import get_session
|
||||
from ...constant.permission import TodoConstant
|
||||
from ...service.todo import TodoService
|
||||
from ..deps import get_current_user
|
||||
from ..deps import get_current_user, PermissionChecker
|
||||
|
||||
|
||||
router = APIRouter(dependencies=[Depends(get_current_user)])
|
||||
@@ -17,6 +18,7 @@ router = APIRouter(dependencies=[Depends(get_current_user)])
|
||||
async def create_todo(
|
||||
todo: TodoCreate,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(TodoConstant.TODO_CREATE)),
|
||||
):
|
||||
"""
|
||||
创建新的待办事项
|
||||
@@ -33,7 +35,10 @@ async def create_todo(
|
||||
|
||||
@router.get("/", response_model=List[TodoRead])
|
||||
async def read_todos(
|
||||
offset: int = 0, limit: int = 100, session: AsyncSession = Depends(get_session)
|
||||
offset: int = 0,
|
||||
limit: int = 100,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(TodoConstant.TODO_READ)),
|
||||
):
|
||||
"""
|
||||
获取待办事项列表
|
||||
@@ -50,7 +55,11 @@ async def read_todos(
|
||||
|
||||
|
||||
@router.get("/{todo_id}", response_model=TodoRead)
|
||||
async def read_todo(todo_id: int, session: AsyncSession = Depends(get_session)):
|
||||
async def read_todo(
|
||||
todo_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(TodoConstant.TODO_READ)),
|
||||
):
|
||||
"""
|
||||
根据ID获取单个待办事项
|
||||
|
||||
@@ -72,7 +81,10 @@ async def read_todo(todo_id: int, session: AsyncSession = Depends(get_session)):
|
||||
|
||||
@router.patch("/{todo_id}", response_model=TodoRead)
|
||||
async def update_todo(
|
||||
todo_id: int, todo_update: TodoUpdate, session: AsyncSession = Depends(get_session)
|
||||
todo_id: int,
|
||||
todo_update: TodoUpdate,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(TodoConstant.TODO_UPDATE)),
|
||||
):
|
||||
"""
|
||||
更新待办事项信息
|
||||
@@ -90,7 +102,11 @@ async def update_todo(
|
||||
|
||||
|
||||
@router.delete("/{todo_id}")
|
||||
async def delete_todo(todo_id: int, session: AsyncSession = Depends(get_session)):
|
||||
async def delete_todo(
|
||||
todo_id: int,
|
||||
session: AsyncSession = Depends(get_session),
|
||||
_: bool = Depends(PermissionChecker(TodoConstant.TODO_DELETE)),
|
||||
):
|
||||
"""
|
||||
删除待办事项
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
from .personnel import PersonnelConstant
|
||||
from .role import RoleConstant
|
||||
from .permission_mgt import PermissionMgtConstant
|
||||
from .system_config import SystemConfigConstant
|
||||
from .todo import TodoConstant
|
||||
|
||||
|
||||
class PermissionConstant:
|
||||
"""
|
||||
权限常量类 - 按业务模块分组管理所有系统权限
|
||||
|
||||
该类定义了系统中各个业务模块的权限标识符,采用分层结构组织:
|
||||
- 每个业务域作为一个内部类(Personnel, Role, Permission, SystemConfig, Todo)
|
||||
- 每个权限使用 "资源:操作" 格式定义
|
||||
|
||||
使用示例:
|
||||
>>> PermissionConstant.Personnel.PERSONNEL_CREATE
|
||||
'personnel:create'
|
||||
>>> PermissionConstant.get_all()
|
||||
['personnel:create', 'personnel:read', ...]
|
||||
"""
|
||||
|
||||
Personnel = PersonnelConstant
|
||||
PREFIX_PERSONNEL = "personnel"
|
||||
Role = RoleConstant
|
||||
PREFIX_ROLE = "role"
|
||||
Permission = PermissionMgtConstant
|
||||
PREFIX_PERMISSION = "permission"
|
||||
SystemConfig = SystemConfigConstant
|
||||
PREFIX_SYSTEM_CONFIG = "system_config"
|
||||
PREFIX_AUDIT_LOG = "audit_log"
|
||||
PREFIX_BACKUP = "backup"
|
||||
Todo = TodoConstant
|
||||
PREFIX_TODO = "todo"
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
"""
|
||||
获取系统中定义的所有权限字符串列表
|
||||
|
||||
通过反射机制自动收集所有内部类中定义的权限常量。
|
||||
|
||||
Returns:
|
||||
list[str]: 所有权限字符串的列表
|
||||
"""
|
||||
perms = []
|
||||
for attr_name in dir(cls):
|
||||
attr = getattr(cls, attr_name)
|
||||
if attr_name.startswith("_") or not hasattr(attr, "__dict__"):
|
||||
continue
|
||||
# 遍历内部类的属性
|
||||
for sub_attr in dir(attr):
|
||||
if sub_attr.startswith("_"):
|
||||
continue
|
||||
val = getattr(attr, sub_attr)
|
||||
if isinstance(val, str) and ":" in val:
|
||||
perms.append(val)
|
||||
return perms
|
||||
|
||||
@classmethod
|
||||
def get_description_map(cls) -> dict:
|
||||
"""
|
||||
获取系统中所有权限的描述映射
|
||||
|
||||
Returns:
|
||||
dict[str, str]: 权限字符串到描述的映射字典
|
||||
"""
|
||||
description_map = {}
|
||||
for attr_name in dir(cls):
|
||||
attr = getattr(cls, attr_name)
|
||||
if attr_name.startswith("_") or not hasattr(attr, "__dict__"):
|
||||
continue
|
||||
description_dict = getattr(attr, "permission_description_dict", None)
|
||||
if isinstance(description_dict, dict):
|
||||
description_map.update(description_dict)
|
||||
return description_map
|
||||
@@ -0,0 +1,18 @@
|
||||
class PermissionMgtConstant:
|
||||
"""权限管理权限常量"""
|
||||
|
||||
PERMISSION_CREATE = "permission:create" # 创建权限
|
||||
PERMISSION_UPDATE = "permission:update" # 修改权限
|
||||
PERMISSION_DELETE = "permission:delete" # 删除权限
|
||||
PERMISSION_READ = "permission:read" # 读取权限
|
||||
PERMISSION_BIND = "permission:bind" # 分配权限
|
||||
PERMISSION_UNBIND = "permission:unbind" # 解绑权限
|
||||
|
||||
permission_description_dict = {
|
||||
PERMISSION_CREATE: "创建权限",
|
||||
PERMISSION_UPDATE: "修改权限",
|
||||
PERMISSION_DELETE: "删除权限",
|
||||
PERMISSION_READ: "读取权限",
|
||||
PERMISSION_BIND: "分配权限",
|
||||
PERMISSION_UNBIND: "解绑权限",
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
class PersonnelConstant:
|
||||
"""人员管理权限常量"""
|
||||
|
||||
PERSONNEL_CREATE = "personnel:create" # 创建人员
|
||||
PERSONNEL_READ = "personnel:read" # 读取人员
|
||||
PERSONNEL_UPDATE = "personnel:update" # 更新人员
|
||||
PERSONNEL_DELETE = "personnel:delete" # 删除人员
|
||||
|
||||
permission_description_dict = {
|
||||
PERSONNEL_CREATE: "创建人员",
|
||||
PERSONNEL_READ: "读取人员",
|
||||
PERSONNEL_UPDATE: "更新人员",
|
||||
PERSONNEL_DELETE: "删除人员",
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
class RoleConstant:
|
||||
"""角色管理权限常量"""
|
||||
|
||||
ROLE_CREATE = "role:create" # 创建角色
|
||||
ROLE_READ = "role:read" # 读取角色
|
||||
ROLE_UPDATE = "role:update" # 更新角色
|
||||
ROLE_DELETE = "role:delete" # 删除角色
|
||||
ROLE_BIND = "role:assign" # 分配角色
|
||||
ROLE_UNBIND = "role:unbind" # 解绑角色
|
||||
|
||||
permission_description_dict = {
|
||||
ROLE_CREATE: "创建角色",
|
||||
ROLE_READ: "读取角色",
|
||||
ROLE_UPDATE: "更新角色",
|
||||
ROLE_DELETE: "删除角色",
|
||||
ROLE_BIND: "分配角色",
|
||||
ROLE_UNBIND: "解绑角色",
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
class SystemConfigConstant:
|
||||
"""系统配置、审计日志与备份恢复权限常量"""
|
||||
|
||||
CONFIG_READ = "system_config:read" # 读取系统配置
|
||||
CONFIG_UPDATE = "system_config:update" # 更新系统配置
|
||||
AUDIT_LOG_READ = "audit_log:read" # 读取审计日志
|
||||
BACKUP_CREATE = "backup:create" # 创建备份
|
||||
BACKUP_RESTORE = "backup:restore" # 恢复备份
|
||||
|
||||
permission_description_dict = {
|
||||
CONFIG_READ: "读取系统配置",
|
||||
CONFIG_UPDATE: "更新系统配置",
|
||||
AUDIT_LOG_READ: "读取审计日志",
|
||||
BACKUP_CREATE: "创建备份",
|
||||
BACKUP_RESTORE: "恢复备份",
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
class TodoConstant:
|
||||
"""Todo任务管理权限常量"""
|
||||
|
||||
TODO_CREATE = "todo:create" # 创建待办事项
|
||||
TODO_READ = "todo:read" # 读取待办事项
|
||||
TODO_UPDATE = "todo:update" # 更新待办事项
|
||||
TODO_DELETE = "todo:delete" # 删除待办事项
|
||||
|
||||
permission_description_dict = {
|
||||
TODO_CREATE: "创建待办事项",
|
||||
TODO_READ: "读取待办事项",
|
||||
TODO_UPDATE: "更新待办事项",
|
||||
TODO_DELETE: "删除待办事项",
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Any, Optional
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import diskcache
|
||||
|
||||
@@ -347,7 +347,7 @@ def get_cache() -> Cache:
|
||||
# 装饰器:缓存函数结果
|
||||
def cached(ttl: Optional[int] = None):
|
||||
"""
|
||||
函数结果缓存装饰器
|
||||
函数结果缓存装饰器(同步版本)
|
||||
|
||||
自动缓存函数的返回值,相同参数的调用直接从缓存返回,
|
||||
避免重复计算。适用于纯函数或副作用小的函数。
|
||||
@@ -363,30 +363,76 @@ def cached(ttl: Optional[int] = None):
|
||||
... def get_user(user_id: int):
|
||||
... return {"id": user_id, "name": "test"}
|
||||
>>>
|
||||
>>> # 第一次调用执行函数并缓存
|
||||
>>> get_user(1)
|
||||
>>> # 60秒内再次调用直接返回缓存结果
|
||||
>>> get_user(1)
|
||||
|
||||
Note:
|
||||
- 缓存键由函数名 + 位置参数 + 关键字参数组成
|
||||
- 参数必须是可哈希的,否则会导致错误
|
||||
- 不适合缓存大型对象或频繁变化的数据
|
||||
- 不同参数组合会生成不同的缓存键
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
# 生成缓存key
|
||||
key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
|
||||
cache = get_cache()
|
||||
# 尝试从缓存获取
|
||||
result = cache.get(key)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
# 执行函数
|
||||
result = func(*args, **kwargs)
|
||||
cache.set(key, result, ex=ttl)
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
# 装饰器:异步函数结果缓存
|
||||
def acached(ttl: Optional[int] = None, key_func: Optional[Callable] = None):
|
||||
"""
|
||||
函数结果缓存装饰器(异步版本)
|
||||
|
||||
自动缓存异步函数的返回值,相同参数的调用直接从缓存返回。
|
||||
支持自定义缓存键生成函数,以便排除 `session` 等无法序列化
|
||||
或不应作为缓存键一部分的参数。
|
||||
|
||||
Args:
|
||||
ttl: 缓存生存时间(秒),None 表示永不过期
|
||||
key_func: 自定义缓存键生成函数,接收与原始函数相同的参数
|
||||
(args, kwargs),返回字符串作为缓存键。
|
||||
默认使用 f"{func.__name__}:{str(args)}:{str(kwargs)}"
|
||||
|
||||
Returns:
|
||||
callable: 装饰器函数
|
||||
|
||||
Example:
|
||||
>>> @acached(ttl=120, key_func=lambda cls, session, user_id: f"user_perms:{user_id}")
|
||||
... async def get_permissions_by_user(cls, session, user_id):
|
||||
... return await some_db_query(session, user_id)
|
||||
>>>
|
||||
>>> # 需要手动清除缓存时:
|
||||
>>> cache = get_cache()
|
||||
>>> cache.delete("user_perms:xxx-uuid-xxx")
|
||||
|
||||
Note:
|
||||
- 装饰器应放在 @classmethod(如果有的话)下方
|
||||
- 使用 key_func 可以精确控制缓存键,避免 session 等对象影响
|
||||
- 缓存失效需通过 cache.delete(key) 手动操作
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
async def wrapper(*args, **kwargs):
|
||||
# 生成缓存key
|
||||
if key_func is not None:
|
||||
key = key_func(*args, **kwargs)
|
||||
else:
|
||||
key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
|
||||
|
||||
cache = get_cache()
|
||||
# 尝试从缓存获取
|
||||
result = cache.get(key)
|
||||
if result is not None:
|
||||
print(f"缓存输出: {result}")
|
||||
return result
|
||||
|
||||
# 执行异步函数
|
||||
result = await func(*args, **kwargs)
|
||||
|
||||
# 存入缓存
|
||||
cache.set(key, result, ex=ttl)
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
class PermissionConstant:
|
||||
"""
|
||||
权限常量类 - 按业务模块分组管理所有系统权限
|
||||
|
||||
该类定义了系统中各个业务模块的权限标识符,采用分层结构组织:
|
||||
- 每个业务域作为一个内部类(如 Sales, Purchase, Inventory 等)
|
||||
- 每个权限使用 "资源:操作" 格式定义
|
||||
- 支持 CRUD 操作及业务特定操作(如 approve, export 等)
|
||||
|
||||
使用示例:
|
||||
>>> Permission.Sales.ORDER_CREATE # 获取销售订单创建权限
|
||||
'sales_order:create'
|
||||
>>> Permission.get_all() # 获取所有权限列表
|
||||
['sales_order:create', 'sales_order:read', ...]
|
||||
"""
|
||||
|
||||
class Sales:
|
||||
"""
|
||||
销售域权限常量
|
||||
|
||||
包含销售相关的权限定义:
|
||||
- 销售订单管理(ORDER_*)
|
||||
- 销售出库管理(OUTBOUND_*)
|
||||
- 客户管理(CUSTOMER_*)
|
||||
|
||||
权限格式: sales_order:create, customer:export 等
|
||||
"""
|
||||
|
||||
ORDER_CREATE = "sales_order:create" # 创建销售订单
|
||||
ORDER_READ = "sales_order:read" # 读取销售订单
|
||||
ORDER_UPDATE = "sales_order:update" # 修改销售订单
|
||||
ORDER_DELETE = "sales_order:delete" # 删除销售订单
|
||||
|
||||
OUTBOUND_CREATE = "sales_outbound:create" # 创建销售出库
|
||||
OUTBOUND_READ = "sales_outbound:read" # 读取销售出库
|
||||
OUTBOUND_UPDATE = "sales_outbound:update" # 修改销售出库
|
||||
OUTBOUND_DELETE = "sales_outbound:delete" # 删除销售出库
|
||||
OUTBOUND_APPROVE = "sales_outbound:approve" # 审批销售出库
|
||||
|
||||
CUSTOMER_CREATE = "customer:create" # 创建客户
|
||||
CUSTOMER_READ = "customer:read" # 读取客户
|
||||
CUSTOMER_UPDATE = "customer:update" # 修改客户
|
||||
CUSTOMER_DELETE = "customer:delete" # 删除客户
|
||||
CUSTOMER_EXPORT = "customer:export" # 导出客户
|
||||
|
||||
class Purchase:
|
||||
"""
|
||||
采购域权限常量
|
||||
|
||||
包含采购相关的权限定义:
|
||||
- 采购订单管理(ORDER_*)
|
||||
- 采购入库管理(INBOUND_*),包含审批和取消操作
|
||||
- 供应商管理(SUPPLIER_*)
|
||||
|
||||
权限格式: purchase_order:create, supplier:update 等
|
||||
"""
|
||||
|
||||
ORDER_CREATE = "purchase_order:create" # 创建采购订单
|
||||
ORDER_READ = "purchase_order:read" # 读取采购订单
|
||||
ORDER_UPDATE = "purchase_order:update" # 更新采购订单
|
||||
ORDER_DELETE = "purchase_order:delete" # 删除采购订单
|
||||
|
||||
INBOUND_CREATE = "purchase_inbound:create" # 创建采购入库
|
||||
INBOUND_READ = "purchase_inbound:read" # 读取采购入库
|
||||
INBOUND_UPDATE = "purchase_inbound:update" # 更新采购入库
|
||||
INBOUND_DELETE = "purchase_inbound:delete" # 删除采购入库
|
||||
INBOUND_APPROVE = "purchase_inbound:approve" # 审批采购入库
|
||||
INBOUND_CANCEL = "purchase_inbound:cancel" # 取消采购入库
|
||||
|
||||
SUPPLIER_CREATE = "supplier:create" # 创建供应商
|
||||
SUPPLIER_READ = "supplier:read" # 读取供应商
|
||||
SUPPLIER_UPDATE = "supplier:update" # 更新供应商
|
||||
SUPPLIER_DELETE = "supplier:delete" # 删除供应商
|
||||
|
||||
class Inventory:
|
||||
"""
|
||||
库存域权限常量
|
||||
|
||||
包含库存管理相关的权限定义:
|
||||
- 库存管理(STOCK_*)
|
||||
- 仓库管理(WAREHOUSE_*)
|
||||
- 其他出入库单据(OTHER_INBOUND_*, OTHER_OUTBOUND_*)
|
||||
- 调拨单管理(TRANSFER_*)
|
||||
- 盘点单管理(STOCK_CHECK_*)
|
||||
|
||||
特点: 大部分单据支持审批流程(APPROVE 权限)
|
||||
权限格式: stock:read, transfer:approve 等
|
||||
"""
|
||||
|
||||
STOCK_CREATE = "stock:create" # 创建库存
|
||||
STOCK_READ = "stock:read" # 读取库存
|
||||
STOCK_UPDATE = "stock:update" # 更新库存
|
||||
STOCK_DELETE = "stock:delete" # 删除库存
|
||||
|
||||
WAREHOUSE_CREATE = "warehouse:create" # 创建仓库
|
||||
WAREHOUSE_READ = "warehouse:read" # 读取仓库
|
||||
WAREHOUSE_UPDATE = "warehouse:update" # 更新仓库
|
||||
WAREHOUSE_DELETE = "warehouse:delete" # 删除仓库
|
||||
|
||||
# 库存单据
|
||||
|
||||
OTHER_INBOUND_CREATE = "other_inbound:create" # 创建其他入库
|
||||
OTHER_INBOUND_READ = "other_inbound:read" # 读取其他入库
|
||||
OTHER_INBOUND_UPDATE = "other_inbound:update" # 更新其他入库
|
||||
OTHER_INBOUND_DELETE = "other_inbound:delete" # 删除其他入库
|
||||
OTHER_INBOUND_APPROVE = "other_inbound:approve" # 审核其他入库
|
||||
|
||||
OTHER_OUTBOUND_CREATE = "other_outbound:create" # 创建其他出库
|
||||
OTHER_OUTBOUND_READ = "other_outbound:read" # 读取其他出库
|
||||
OTHER_OUTBOUND_UPDATE = "other_outbound:update" # 更新其他出库
|
||||
OTHER_OUTBOUND_DELETE = "other_outbound:delete" # 删除其他出库
|
||||
OTHER_OUTBOUND_APPROVE = "other_outbound:approve" # 审核其他出库
|
||||
|
||||
TRANSFER_CREATE = "transfer:create" # 创建调拨
|
||||
TRANSFER_READ = "transfer:read" # 读取调拨
|
||||
TRANSFER_UPDATE = "transfer:update" # 更新调拨
|
||||
TRANSFER_DELETE = "transfer:delete" # 删除调拨
|
||||
TRANSFER_APPROVE = "transfer:approve" # 审核调拨
|
||||
|
||||
STOCK_CHECK_CREATE = "stock_check:create" # 创建盘点
|
||||
STOCK_CHECK_READ = "stock_check:read" # 读取盘点
|
||||
STOCK_CHECK_UPDATE = "stock_check:update" # 更新盘点
|
||||
STOCK_CHECK_DELETE = "stock_check:delete" # 删除盘点
|
||||
STOCK_CHECK_APPROVE = "stock_check:approve" # 审核盘点
|
||||
|
||||
class Product:
|
||||
"""
|
||||
产品主数据权限常量
|
||||
|
||||
包含产品和基础数据管理的权限定义:
|
||||
- 产品管理(PRODUCT_*),支持导入导出
|
||||
- 产品分类管理(CATEGORY_*)
|
||||
- 计量单位管理(UNIT_*)
|
||||
|
||||
权限格式: product:import, product_category:read 等
|
||||
"""
|
||||
|
||||
PRODUCT_CREATE = "product:create" # 创建产品
|
||||
PRODUCT_READ = "product:read" # 读取产品
|
||||
PRODUCT_UPDATE = "product:update" # 更新产品
|
||||
PRODUCT_DELETE = "product:delete" # 删除产品
|
||||
PRODUCT_IMPORT = "product:import" # 导入产品
|
||||
PRODUCT_EXPORT = "product:export" # 导出产品
|
||||
|
||||
CATEGORY_CREATE = "product_category:create" # 创建产品分类
|
||||
CATEGORY_READ = "product_category:read" # 读取产品分类
|
||||
CATEGORY_UPDATE = "product_category:update" # 更新产品分类
|
||||
CATEGORY_DELETE = "product_category:delete" # 删除产品分类
|
||||
|
||||
UNIT_CREATE = "unit:create" # 创建单位
|
||||
UNIT_READ = "unit:read" # 读取单位
|
||||
UNIT_UPDATE = "unit:update" # 更新单位
|
||||
UNIT_DELETE = "unit:delete" # 删除单位
|
||||
|
||||
class Finance:
|
||||
"""
|
||||
财务域权限常量
|
||||
|
||||
包含财务管理相关的权限定义:
|
||||
- 应收应付查询(RECEIVABLE_READ, PAYABLE_READ)
|
||||
- 付款管理(PAYMENT_*),包含审批流程
|
||||
- 发票管理(INVOICE_*)
|
||||
|
||||
注意: 财务域以只读权限为主,写操作需要审批
|
||||
权限格式: payment:approve, invoice:create 等
|
||||
"""
|
||||
|
||||
RECEIVABLE_READ = "receivable:read" # 读取应收应付
|
||||
PAYABLE_READ = "payable:read" # 读取应付
|
||||
PAYMENT_CREATE = "payment:create" # 创建付款
|
||||
PAYMENT_READ = "payment:read" # 读取付款
|
||||
PAYMENT_APPROVE = "payment:approve" # 审批付款
|
||||
INVOICE_CREATE = "invoice:create" # 创建发票
|
||||
INVOICE_READ = "invoice:read" # 读取发票
|
||||
|
||||
class Report:
|
||||
"""
|
||||
报表与分析权限常量
|
||||
|
||||
包含各类报表查看权限:
|
||||
- 销售报表(SALES_VIEW)
|
||||
- 采购报表(PURCHASE_VIEW)
|
||||
- 库存报表(STOCK_VIEW)
|
||||
- 财务报表(FINANCE_VIEW)
|
||||
- 仪表盘(DASHBOARD_VIEW)
|
||||
|
||||
特点: 所有权限均为只读查看权限
|
||||
权限格式: report:sales:view, dashboard:view 等
|
||||
"""
|
||||
|
||||
SALES_VIEW = "report_sales:view" # 销售报表
|
||||
PURCHASE_VIEW = "report_purchase:view" # 采购报表
|
||||
STOCK_VIEW = "report_stock:view" # 库存报表
|
||||
FINANCE_VIEW = "report_finance:view" # 财务报表
|
||||
DASHBOARD_VIEW = "dashboard:view" # 仪表盘
|
||||
|
||||
class System:
|
||||
"""
|
||||
系统管理权限常量
|
||||
|
||||
包含系统级管理功能的权限定义:
|
||||
- 人员管理(PERSONNEL_*)
|
||||
- 角色管理(ROLE_*),包含角色分配权限
|
||||
- 系统配置(CONFIG_*)
|
||||
- 审计日志(AUDIT_LOG_READ)
|
||||
- 数据备份恢复(BACKUP_*)
|
||||
|
||||
注意: 这些是最高权限级别的操作,应谨慎分配
|
||||
权限格式: role:assign, backup:restore 等
|
||||
"""
|
||||
|
||||
PERSONNEL_CREATE = "personnel:create" # 创建人员
|
||||
PERSONNEL_READ = "personnel:read" # 读取人员
|
||||
PERSONNEL_UPDATE = "personnel:update" # 更新人员
|
||||
PERSONNEL_DELETE = "personnel:delete" # 删除人员
|
||||
|
||||
ROLE_CREATE = "role:create" # 创建角色
|
||||
ROLE_READ = "role:read" # 读取角色
|
||||
ROLE_UPDATE = "role:update" # 更新角色
|
||||
ROLE_DELETE = "role:delete" # 删除角色
|
||||
ASSIGN_PERMISSION = "permission:assign" # 分配权限
|
||||
ASSIGN_ROLE = "role:assign" # 分配角色
|
||||
|
||||
CONFIG_READ = "system_config:read" # 读取系统配置
|
||||
CONFIG_UPDATE = "system_config:update" # 更新系统配置
|
||||
AUDIT_LOG_READ = "audit_log:read" # 读取审计日志
|
||||
BACKUP_CREATE = "backup:create" # 创建备份
|
||||
BACKUP_RESTORE = "backup:restore" # 恢复备份
|
||||
|
||||
@classmethod
|
||||
def get_all(cls):
|
||||
"""
|
||||
获取系统中定义的所有权限字符串列表
|
||||
|
||||
该方法通过反射机制自动收集所有内部类中定义的权限常量,
|
||||
用于数据库同步、权限初始化等场景。
|
||||
|
||||
工作流程:
|
||||
1. 遍历 Permission 类的所有属性
|
||||
2. 跳过私有属性和非类属性
|
||||
3. 对每个内部类,遍历其所有字符串类型的权限常量
|
||||
4. 过滤出符合 "resource:action" 格式的权限标识
|
||||
|
||||
Returns:
|
||||
list[str]: 所有权限字符串的列表,例如:
|
||||
[
|
||||
'sales_order:create',
|
||||
'sales_order:read',
|
||||
'customer:export',
|
||||
...
|
||||
]
|
||||
|
||||
使用示例:
|
||||
>>> all_perms = Permission.get_all()
|
||||
>>> len(all_perms) # 获取权限总数
|
||||
85
|
||||
>>> 'sales_order:create' in all_perms # 检查权限是否存在
|
||||
True
|
||||
|
||||
Note:
|
||||
- 该方法会自动排除以下划线开头的私有属性
|
||||
- 只收集包含冒号的字符串值,确保是有效的权限标识
|
||||
- 返回的列表顺序取决于 dir() 函数的返回顺序
|
||||
"""
|
||||
perms = []
|
||||
for attr_name in dir(cls):
|
||||
attr = getattr(cls, attr_name)
|
||||
if attr_name.startswith("_") or not hasattr(attr, "__dict__"):
|
||||
continue
|
||||
# 遍历内部类的属性
|
||||
for sub_attr in dir(attr):
|
||||
if sub_attr.startswith("_"):
|
||||
continue
|
||||
val = getattr(attr, sub_attr)
|
||||
if isinstance(val, str) and ":" in val:
|
||||
perms.append(val)
|
||||
return perms
|
||||
@@ -1,6 +1,6 @@
|
||||
from .user import User
|
||||
from .role import Role, RoleUpdate
|
||||
from .permission import Permission
|
||||
from .permission import Permission, PermissionUpdate, PermissionCreate
|
||||
from .relationship_table import UserRole, RolePermission
|
||||
from .todo import Todo
|
||||
|
||||
@@ -10,6 +10,8 @@ __all__ = [
|
||||
"Role",
|
||||
"RoleUpdate",
|
||||
"Permission",
|
||||
"PermissionUpdate",
|
||||
"PermissionCreate",
|
||||
"UserRole",
|
||||
"RolePermission",
|
||||
"Todo",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
|
||||
from pydantic import BaseModel
|
||||
from sqlmodel import SQLModel, Field, Relationship
|
||||
|
||||
from .relationship_table import RolePermission
|
||||
@@ -34,3 +35,39 @@ class Permission(SQLModel, table=True):
|
||||
roles: List["Role"] = Relationship(
|
||||
back_populates="permissions", link_model=RolePermission
|
||||
)
|
||||
|
||||
|
||||
class PermissionUpdate(BaseModel):
|
||||
"""
|
||||
权限更新请求模型类,用于接收权限更新时的参数。
|
||||
|
||||
所有字段均为可选,允许部分更新权限信息。
|
||||
|
||||
Attributes:
|
||||
name: 更新后的权限名称,可选字段
|
||||
description: 更新后的权限描述信息,可选字段
|
||||
resource: 更新后的资源名称,可选字段
|
||||
action: 更新后的操作类型,可选字段
|
||||
"""
|
||||
|
||||
name: Optional[str] = None
|
||||
description: Optional[str] = None
|
||||
resource: Optional[str] = None
|
||||
action: Optional[str] = None
|
||||
|
||||
|
||||
class PermissionCreate(BaseModel):
|
||||
"""
|
||||
权限创建请求模型类,用于接收权限创建时的参数。
|
||||
|
||||
Attributes:
|
||||
name: 权限名称,格式为"资源:操作"
|
||||
description: 权限描述信息,可选字段
|
||||
resource: 资源名称,如 user、order、product 等
|
||||
action: 操作类型,如 create、read、update、delete 等
|
||||
"""
|
||||
|
||||
name: str
|
||||
description: Optional[str] = None
|
||||
resource: str
|
||||
action: str
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
from typing import Optional
|
||||
from typing import Optional, List, Sequence
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import HTTPException
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlmodel import select
|
||||
|
||||
from ..model import Permission, RolePermission, Role, User
|
||||
from ..model import Permission, PermissionUpdate, RolePermission, Role, User
|
||||
from ..core.cache import acached, get_cache
|
||||
from .user import UserService
|
||||
from .role import RoleService
|
||||
|
||||
|
||||
# 用户权限缓存的 TTL(秒),权限变更后最多 2 分钟自动刷新
|
||||
_USER_PERMS_CACHE_TTL = 120
|
||||
|
||||
|
||||
class PermissionService:
|
||||
"""权限服务类,提供权限的创建、查询、分配和解除绑定等功能"""
|
||||
|
||||
@@ -69,6 +74,189 @@ class PermissionService:
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
@classmethod
|
||||
async def list_permissions(
|
||||
cls,
|
||||
session: AsyncSession,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
resource: Optional[str] = None,
|
||||
) -> Sequence[Permission]:
|
||||
"""
|
||||
获取权限列表,支持分页和按资源筛选
|
||||
|
||||
:param session: 数据库会话对象
|
||||
:param skip: 跳过的记录数,用于分页
|
||||
:param limit: 返回的最大记录数,用于分页
|
||||
:param resource: 资源名称筛选条件,可选
|
||||
:return: 权限对象列表
|
||||
"""
|
||||
query = select(Permission)
|
||||
|
||||
if resource:
|
||||
query = query.where(Permission.resource == resource)
|
||||
|
||||
query = query.offset(skip).limit(limit)
|
||||
result = await session.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
@classmethod
|
||||
async def update_permission(
|
||||
cls,
|
||||
session: AsyncSession,
|
||||
permission_id: int,
|
||||
permission_update: PermissionUpdate,
|
||||
) -> Permission:
|
||||
"""
|
||||
更新权限信息
|
||||
|
||||
:param session: 数据库会话对象
|
||||
:param permission_id: 权限ID
|
||||
:param permission_update: 权限更新数据
|
||||
:return: 更新后的权限对象
|
||||
:raises HTTPException: 当权限不存在时抛出404错误
|
||||
"""
|
||||
permission = await cls.get_permission_by_id(session, permission_id)
|
||||
if not permission:
|
||||
raise HTTPException(status_code=404, detail="Permission not found")
|
||||
|
||||
# 只更新提供的字段
|
||||
update_data = permission_update.model_dump(exclude_unset=True)
|
||||
for field, value in update_data.items():
|
||||
setattr(permission, field, value)
|
||||
|
||||
await session.commit()
|
||||
await session.refresh(permission)
|
||||
return permission
|
||||
|
||||
@classmethod
|
||||
async def delete_permission(
|
||||
cls,
|
||||
session: AsyncSession,
|
||||
permission_id: int,
|
||||
) -> bool:
|
||||
"""
|
||||
删除权限
|
||||
|
||||
:param session: 数据库会话对象
|
||||
:param permission_id: 权限ID
|
||||
:return: 删除成功返回True
|
||||
:raises HTTPException: 当权限不存在或正在被使用时抛出对应错误
|
||||
"""
|
||||
# 检查权限是否被角色使用
|
||||
result = await session.execute(
|
||||
select(RolePermission).where(
|
||||
RolePermission.permission_id == permission_id
|
||||
)
|
||||
)
|
||||
if result.scalars().all():
|
||||
raise HTTPException(
|
||||
status_code=400, detail="Permission is assigned to roles, cannot delete"
|
||||
)
|
||||
|
||||
permission = await cls.get_permission_by_id(session, permission_id)
|
||||
if not permission:
|
||||
raise HTTPException(status_code=404, detail="Permission not found")
|
||||
|
||||
await session.delete(permission)
|
||||
await session.commit()
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
async def get_permissions_by_role(
|
||||
cls,
|
||||
session: AsyncSession,
|
||||
role_id: int,
|
||||
) -> Sequence[Permission]:
|
||||
"""
|
||||
获取指定角色的所有权限
|
||||
|
||||
使用 JOIN 查询直接从数据库获取,避免在异步上下文中触发懒加载。
|
||||
|
||||
:param session: 数据库会话对象
|
||||
:param role_id: 角色ID
|
||||
:return: 权限对象列表
|
||||
:raises HTTPException: 当角色不存在时抛出404错误
|
||||
"""
|
||||
from ..model.relationship_table import RolePermission as RolePermTB
|
||||
|
||||
stmt = (
|
||||
select(Permission)
|
||||
.join(RolePermTB, RolePermTB.permission_id == Permission.id) # type: ignore
|
||||
.where(RolePermTB.role_id == role_id)
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
permissions = result.scalars().all()
|
||||
|
||||
if not permissions:
|
||||
# 确认角色是否存在
|
||||
role = await RoleService.get_role_by_id(session, role_id)
|
||||
if not role:
|
||||
raise HTTPException(status_code=404, detail="Role not found")
|
||||
|
||||
return permissions
|
||||
|
||||
# ---- TTL 缓存辅助方法 ----
|
||||
|
||||
@classmethod
|
||||
def _get_user_perms_cache_key(cls, user_id: UUID) -> str:
|
||||
"""生成用户权限缓存的键"""
|
||||
return f"user_perms:{user_id}"
|
||||
|
||||
@classmethod
|
||||
def _invalidate_user_perms_cache(cls, user_id: UUID):
|
||||
"""
|
||||
清除指定用户的权限缓存
|
||||
|
||||
:param user_id: 用户UUID
|
||||
"""
|
||||
cache = get_cache()
|
||||
cache_key = cls._get_user_perms_cache_key(user_id)
|
||||
cache.delete(cache_key)
|
||||
|
||||
# ---- 核心业务方法 ----
|
||||
|
||||
@classmethod
|
||||
@acached(ttl=_USER_PERMS_CACHE_TTL, key_func=lambda cls, session, user_id: f"user_perms:{user_id}")
|
||||
async def get_permissions_by_user(
|
||||
cls,
|
||||
session: AsyncSession,
|
||||
user_id: UUID,
|
||||
) -> List[Permission]:
|
||||
"""
|
||||
获取指定用户的所有权限(通过用户的所有角色汇总)
|
||||
|
||||
使用 @acached 装饰器自动缓存查询结果,缓存键为 user_perms:{user_id}。
|
||||
TTL 120 秒作为兜底机制。当用户权限发生变更时,主动调用
|
||||
_invalidate_user_perms_cache() 清除缓存。
|
||||
|
||||
:param session: 数据库会话对象
|
||||
:param user_id: 用户UUID
|
||||
:return: 去重后的权限对象列表
|
||||
:raises HTTPException: 当用户不存在时抛出404错误
|
||||
"""
|
||||
# 通过 role_permission 和 user_role 关联表进行 JOIN 查询
|
||||
from ..model.relationship_table import UserRole as UserRoleTB, RolePermission as RolePermTB
|
||||
|
||||
stmt = (
|
||||
select(Permission)
|
||||
.join(RolePermTB, RolePermTB.permission_id == Permission.id) # type: ignore
|
||||
.join(Role, Role.id == RolePermTB.role_id) # type: ignore
|
||||
.join(UserRoleTB, UserRoleTB.role_id == Role.id) # type: ignore
|
||||
.where(UserRoleTB.user_id == user_id)
|
||||
.distinct()
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
permissions = result.scalars().all()
|
||||
|
||||
if not permissions:
|
||||
# 确认用户是否存在
|
||||
user = await UserService.get_user_by_id(user_id, session)
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
return list(permissions)
|
||||
|
||||
@classmethod
|
||||
async def assign_permission_to_role(
|
||||
cls, session: AsyncSession, role_id: int, permission_id: int
|
||||
@@ -80,7 +268,26 @@ class PermissionService:
|
||||
:param role_id: 角色ID
|
||||
:param permission_id: 权限ID
|
||||
:return: 创建的角色权限关联对象
|
||||
:raises HTTPException: 当角色或权限不存在,或权限已分配给角色时抛出错误
|
||||
"""
|
||||
role = await RoleService.get_role_by_id(session, role_id)
|
||||
if not role:
|
||||
raise HTTPException(status_code=404, detail="Role not found")
|
||||
|
||||
permission = await cls.get_permission_by_id(session, permission_id)
|
||||
if not permission:
|
||||
raise HTTPException(status_code=404, detail="Permission not found")
|
||||
|
||||
# 检查是否已经分配
|
||||
existing = await session.execute(
|
||||
select(RolePermission).where(
|
||||
RolePermission.role_id == role_id,
|
||||
RolePermission.permission_id == permission_id,
|
||||
)
|
||||
)
|
||||
if existing.scalar_one_or_none():
|
||||
raise HTTPException(status_code=400, detail="Permission already assigned to role")
|
||||
|
||||
role_permission = RolePermission(role_id=role_id, permission_id=permission_id)
|
||||
session.add(role_permission)
|
||||
await session.commit()
|
||||
@@ -133,6 +340,8 @@ class PermissionService:
|
||||
user.roles.append(role)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
# 清除该用户的权限缓存,下次查询时重新加载
|
||||
cls._invalidate_user_perms_cache(user_id)
|
||||
return user
|
||||
|
||||
@classmethod
|
||||
@@ -142,6 +351,8 @@ class PermissionService:
|
||||
"""
|
||||
从角色中解除权限绑定
|
||||
|
||||
直接通过 RolePermission 关联表删除记录,避免在异步上下文中触发懒加载。
|
||||
|
||||
:param session: 数据库会话对象
|
||||
:param role_id: 角色ID
|
||||
:param permission_id: 权限ID
|
||||
@@ -153,8 +364,15 @@ class PermissionService:
|
||||
if role is None:
|
||||
raise HTTPException(status_code=404, detail="Role not found")
|
||||
|
||||
# 从角色的权限列表中移除指定权限
|
||||
role.permissions = [p for p in role.permissions if p.id != permission_id]
|
||||
# 通过关联表删除记录
|
||||
from ..model.relationship_table import RolePermission as RolePermTB
|
||||
from sqlmodel import delete
|
||||
|
||||
stmt = delete(RolePermTB).where(
|
||||
RolePermTB.role_id == role_id, # type: ignore
|
||||
RolePermTB.permission_id == permission_id, # type: ignore
|
||||
)
|
||||
await session.execute(stmt)
|
||||
await session.commit()
|
||||
await session.refresh(role)
|
||||
return role
|
||||
@@ -166,7 +384,7 @@ class PermissionService:
|
||||
"""
|
||||
从用户中解除权限绑定
|
||||
|
||||
通过找到用户的专属角色,从该角色中移除指定权限
|
||||
通过找到用户的专属角色,从该角色中移除指定权限。
|
||||
|
||||
:param session: 数据库会话对象
|
||||
:param user_id: 用户UUID
|
||||
@@ -183,11 +401,17 @@ class PermissionService:
|
||||
if role is None:
|
||||
raise HTTPException(status_code=404, detail="Role not found")
|
||||
|
||||
# 从角色权限列表中移除指定权限
|
||||
role.permissions = [
|
||||
permission
|
||||
for permission in role.permissions
|
||||
if permission.id != permission_id
|
||||
]
|
||||
if role.id is not None:
|
||||
# 直接通过关联表删除记录
|
||||
from ..model.relationship_table import RolePermission as RolePermTB
|
||||
from sqlmodel import delete
|
||||
|
||||
stmt = delete(RolePermTB).where(
|
||||
RolePermTB.role_id == role.id, # type: ignore
|
||||
RolePermTB.permission_id == permission_id, # type: ignore
|
||||
)
|
||||
await session.execute(stmt)
|
||||
await session.commit()
|
||||
await session.refresh(role)
|
||||
# 清除该用户的权限缓存
|
||||
cls._invalidate_user_perms_cache(user_id)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Optional
|
||||
from typing import Optional, Sequence
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import HTTPException
|
||||
@@ -89,6 +89,53 @@ class RoleService:
|
||||
result = await session.get(Role, role_id)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
async def list_roles(
|
||||
cls,
|
||||
session: AsyncSession,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
) -> Sequence[Role]:
|
||||
"""
|
||||
获取角色列表,支持分页
|
||||
|
||||
Args:
|
||||
session: 数据库会话对象
|
||||
skip: 跳过的记录数,用于分页
|
||||
limit: 返回的最大记录数,用于分页
|
||||
|
||||
Returns:
|
||||
Sequence[Role]: 角色对象列表
|
||||
"""
|
||||
query = select(Role).offset(skip).limit(limit)
|
||||
result = await session.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
@classmethod
|
||||
async def get_roles_by_user(
|
||||
cls,
|
||||
session: AsyncSession,
|
||||
user_id: UUID,
|
||||
) -> Sequence[Role]:
|
||||
"""
|
||||
获取指定用户的所有角色
|
||||
|
||||
Args:
|
||||
session: 数据库会话对象
|
||||
user_id: 用户UUID
|
||||
|
||||
Returns:
|
||||
Sequence[Role]: 用户关联的角色对象列表
|
||||
|
||||
Raises:
|
||||
HTTPException: 当用户不存在时抛出404错误
|
||||
"""
|
||||
user = await UserService.get_user_by_id(user_id, session)
|
||||
if not user:
|
||||
raise HTTPException(status_code=404, detail="User not found")
|
||||
|
||||
return user.roles
|
||||
|
||||
@staticmethod
|
||||
async def unbind_role_from_user(
|
||||
session: AsyncSession,
|
||||
@@ -103,9 +150,6 @@ class RoleService:
|
||||
user_id: 用户UUID
|
||||
role_id: 角色ID
|
||||
|
||||
Returns:
|
||||
User: 更新后的用户对象
|
||||
|
||||
Raises:
|
||||
HTTPException: 当用户不存在时抛出404错误
|
||||
"""
|
||||
@@ -117,7 +161,6 @@ class RoleService:
|
||||
user.roles = [role for role in user.roles if role.id != role_id]
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
async def delete_role(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
import time
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -60,7 +61,7 @@ class UserService:
|
||||
IntegrityError: 当邮箱已存在时可能抛出完整性错误
|
||||
"""
|
||||
hashed = get_password_hash(password)
|
||||
user = User(email=email, hashed_password=hashed, **kwargs)
|
||||
user = User(email=email, hashed_password=hashed, created_at=str(time.time() * 1000), **kwargs)
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
await session.refresh(user)
|
||||
|
||||
@@ -1,13 +1,69 @@
|
||||
from sqlalchemy.orm import selectinload
|
||||
from sqlmodel import select
|
||||
|
||||
from ..core.logger import get_logger
|
||||
from ..core.constant import PermissionConstant
|
||||
from ..constant.permission import PermissionConstant
|
||||
from ..service.user import UserService
|
||||
from ..service.role import RoleService
|
||||
from ..service.permission import PermissionService
|
||||
from ..model.role import Role
|
||||
from ..model.user import User
|
||||
|
||||
from plugins.scheduler.scheduler import get_task_service_instance
|
||||
|
||||
|
||||
logger = get_logger("scheduler")
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 默认角色定义
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
# 角色名称常量
|
||||
ROLE_ADMIN = "admin" # 超级管理员 - 拥有所有权限
|
||||
ROLE_MANAGER = "manager" # 经理 - 拥有业务模块完整操作权限
|
||||
ROLE_OPERATOR = "operator" # 操作员 - 拥有业务模块基本操作权限
|
||||
ROLE_VIEWER = "viewer" # 访客 - 仅有读取权限
|
||||
|
||||
|
||||
async def init_permission(session):
|
||||
"""
|
||||
初始化系统权限数据
|
||||
|
||||
遍历所有预定义的权限常量,检查数据库中是否已存在对应权限记录。
|
||||
对于不存在的权限,创建新的权限记录并关联对应的资源和操作类型,
|
||||
同时使用 permission_description_dict 中的描述信息设置 description 字段。
|
||||
|
||||
Args:
|
||||
session: 数据库会话对象,用于执行数据库操作
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Note:
|
||||
- 权限格式为"resource:action",例如"user:create"
|
||||
- 使用冒号分隔资源和操作,如果缺少操作部分则默认为空字符串
|
||||
- 已存在的权限会被跳过,避免重复创建
|
||||
- 每个新创建的权限都会记录到日志中
|
||||
- 描述信息从 PermissionConstant.get_description_map() 获取
|
||||
"""
|
||||
description_map = PermissionConstant.get_description_map()
|
||||
for permission in PermissionConstant.get_all():
|
||||
parts = permission.split(":", 1)
|
||||
resource = parts[0]
|
||||
action = parts[1] if len(parts) > 1 else ""
|
||||
|
||||
existing_permission = await PermissionService.get_permission_by_name(
|
||||
session, permission
|
||||
)
|
||||
if existing_permission:
|
||||
logger.debug(f"权限已存在: {permission}")
|
||||
continue
|
||||
|
||||
description = description_map.get(permission, None)
|
||||
await PermissionService.create_permission(
|
||||
session, permission, resource, action, description=description
|
||||
)
|
||||
logger.info(f"创建权限: {permission} - {description}")
|
||||
|
||||
async def dev_init_admin(session):
|
||||
"""
|
||||
@@ -27,53 +83,242 @@ async def dev_init_admin(session):
|
||||
- 如果管理员已存在,则跳过创建并记录日志
|
||||
- 新创建的用户具有超级管理员权限(is_superuser=True)
|
||||
"""
|
||||
admin_user = await UserService.get_user_by_email("admin", session)
|
||||
if admin_user: # type: ignore
|
||||
logger.info("管理员用户已存在") # type: ignore
|
||||
# 先检查 admin@qq.com 邮箱是否已被使用
|
||||
admin_user = await UserService.get_user_by_email("admin@qq.com", session)
|
||||
if admin_user:
|
||||
logger.info("管理员用户已存在")
|
||||
return
|
||||
await UserService.create_user(session, "admin", "admin", is_superuser=True) # type: ignore
|
||||
# 再检查 "admin" 邮箱(兼容旧版初始化逻辑)
|
||||
admin_user = await UserService.get_user_by_email("admin", session)
|
||||
if admin_user:
|
||||
logger.info("管理员用户已存在")
|
||||
return
|
||||
user = await UserService.create_user(session, "admin@qq.com", "admin", is_superuser=True)
|
||||
admin_role = await RoleService.get_role_by_name(session, ROLE_ADMIN)
|
||||
if admin_role is None or admin_role.id is None:
|
||||
raise ValueError("管理员未创建")
|
||||
await RoleService.assign_role_to_user(session, user.id, admin_role.id)
|
||||
|
||||
|
||||
async def init_permission(session):
|
||||
def _get_admin_permissions() -> list:
|
||||
"""
|
||||
初始化系统权限数据
|
||||
获取管理员角色的所有权限 - 包含系统中定义的全部权限。
|
||||
|
||||
遍历所有预定义的权限常量,检查数据库中是否已存在对应权限记录。
|
||||
对于不存在的权限,创建新的权限记录并关联对应的资源和操作类型。
|
||||
Returns:
|
||||
list[str]: 所有权限名称列表
|
||||
"""
|
||||
return PermissionConstant.get_all()
|
||||
|
||||
|
||||
def _get_manager_permissions() -> list:
|
||||
"""
|
||||
获取经理角色的权限列表。
|
||||
|
||||
经理拥有所有业务模块的完整操作权限(创建、读取、更新、删除、审批),
|
||||
但不包含系统管理权限(人员、角色、权限配置、系统配置、审计日志、备份恢复)。
|
||||
|
||||
Returns:
|
||||
list[str]: 权限名称列表
|
||||
"""
|
||||
# 系统管理权限 - 经理不包含
|
||||
exclude_prefixes = {
|
||||
PermissionConstant.PREFIX_PERMISSION,
|
||||
PermissionConstant.PREFIX_ROLE,
|
||||
PermissionConstant.PREFIX_PERMISSION,
|
||||
PermissionConstant.PREFIX_SYSTEM_CONFIG,
|
||||
PermissionConstant.PREFIX_AUDIT_LOG,
|
||||
PermissionConstant.PREFIX_BACKUP,
|
||||
}
|
||||
|
||||
result = []
|
||||
for p in PermissionConstant.get_all():
|
||||
prefix = p.split(":")[0] if ":" in p else p
|
||||
# 审计日志特殊处理
|
||||
if prefix.startswith("report_"):
|
||||
# 报表权限保留
|
||||
result.append(p)
|
||||
continue
|
||||
if prefix not in exclude_prefixes:
|
||||
result.append(p)
|
||||
return result
|
||||
|
||||
|
||||
def _get_operator_permissions() -> list:
|
||||
"""
|
||||
获取操作员角色的权限列表。
|
||||
|
||||
操作员拥有业务模块的基本操作权限(创建、读取、更新),
|
||||
不包含删除、审批、导入导出等敏感操作,也不包含系统管理权限。
|
||||
|
||||
Returns:
|
||||
list[str]: 权限名称列表
|
||||
"""
|
||||
# 操作员排除的操作类型
|
||||
exclude_actions = {"delete", "approve", "cancel", "import", "export"}
|
||||
# 操作员排除的资源前缀
|
||||
exclude_prefixes = {
|
||||
PermissionConstant.PREFIX_PERSONNEL,
|
||||
PermissionConstant.PREFIX_ROLE,
|
||||
PermissionConstant.PREFIX_PERMISSION,
|
||||
PermissionConstant.PREFIX_SYSTEM_CONFIG,
|
||||
PermissionConstant.PREFIX_AUDIT_LOG,
|
||||
PermissionConstant.PREFIX_BACKUP,
|
||||
}
|
||||
|
||||
result = []
|
||||
for p in PermissionConstant.get_all():
|
||||
prefix = p.split(":")[0] if ":" in p else p
|
||||
action = p.split(":")[1] if ":" in p else ""
|
||||
|
||||
# 排除指定资源前缀
|
||||
if prefix in exclude_prefixes:
|
||||
continue
|
||||
|
||||
# 报表只保留查看
|
||||
if prefix.startswith("report_") or prefix.startswith("dashboard"):
|
||||
if action == "view":
|
||||
result.append(p)
|
||||
continue
|
||||
|
||||
# 排除敏感操作
|
||||
if action in exclude_actions:
|
||||
continue
|
||||
|
||||
result.append(p)
|
||||
return result
|
||||
|
||||
|
||||
def _get_viewer_permissions() -> list:
|
||||
"""
|
||||
获取访客角色的权限列表。
|
||||
|
||||
访客只有最基本的读取和查看权限,没有任何写入、修改或删除权限。
|
||||
|
||||
Returns:
|
||||
list[str]: 权限名称列表
|
||||
"""
|
||||
read_actions = {"read", "view"}
|
||||
read_prefixes = {
|
||||
PermissionConstant.PREFIX_TODO
|
||||
}
|
||||
|
||||
result = []
|
||||
for p in PermissionConstant.get_all():
|
||||
prefix = p.split(":")[0] if ":" in p else p
|
||||
action = p.split(":")[1] if ":" in p else ""
|
||||
|
||||
# 只读取指定资源的 read/view 权限
|
||||
if prefix in read_prefixes and action in read_actions:
|
||||
result.append(p)
|
||||
return result
|
||||
|
||||
|
||||
# 角色-权限映射表
|
||||
ROLE_PERMISSION_MAP = {
|
||||
ROLE_ADMIN: _get_admin_permissions,
|
||||
ROLE_MANAGER: _get_manager_permissions,
|
||||
ROLE_OPERATOR: _get_operator_permissions,
|
||||
ROLE_VIEWER: _get_viewer_permissions,
|
||||
}
|
||||
|
||||
ROLE_DESCRIPTION_MAP = {
|
||||
ROLE_ADMIN: "超级管理员,拥有系统所有权限",
|
||||
ROLE_MANAGER: "业务经理,拥有所有业务模块的完整操作权限",
|
||||
ROLE_OPERATOR: "操作员,拥有业务模块的基本操作权限",
|
||||
ROLE_VIEWER: "访客,仅有只读访问权限",
|
||||
}
|
||||
|
||||
|
||||
async def init_default_roles(session):
|
||||
"""
|
||||
初始化默认角色及权限关联
|
||||
|
||||
创建系统预定义的四个默认角色(admin、manager、operator、viewer),
|
||||
并为每个角色分配对应的权限集。如果角色已存在则跳过创建,但会补充缺失的权限关联。
|
||||
|
||||
Args:
|
||||
session: 数据库会话对象,用于执行数据库操作
|
||||
session: 数据库会话对象
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
Note:
|
||||
- 权限格式为"resource:action",例如"user:create"
|
||||
- 使用冒号分隔资源和操作,如果缺少操作部分则默认为空字符串
|
||||
- 已存在的权限会被跳过,避免重复创建
|
||||
- 每个新创建的权限都会记录到日志中
|
||||
"""
|
||||
for permission in PermissionConstant.get_all():
|
||||
parts = permission.split(":", 1)
|
||||
resource = parts[0]
|
||||
action = parts[1] if len(parts) > 1 else ""
|
||||
|
||||
existing_permission = await PermissionService.get_permission_by_name(
|
||||
session, permission
|
||||
for role_name, get_permissions_fn in ROLE_PERMISSION_MAP.items():
|
||||
# 检查角色是否已存在(主动加载 permissions 关系,避免异步懒加载问题)
|
||||
result = await session.execute(
|
||||
select(Role)
|
||||
.where(Role.name == role_name)
|
||||
.options(selectinload(Role.permissions)) # type: ignore[arg-type]
|
||||
)
|
||||
if existing_permission:
|
||||
logger.debug(f"权限已存在: {permission}")
|
||||
existing_role = result.scalar_one_or_none()
|
||||
|
||||
if existing_role:
|
||||
logger.info(f"角色已存在: {role_name}")
|
||||
role = existing_role
|
||||
else:
|
||||
# 创建角色
|
||||
description = ROLE_DESCRIPTION_MAP.get(role_name, "")
|
||||
role = await RoleService.create_role(
|
||||
session, role_name, description=description
|
||||
)
|
||||
# 创建后重新查询以加载 permissions 关系
|
||||
result = await session.execute(
|
||||
select(Role)
|
||||
.where(Role.name == role_name)
|
||||
.options(selectinload(Role.permissions)) # type: ignore[arg-type]
|
||||
)
|
||||
role = result.scalar_one_or_none()
|
||||
logger.info(f"创建角色: {role_name}")
|
||||
|
||||
# 获取该角色应有的权限名称列表
|
||||
expected_permission_names = get_permissions_fn()
|
||||
|
||||
# 获取角色当前已关联的权限名称集合
|
||||
existing_permission_names = {
|
||||
p.name for p in role.permissions
|
||||
} if role else set()
|
||||
|
||||
# 分配缺失的权限
|
||||
for perm_name in expected_permission_names:
|
||||
if perm_name in existing_permission_names:
|
||||
continue
|
||||
|
||||
await PermissionService.create_permission(session, permission, resource, action)
|
||||
logger.info(f"创建权限: {permission}")
|
||||
permission = await PermissionService.get_permission_by_name(
|
||||
session, perm_name
|
||||
)
|
||||
if permission is None:
|
||||
logger.warning(f"权限不存在,跳过: {perm_name}")
|
||||
continue
|
||||
|
||||
if role.id is not None and permission.id is not None:
|
||||
await PermissionService.assign_permission_to_role(
|
||||
session, role.id, permission.id
|
||||
)
|
||||
logger.debug(f"为角色 '{role_name}' 分配权限: {perm_name}")
|
||||
|
||||
# 将 admin 角色分配给超级管理员用户(主动加载 roles 关系)
|
||||
result = await session.execute(
|
||||
select(User).where(User.email == "admin@qq.com").options(selectinload(User.roles)) # type: ignore[arg-type]
|
||||
)
|
||||
admin_user = result.scalar_one_or_none()
|
||||
|
||||
admin_role = await RoleService.get_role_by_name(session, ROLE_ADMIN)
|
||||
if admin_user and admin_role and admin_role.id is not None:
|
||||
# 检查是否已关联
|
||||
already_assigned = any(
|
||||
r.id == admin_role.id for r in admin_user.roles
|
||||
)
|
||||
if not already_assigned:
|
||||
await RoleService.assign_role_to_user(
|
||||
session, admin_user.id, admin_role.id
|
||||
)
|
||||
logger.info("为 admin 用户分配 admin 角色")
|
||||
|
||||
|
||||
async def init_db():
|
||||
"""
|
||||
执行数据库初始化流程
|
||||
|
||||
协调执行所有数据库初始化任务,包括管理员账户创建和权限数据初始化。
|
||||
协调执行所有数据库初始化任务,包括管理员账户创建、权限数据初始化和默认角色初始化。
|
||||
该函数是系统启动时数据库初始化的入口点。
|
||||
|
||||
Returns:
|
||||
@@ -82,12 +327,16 @@ async def init_db():
|
||||
Note:
|
||||
- 通过任务服务获取异步会话工厂
|
||||
- 如果会话工厂未初始化,则直接返回不执行任何操作
|
||||
- 按顺序执行:先初始化管理员,再初始化权限
|
||||
- 按顺序执行:先初始化权限,再初始化角色,最后初始化管理员
|
||||
- 使用异步上下文管理器确保会话正确关闭
|
||||
"""
|
||||
task_service = get_task_service_instance()
|
||||
if task_service.async_session_maker is None:
|
||||
return
|
||||
async with task_service.async_session_maker() as session:
|
||||
await dev_init_admin(session)
|
||||
# 先初始化权限数据(角色依赖权限存在)
|
||||
await init_permission(session)
|
||||
# 再初始化默认角色及权限关联
|
||||
await init_default_roles(session)
|
||||
# 最后初始化管理员用户
|
||||
await dev_init_admin(session)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import json
|
||||
import threading
|
||||
import hashlib
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, Dict, Any, List
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass, asdict
|
||||
import hashlib
|
||||
import threading
|
||||
|
||||
from pydantic import BaseModel
|
||||
import jwt
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user