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 && \
|
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/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY build/chongming /tmp/chongming
|
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 { type Client, formDataBodySerializer, type Options as Options2, type TDataShape } from './client';
|
||||||
import { client } from './client.gen';
|
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> & {
|
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
|
* 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>({
|
export const readTodosApiV1TodosGet = <ThrowOnError extends boolean = false>(options?: Options<ReadTodosApiV1TodosGetData, ThrowOnError>) => (options?.client ?? client).get<ReadTodosApiV1TodosGetResponses, ReadTodosApiV1TodosGetErrors, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
security: [{ scheme: 'bearer', type: 'http' }],
|
||||||
@@ -29,6 +39,15 @@ export const readTodosApiV1TodosGet = <ThrowOnError extends boolean = false>(opt
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create Todo
|
* 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>({
|
export const createTodoApiV1TodosPost = <ThrowOnError extends boolean = false>(options: Options<CreateTodoApiV1TodosPostData, ThrowOnError>) => (options.client ?? client).post<CreateTodoApiV1TodosPostResponses, CreateTodoApiV1TodosPostErrors, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
security: [{ scheme: 'bearer', type: 'http' }],
|
||||||
@@ -42,6 +61,18 @@ export const createTodoApiV1TodosPost = <ThrowOnError extends boolean = false>(o
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete Todo
|
* 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>({
|
export const deleteTodoApiV1TodosTodoIdDelete = <ThrowOnError extends boolean = false>(options: Options<DeleteTodoApiV1TodosTodoIdDeleteData, ThrowOnError>) => (options.client ?? client).delete<DeleteTodoApiV1TodosTodoIdDeleteResponses, DeleteTodoApiV1TodosTodoIdDeleteErrors, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
security: [{ scheme: 'bearer', type: 'http' }],
|
||||||
@@ -51,6 +82,18 @@ export const deleteTodoApiV1TodosTodoIdDelete = <ThrowOnError extends boolean =
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Read Todo
|
* 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>({
|
export const readTodoApiV1TodosTodoIdGet = <ThrowOnError extends boolean = false>(options: Options<ReadTodoApiV1TodosTodoIdGetData, ThrowOnError>) => (options.client ?? client).get<ReadTodoApiV1TodosTodoIdGetResponses, ReadTodoApiV1TodosTodoIdGetErrors, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
security: [{ scheme: 'bearer', type: 'http' }],
|
||||||
@@ -60,6 +103,16 @@ export const readTodoApiV1TodosTodoIdGet = <ThrowOnError extends boolean = false
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Update Todo
|
* 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>({
|
export const updateTodoApiV1TodosTodoIdPatch = <ThrowOnError extends boolean = false>(options: Options<UpdateTodoApiV1TodosTodoIdPatchData, ThrowOnError>) => (options.client ?? client).patch<UpdateTodoApiV1TodosTodoIdPatchResponses, UpdateTodoApiV1TodosTodoIdPatchErrors, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
security: [{ scheme: 'bearer', type: 'http' }],
|
||||||
@@ -73,6 +126,20 @@ export const updateTodoApiV1TodosTodoIdPatch = <ThrowOnError extends boolean = f
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Register
|
* 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>({
|
export const registerApiV1AuthRegisterPost = <ThrowOnError extends boolean = false>(options: Options<RegisterApiV1AuthRegisterPostData, ThrowOnError>) => (options.client ?? client).post<RegisterApiV1AuthRegisterPostResponses, RegisterApiV1AuthRegisterPostErrors, ThrowOnError>({
|
||||||
url: '/api/v1/auth/register',
|
url: '/api/v1/auth/register',
|
||||||
@@ -85,6 +152,24 @@ export const registerApiV1AuthRegisterPost = <ThrowOnError extends boolean = fal
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Login
|
* 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>({
|
export const loginApiV1AuthLoginPost = <ThrowOnError extends boolean = false>(options: Options<LoginApiV1AuthLoginPostData, ThrowOnError>) => (options.client ?? client).post<LoginApiV1AuthLoginPostResponses, LoginApiV1AuthLoginPostErrors, ThrowOnError>({
|
||||||
url: '/api/v1/auth/login',
|
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>({
|
export const refreshTokenApiV1AuthRefreshPost = <ThrowOnError extends boolean = false>(options?: Options<RefreshTokenApiV1AuthRefreshPostData, ThrowOnError>) => (options?.client ?? client).post<RefreshTokenApiV1AuthRefreshPostResponses, RefreshTokenApiV1AuthRefreshPostErrors, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
security: [{ scheme: 'bearer', type: 'http' }],
|
||||||
@@ -106,6 +206,16 @@ export const refreshTokenApiV1AuthRefreshPost = <ThrowOnError extends boolean =
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Logout
|
* 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>({
|
export const logoutApiV1AuthLogoutPost = <ThrowOnError extends boolean = false>(options?: Options<LogoutApiV1AuthLogoutPostData, ThrowOnError>) => (options?.client ?? client).post<LogoutApiV1AuthLogoutPostResponses, unknown, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
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>({
|
export const logoutAllApiV1AuthLogoutAllPost = <ThrowOnError extends boolean = false>(options?: Options<LogoutAllApiV1AuthLogoutAllPostData, ThrowOnError>) => (options?.client ?? client).post<LogoutAllApiV1AuthLogoutAllPostResponses, unknown, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
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>({
|
export const getSessionsApiV1AuthSessionsGet = <ThrowOnError extends boolean = false>(options?: Options<GetSessionsApiV1AuthSessionsGetData, ThrowOnError>) => (options?.client ?? client).get<GetSessionsApiV1AuthSessionsGetResponses, unknown, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
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>({
|
export const getCurrentUserInfoApiV1AuthMeGet = <ThrowOnError extends boolean = false>(options?: Options<GetCurrentUserInfoApiV1AuthMeGetData, ThrowOnError>) => (options?.client ?? client).get<GetCurrentUserInfoApiV1AuthMeGetResponses, GetCurrentUserInfoApiV1AuthMeGetErrors, ThrowOnError>({
|
||||||
security: [{ scheme: 'bearer', type: 'http' }],
|
security: [{ scheme: 'bearer', type: 'http' }],
|
||||||
@@ -146,6 +286,30 @@ export const getCurrentUserInfoApiV1AuthMeGet = <ThrowOnError extends boolean =
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload Image
|
* 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>({
|
export const uploadImageApiV1UploadImagePost = <ThrowOnError extends boolean = false>(options: Options<UploadImageApiV1UploadImagePostData, ThrowOnError>) => (options.client ?? client).post<UploadImageApiV1UploadImagePostResponses, UploadImageApiV1UploadImagePostErrors, ThrowOnError>({
|
||||||
...formDataBodySerializer,
|
...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 });
|
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 fastapi import APIRouter
|
||||||
|
|
||||||
from .routers import todo, auth, upload
|
from .routers import todo, auth, upload, permission, role
|
||||||
|
|
||||||
|
|
||||||
# 创建主路由
|
# 创建主路由
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
@@ -13,6 +14,8 @@ api_router = APIRouter()
|
|||||||
api_router.include_router(todo.router, prefix="/todos", tags=["todo"])
|
api_router.include_router(todo.router, prefix="/todos", tags=["todo"])
|
||||||
api_router.include_router(auth.router, prefix="/auth", tags=["authentication"])
|
api_router.include_router(auth.router, prefix="/auth", tags=["authentication"])
|
||||||
api_router.include_router(upload.router, prefix="/upload", tags=["upload"])
|
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 import Depends, HTTPException, status
|
||||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from ..service.auth import get_auth_service
|
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
|
from plugins.jwt.jwt_cache import TokenData
|
||||||
|
|
||||||
|
|
||||||
@@ -40,3 +47,177 @@ async def get_current_user(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return token_data
|
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 ...model.todo import TodoCreate, TodoUpdate, TodoRead
|
||||||
from ...core.database import get_session
|
from ...core.database import get_session
|
||||||
|
from ...constant.permission import TodoConstant
|
||||||
from ...service.todo import TodoService
|
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)])
|
router = APIRouter(dependencies=[Depends(get_current_user)])
|
||||||
@@ -17,6 +18,7 @@ router = APIRouter(dependencies=[Depends(get_current_user)])
|
|||||||
async def create_todo(
|
async def create_todo(
|
||||||
todo: TodoCreate,
|
todo: TodoCreate,
|
||||||
session: AsyncSession = Depends(get_session),
|
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])
|
@router.get("/", response_model=List[TodoRead])
|
||||||
async def read_todos(
|
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)
|
@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获取单个待办事项
|
根据ID获取单个待办事项
|
||||||
|
|
||||||
@@ -72,7 +81,10 @@ async def read_todo(todo_id: int, session: AsyncSession = Depends(get_session)):
|
|||||||
|
|
||||||
@router.patch("/{todo_id}", response_model=TodoRead)
|
@router.patch("/{todo_id}", response_model=TodoRead)
|
||||||
async def update_todo(
|
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}")
|
@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
|
import time
|
||||||
from typing import Any, Optional
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
import diskcache
|
import diskcache
|
||||||
|
|
||||||
@@ -347,7 +347,7 @@ def get_cache() -> Cache:
|
|||||||
# 装饰器:缓存函数结果
|
# 装饰器:缓存函数结果
|
||||||
def cached(ttl: Optional[int] = None):
|
def cached(ttl: Optional[int] = None):
|
||||||
"""
|
"""
|
||||||
函数结果缓存装饰器
|
函数结果缓存装饰器(同步版本)
|
||||||
|
|
||||||
自动缓存函数的返回值,相同参数的调用直接从缓存返回,
|
自动缓存函数的返回值,相同参数的调用直接从缓存返回,
|
||||||
避免重复计算。适用于纯函数或副作用小的函数。
|
避免重复计算。适用于纯函数或副作用小的函数。
|
||||||
@@ -363,30 +363,76 @@ def cached(ttl: Optional[int] = None):
|
|||||||
... def get_user(user_id: int):
|
... def get_user(user_id: int):
|
||||||
... return {"id": user_id, "name": "test"}
|
... return {"id": user_id, "name": "test"}
|
||||||
>>>
|
>>>
|
||||||
>>> # 第一次调用执行函数并缓存
|
|
||||||
>>> get_user(1)
|
>>> get_user(1)
|
||||||
>>> # 60秒内再次调用直接返回缓存结果
|
|
||||||
>>> get_user(1)
|
|
||||||
|
|
||||||
Note:
|
|
||||||
- 缓存键由函数名 + 位置参数 + 关键字参数组成
|
|
||||||
- 参数必须是可哈希的,否则会导致错误
|
|
||||||
- 不适合缓存大型对象或频繁变化的数据
|
|
||||||
- 不同参数组合会生成不同的缓存键
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
# 生成缓存key
|
|
||||||
key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
|
key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
|
||||||
cache = get_cache()
|
cache = get_cache()
|
||||||
# 尝试从缓存获取
|
|
||||||
result = cache.get(key)
|
result = cache.get(key)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# 执行函数
|
|
||||||
result = func(*args, **kwargs)
|
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)
|
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 .user import User
|
||||||
from .role import Role, RoleUpdate
|
from .role import Role, RoleUpdate
|
||||||
from .permission import Permission
|
from .permission import Permission, PermissionUpdate, PermissionCreate
|
||||||
from .relationship_table import UserRole, RolePermission
|
from .relationship_table import UserRole, RolePermission
|
||||||
from .todo import Todo
|
from .todo import Todo
|
||||||
|
|
||||||
@@ -10,6 +10,8 @@ __all__ = [
|
|||||||
"Role",
|
"Role",
|
||||||
"RoleUpdate",
|
"RoleUpdate",
|
||||||
"Permission",
|
"Permission",
|
||||||
|
"PermissionUpdate",
|
||||||
|
"PermissionCreate",
|
||||||
"UserRole",
|
"UserRole",
|
||||||
"RolePermission",
|
"RolePermission",
|
||||||
"Todo",
|
"Todo",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import Optional, List, TYPE_CHECKING
|
from typing import Optional, List, TYPE_CHECKING
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
from sqlmodel import SQLModel, Field, Relationship
|
from sqlmodel import SQLModel, Field, Relationship
|
||||||
|
|
||||||
from .relationship_table import RolePermission
|
from .relationship_table import RolePermission
|
||||||
@@ -34,3 +35,39 @@ class Permission(SQLModel, table=True):
|
|||||||
roles: List["Role"] = Relationship(
|
roles: List["Role"] = Relationship(
|
||||||
back_populates="permissions", link_model=RolePermission
|
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 uuid import UUID
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlmodel import select
|
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 .user import UserService
|
||||||
from .role import RoleService
|
from .role import RoleService
|
||||||
|
|
||||||
|
|
||||||
|
# 用户权限缓存的 TTL(秒),权限变更后最多 2 分钟自动刷新
|
||||||
|
_USER_PERMS_CACHE_TTL = 120
|
||||||
|
|
||||||
|
|
||||||
class PermissionService:
|
class PermissionService:
|
||||||
"""权限服务类,提供权限的创建、查询、分配和解除绑定等功能"""
|
"""权限服务类,提供权限的创建、查询、分配和解除绑定等功能"""
|
||||||
|
|
||||||
@@ -69,6 +74,189 @@ class PermissionService:
|
|||||||
)
|
)
|
||||||
return result.scalar_one_or_none()
|
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
|
@classmethod
|
||||||
async def assign_permission_to_role(
|
async def assign_permission_to_role(
|
||||||
cls, session: AsyncSession, role_id: int, permission_id: int
|
cls, session: AsyncSession, role_id: int, permission_id: int
|
||||||
@@ -80,7 +268,26 @@ class PermissionService:
|
|||||||
:param role_id: 角色ID
|
:param role_id: 角色ID
|
||||||
:param permission_id: 权限ID
|
:param permission_id: 权限ID
|
||||||
:return: 创建的角色权限关联对象
|
: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)
|
role_permission = RolePermission(role_id=role_id, permission_id=permission_id)
|
||||||
session.add(role_permission)
|
session.add(role_permission)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
@@ -133,6 +340,8 @@ class PermissionService:
|
|||||||
user.roles.append(role)
|
user.roles.append(role)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(user)
|
await session.refresh(user)
|
||||||
|
# 清除该用户的权限缓存,下次查询时重新加载
|
||||||
|
cls._invalidate_user_perms_cache(user_id)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -142,6 +351,8 @@ class PermissionService:
|
|||||||
"""
|
"""
|
||||||
从角色中解除权限绑定
|
从角色中解除权限绑定
|
||||||
|
|
||||||
|
直接通过 RolePermission 关联表删除记录,避免在异步上下文中触发懒加载。
|
||||||
|
|
||||||
:param session: 数据库会话对象
|
:param session: 数据库会话对象
|
||||||
:param role_id: 角色ID
|
:param role_id: 角色ID
|
||||||
:param permission_id: 权限ID
|
:param permission_id: 权限ID
|
||||||
@@ -153,8 +364,15 @@ class PermissionService:
|
|||||||
if role is None:
|
if role is None:
|
||||||
raise HTTPException(status_code=404, detail="Role not found")
|
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.commit()
|
||||||
await session.refresh(role)
|
await session.refresh(role)
|
||||||
return role
|
return role
|
||||||
@@ -166,7 +384,7 @@ class PermissionService:
|
|||||||
"""
|
"""
|
||||||
从用户中解除权限绑定
|
从用户中解除权限绑定
|
||||||
|
|
||||||
通过找到用户的专属角色,从该角色中移除指定权限
|
通过找到用户的专属角色,从该角色中移除指定权限。
|
||||||
|
|
||||||
:param session: 数据库会话对象
|
:param session: 数据库会话对象
|
||||||
:param user_id: 用户UUID
|
:param user_id: 用户UUID
|
||||||
@@ -183,11 +401,17 @@ class PermissionService:
|
|||||||
if role is None:
|
if role is None:
|
||||||
raise HTTPException(status_code=404, detail="Role not found")
|
raise HTTPException(status_code=404, detail="Role not found")
|
||||||
|
|
||||||
# 从角色权限列表中移除指定权限
|
if role.id is not None:
|
||||||
role.permissions = [
|
# 直接通过关联表删除记录
|
||||||
permission
|
from ..model.relationship_table import RolePermission as RolePermTB
|
||||||
for permission in role.permissions
|
from sqlmodel import delete
|
||||||
if permission.id != permission_id
|
|
||||||
]
|
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.commit()
|
||||||
await session.refresh(role)
|
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 uuid import UUID
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
@@ -89,6 +89,53 @@ class RoleService:
|
|||||||
result = await session.get(Role, role_id)
|
result = await session.get(Role, role_id)
|
||||||
return result
|
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
|
@staticmethod
|
||||||
async def unbind_role_from_user(
|
async def unbind_role_from_user(
|
||||||
session: AsyncSession,
|
session: AsyncSession,
|
||||||
@@ -103,9 +150,6 @@ class RoleService:
|
|||||||
user_id: 用户UUID
|
user_id: 用户UUID
|
||||||
role_id: 角色ID
|
role_id: 角色ID
|
||||||
|
|
||||||
Returns:
|
|
||||||
User: 更新后的用户对象
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
HTTPException: 当用户不存在时抛出404错误
|
HTTPException: 当用户不存在时抛出404错误
|
||||||
"""
|
"""
|
||||||
@@ -117,7 +161,6 @@ class RoleService:
|
|||||||
user.roles = [role for role in user.roles if role.id != role_id]
|
user.roles = [role for role in user.roles if role.id != role_id]
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(user)
|
await session.refresh(user)
|
||||||
return user
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def delete_role(
|
async def delete_role(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
import time
|
||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
@@ -60,7 +61,7 @@ class UserService:
|
|||||||
IntegrityError: 当邮箱已存在时可能抛出完整性错误
|
IntegrityError: 当邮箱已存在时可能抛出完整性错误
|
||||||
"""
|
"""
|
||||||
hashed = get_password_hash(password)
|
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)
|
session.add(user)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
await session.refresh(user)
|
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.logger import get_logger
|
||||||
from ..core.constant import PermissionConstant
|
from ..constant.permission import PermissionConstant
|
||||||
from ..service.user import UserService
|
from ..service.user import UserService
|
||||||
|
from ..service.role import RoleService
|
||||||
from ..service.permission import PermissionService
|
from ..service.permission import PermissionService
|
||||||
|
from ..model.role import Role
|
||||||
|
from ..model.user import User
|
||||||
|
|
||||||
from plugins.scheduler.scheduler import get_task_service_instance
|
from plugins.scheduler.scheduler import get_task_service_instance
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger("scheduler")
|
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):
|
async def dev_init_admin(session):
|
||||||
"""
|
"""
|
||||||
@@ -27,53 +83,242 @@ async def dev_init_admin(session):
|
|||||||
- 如果管理员已存在,则跳过创建并记录日志
|
- 如果管理员已存在,则跳过创建并记录日志
|
||||||
- 新创建的用户具有超级管理员权限(is_superuser=True)
|
- 新创建的用户具有超级管理员权限(is_superuser=True)
|
||||||
"""
|
"""
|
||||||
admin_user = await UserService.get_user_by_email("admin", session)
|
# 先检查 admin@qq.com 邮箱是否已被使用
|
||||||
if admin_user: # type: ignore
|
admin_user = await UserService.get_user_by_email("admin@qq.com", session)
|
||||||
logger.info("管理员用户已存在") # type: ignore
|
if admin_user:
|
||||||
|
logger.info("管理员用户已存在")
|
||||||
return
|
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:
|
Args:
|
||||||
session: 数据库会话对象,用于执行数据库操作
|
session: 数据库会话对象
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
None
|
None
|
||||||
|
|
||||||
Note:
|
|
||||||
- 权限格式为"resource:action",例如"user:create"
|
|
||||||
- 使用冒号分隔资源和操作,如果缺少操作部分则默认为空字符串
|
|
||||||
- 已存在的权限会被跳过,避免重复创建
|
|
||||||
- 每个新创建的权限都会记录到日志中
|
|
||||||
"""
|
"""
|
||||||
for permission in PermissionConstant.get_all():
|
for role_name, get_permissions_fn in ROLE_PERMISSION_MAP.items():
|
||||||
parts = permission.split(":", 1)
|
# 检查角色是否已存在(主动加载 permissions 关系,避免异步懒加载问题)
|
||||||
resource = parts[0]
|
result = await session.execute(
|
||||||
action = parts[1] if len(parts) > 1 else ""
|
select(Role)
|
||||||
|
.where(Role.name == role_name)
|
||||||
existing_permission = await PermissionService.get_permission_by_name(
|
.options(selectinload(Role.permissions)) # type: ignore[arg-type]
|
||||||
session, permission
|
|
||||||
)
|
)
|
||||||
if existing_permission:
|
existing_role = result.scalar_one_or_none()
|
||||||
logger.debug(f"权限已存在: {permission}")
|
|
||||||
|
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
|
continue
|
||||||
|
|
||||||
await PermissionService.create_permission(session, permission, resource, action)
|
permission = await PermissionService.get_permission_by_name(
|
||||||
logger.info(f"创建权限: {permission}")
|
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():
|
async def init_db():
|
||||||
"""
|
"""
|
||||||
执行数据库初始化流程
|
执行数据库初始化流程
|
||||||
|
|
||||||
协调执行所有数据库初始化任务,包括管理员账户创建和权限数据初始化。
|
协调执行所有数据库初始化任务,包括管理员账户创建、权限数据初始化和默认角色初始化。
|
||||||
该函数是系统启动时数据库初始化的入口点。
|
该函数是系统启动时数据库初始化的入口点。
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -82,12 +327,16 @@ async def init_db():
|
|||||||
Note:
|
Note:
|
||||||
- 通过任务服务获取异步会话工厂
|
- 通过任务服务获取异步会话工厂
|
||||||
- 如果会话工厂未初始化,则直接返回不执行任何操作
|
- 如果会话工厂未初始化,则直接返回不执行任何操作
|
||||||
- 按顺序执行:先初始化管理员,再初始化权限
|
- 按顺序执行:先初始化权限,再初始化角色,最后初始化管理员
|
||||||
- 使用异步上下文管理器确保会话正确关闭
|
- 使用异步上下文管理器确保会话正确关闭
|
||||||
"""
|
"""
|
||||||
task_service = get_task_service_instance()
|
task_service = get_task_service_instance()
|
||||||
if task_service.async_session_maker is None:
|
if task_service.async_session_maker is None:
|
||||||
return
|
return
|
||||||
async with task_service.async_session_maker() as session:
|
async with task_service.async_session_maker() as session:
|
||||||
await dev_init_admin(session)
|
# 先初始化权限数据(角色依赖权限存在)
|
||||||
await init_permission(session)
|
await init_permission(session)
|
||||||
|
# 再初始化默认角色及权限关联
|
||||||
|
await init_default_roles(session)
|
||||||
|
# 最后初始化管理员用户
|
||||||
|
await dev_init_admin(session)
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import json
|
import json
|
||||||
|
import threading
|
||||||
|
import hashlib
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional, Dict, Any, List
|
from typing import Optional, Dict, Any, List
|
||||||
from pydantic import BaseModel
|
|
||||||
from dataclasses import dataclass, asdict
|
from dataclasses import dataclass, asdict
|
||||||
import hashlib
|
|
||||||
import threading
|
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
import jwt
|
import jwt
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user