Optimize routes to better align with RESTful (#673)

* Optimize routes to better align with RESTful

* Add codes endpoint description

* Update jinja templates

* fix typo

* fix sql
This commit is contained in:
Wu Clan
2025-06-19 11:06:34 +08:00
committed by GitHub
parent 0d1f05d307
commit 319ba13df1
74 changed files with 626 additions and 642 deletions
+1 -1
View File
@@ -8,4 +8,4 @@ from backend.app.admin.api.v1.auth.captcha import router as captcha_router
router = APIRouter(prefix='/auth') router = APIRouter(prefix='/auth')
router.include_router(auth_router, tags=['授权']) router.include_router(auth_router, tags=['授权'])
router.include_router(captcha_router, prefix='/captcha', tags=['验证码']) router.include_router(captcha_router, tags=['验证码'])
+13 -6
View File
@@ -11,14 +11,15 @@ from backend.app.admin.schema.token import GetLoginToken, GetNewToken, GetSwagge
from backend.app.admin.schema.user import AuthLoginParam from backend.app.admin.schema.user import AuthLoginParam
from backend.app.admin.service.auth_service import auth_service from backend.app.admin.service.auth_service import auth_service
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
from backend.common.security.jwt import DependsJwtAuth
router = APIRouter() router = APIRouter()
@router.post('/login/swagger', summary='swagger 调试专用', description='用于快捷获取 token 进行 swagger 认证') @router.post('/login/swagger', summary='swagger 调试专用', description='用于快捷获取 token 进行 swagger 认证')
async def swagger_login(obj: Annotated[HTTPBasicCredentials, Depends()]) -> GetSwaggerToken: async def login_swagger(obj: Annotated[HTTPBasicCredentials, Depends()]) -> GetSwaggerToken:
token, user = await auth_service.swagger_login(obj=obj) token, user = await auth_service.swagger_login(obj=obj)
return GetSwaggerToken(access_token=token, user=user) # type: ignore return GetSwaggerToken(access_token=token, user=user)
@router.post( @router.post(
@@ -27,20 +28,26 @@ async def swagger_login(obj: Annotated[HTTPBasicCredentials, Depends()]) -> GetS
description='json 格式登录, 仅支持在第三方api工具调试, 例如: postman', description='json 格式登录, 仅支持在第三方api工具调试, 例如: postman',
dependencies=[Depends(RateLimiter(times=5, minutes=1))], dependencies=[Depends(RateLimiter(times=5, minutes=1))],
) )
async def user_login( async def login(
request: Request, response: Response, obj: AuthLoginParam, background_tasks: BackgroundTasks request: Request, response: Response, obj: AuthLoginParam, background_tasks: BackgroundTasks
) -> ResponseSchemaModel[GetLoginToken]: ) -> ResponseSchemaModel[GetLoginToken]:
data = await auth_service.login(request=request, response=response, obj=obj, background_tasks=background_tasks) data = await auth_service.login(request=request, response=response, obj=obj, background_tasks=background_tasks)
return response_base.success(data=data) return response_base.success(data=data)
@router.post('/tokens/refresh', summary='刷新 token') @router.get('/codes', summary='获取所有授权码', description='适配 vben admin v5', dependencies=[DependsJwtAuth])
async def get_codes(request: Request) -> ResponseSchemaModel[list[str]]:
codes = await auth_service.get_codes(request=request)
return response_base.success(data=codes)
@router.post('/tokens', summary='刷新 token')
async def refresh_token(request: Request) -> ResponseSchemaModel[GetNewToken]: async def refresh_token(request: Request) -> ResponseSchemaModel[GetNewToken]:
data = await auth_service.new_token(request=request) data = await auth_service.refresh_token(request=request)
return response_base.success(data=data) return response_base.success(data=data)
@router.post('/logout', summary='用户登出') @router.post('/logout', summary='用户登出')
async def user_logout(request: Request, response: Response) -> ResponseModel: async def logout(request: Request, response: Response) -> ResponseModel:
await auth_service.logout(request=request, response=response) await auth_service.logout(request=request, response=response)
return response_base.success() return response_base.success()
+1 -1
View File
@@ -14,7 +14,7 @@ router = APIRouter()
@router.get( @router.get(
'', '/captcha',
summary='获取登录验证码', summary='获取登录验证码',
dependencies=[Depends(RateLimiter(times=5, seconds=10))], dependencies=[Depends(RateLimiter(times=5, seconds=10))],
) )
+5 -5
View File
@@ -4,7 +4,7 @@ from typing import Annotated
from fastapi import APIRouter, Depends, Query from fastapi import APIRouter, Depends, Query
from backend.app.admin.schema.login_log import GetLoginLogDetail from backend.app.admin.schema.login_log import DeleteLoginLogParam, GetLoginLogDetail
from backend.app.admin.service.login_log_service import login_log_service from backend.app.admin.service.login_log_service import login_log_service
from backend.common.pagination import DependsPagination, PageData, paging_data from backend.common.pagination import DependsPagination, PageData, paging_data
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
@@ -24,7 +24,7 @@ router = APIRouter()
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_login_logs( async def get_login_logs_paged(
db: CurrentSession, db: CurrentSession,
username: Annotated[str | None, Query(description='用户名')] = None, username: Annotated[str | None, Query(description='用户名')] = None,
status: Annotated[int | None, Query(description='状态')] = None, status: Annotated[int | None, Query(description='状态')] = None,
@@ -43,8 +43,8 @@ async def get_pagination_login_logs(
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_login_log(pk: Annotated[list[int], Query(description='登录日志 ID 列表')]) -> ResponseModel: async def delete_login_logs(obj: DeleteLoginLogParam) -> ResponseModel:
count = await login_log_service.delete(pk=pk) count = await login_log_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
@@ -54,7 +54,7 @@ async def delete_login_log(pk: Annotated[list[int], Query(description='登录日
'/all', '/all',
summary='清空登录日志', summary='清空登录日志',
dependencies=[ dependencies=[
Depends(RequestPermission('log:login:empty')), Depends(RequestPermission('log:login:clear')),
DependsRBAC, DependsRBAC,
], ],
) )
+5 -5
View File
@@ -4,7 +4,7 @@ from typing import Annotated
from fastapi import APIRouter, Depends, Query from fastapi import APIRouter, Depends, Query
from backend.app.admin.schema.opera_log import GetOperaLogDetail from backend.app.admin.schema.opera_log import DeleteOperaLogParam, GetOperaLogDetail
from backend.app.admin.service.opera_log_service import opera_log_service from backend.app.admin.service.opera_log_service import opera_log_service
from backend.common.pagination import DependsPagination, PageData, paging_data from backend.common.pagination import DependsPagination, PageData, paging_data
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
@@ -24,7 +24,7 @@ router = APIRouter()
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_opera_logs( async def get_opera_logs_paged(
db: CurrentSession, db: CurrentSession,
username: Annotated[str | None, Query(description='用户名')] = None, username: Annotated[str | None, Query(description='用户名')] = None,
status: Annotated[int | None, Query(description='状态')] = None, status: Annotated[int | None, Query(description='状态')] = None,
@@ -43,8 +43,8 @@ async def get_pagination_opera_logs(
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_opera_log(pk: Annotated[list[int], Query(description='操作日志 ID 列表')]) -> ResponseModel: async def delete_opera_logs(obj: DeleteOperaLogParam) -> ResponseModel:
count = await opera_log_service.delete(pk=pk) count = await opera_log_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
@@ -54,7 +54,7 @@ async def delete_opera_log(pk: Annotated[list[int], Query(description='操作日
'/all', '/all',
summary='清空操作日志', summary='清空操作日志',
dependencies=[ dependencies=[
Depends(RequestPermission('log:opera:empty')), Depends(RequestPermission('log:opera:clear')),
DependsRBAC, DependsRBAC,
], ],
) )
+1 -1
View File
@@ -10,4 +10,4 @@ router = APIRouter(prefix='/monitors')
router.include_router(redis_router, prefix='/redis', tags=['redis监控']) router.include_router(redis_router, prefix='/redis', tags=['redis监控'])
router.include_router(server_router, prefix='/server', tags=['服务器监控']) router.include_router(server_router, prefix='/server', tags=['服务器监控'])
router.include_router(token_router, prefix='/online', tags=['在线用户']) router.include_router(token_router, prefix='/sessions', tags=['会话监控'])
+4 -4
View File
@@ -19,7 +19,7 @@ router = APIRouter()
@router.get('', summary='获取在线用户', dependencies=[DependsJwtAuth]) @router.get('', summary='获取在线用户', dependencies=[DependsJwtAuth])
async def get_online( async def get_sessions(
username: Annotated[str | None, Query(description='用户名')] = None, username: Annotated[str | None, Query(description='用户名')] = None,
) -> ResponseSchemaModel[list[GetTokenDetail]]: ) -> ResponseSchemaModel[list[GetTokenDetail]]:
token_keys = await redis_client.keys(f'{settings.TOKEN_REDIS_PREFIX}:*') token_keys = await redis_client.keys(f'{settings.TOKEN_REDIS_PREFIX}:*')
@@ -75,13 +75,13 @@ async def get_online(
@router.delete( @router.delete(
'/{pk}', '/{pk}',
summary='下线', summary='强制下线',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:token:kick')), Depends(RequestPermission('sys:session:delete')),
DependsRBAC, DependsRBAC,
], ],
) )
async def kick_out( async def delete_session(
request: Request, request: Request,
pk: Annotated[int, Path(description='用户 ID')], pk: Annotated[int, Path(description='用户 ID')],
session_uuid: Annotated[str, Query(description='会话 UUID')], session_uuid: Annotated[str, Query(description='会话 UUID')],
+2 -2
View File
@@ -5,10 +5,10 @@ from fastapi import APIRouter
from backend.app.admin.api.v1.sys.data_rule import router as data_rule_router from backend.app.admin.api.v1.sys.data_rule import router as data_rule_router
from backend.app.admin.api.v1.sys.data_scope import router as data_scope_router from backend.app.admin.api.v1.sys.data_scope import router as data_scope_router
from backend.app.admin.api.v1.sys.dept import router as dept_router from backend.app.admin.api.v1.sys.dept import router as dept_router
from backend.app.admin.api.v1.sys.files import router as file_router
from backend.app.admin.api.v1.sys.menu import router as menu_router from backend.app.admin.api.v1.sys.menu import router as menu_router
from backend.app.admin.api.v1.sys.plugin import router as plugin_router from backend.app.admin.api.v1.sys.plugin import router as plugin_router
from backend.app.admin.api.v1.sys.role import router as role_router from backend.app.admin.api.v1.sys.role import router as role_router
from backend.app.admin.api.v1.sys.upload import router as upload_router
from backend.app.admin.api.v1.sys.user import router as user_router from backend.app.admin.api.v1.sys.user import router as user_router
router = APIRouter(prefix='/sys') router = APIRouter(prefix='/sys')
@@ -19,5 +19,5 @@ router.include_router(role_router, prefix='/roles', tags=['系统角色'])
router.include_router(user_router, prefix='/users', tags=['系统用户']) router.include_router(user_router, prefix='/users', tags=['系统用户'])
router.include_router(data_rule_router, prefix='/data-rules', tags=['系统数据规则']) router.include_router(data_rule_router, prefix='/data-rules', tags=['系统数据规则'])
router.include_router(data_scope_router, prefix='/data-scopes', tags=['系统数据范围']) router.include_router(data_scope_router, prefix='/data-scopes', tags=['系统数据范围'])
router.include_router(upload_router, prefix='/upload', tags=['系统上传']) router.include_router(file_router, prefix='/files', tags=['系统文件'])
router.include_router(plugin_router, prefix='/plugins', tags=['系统插件']) router.include_router(plugin_router, prefix='/plugins', tags=['系统插件'])
+4 -3
View File
@@ -6,6 +6,7 @@ from fastapi import APIRouter, Depends, Path, Query
from backend.app.admin.schema.data_rule import ( from backend.app.admin.schema.data_rule import (
CreateDataRuleParam, CreateDataRuleParam,
DeleteDataRuleParam,
GetDataRuleColumnDetail, GetDataRuleColumnDetail,
GetDataRuleDetail, GetDataRuleDetail,
UpdateDataRuleParam, UpdateDataRuleParam,
@@ -57,7 +58,7 @@ async def get_data_rule(
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_data_rules( async def get_data_rules_paged(
db: CurrentSession, name: Annotated[str | None, Query(description='规则名称')] = None db: CurrentSession, name: Annotated[str | None, Query(description='规则名称')] = None
) -> ResponseSchemaModel[PageData[GetDataRuleDetail]]: ) -> ResponseSchemaModel[PageData[GetDataRuleDetail]]:
data_rule_select = await data_rule_service.get_select(name=name) data_rule_select = await data_rule_service.get_select(name=name)
@@ -103,8 +104,8 @@ async def update_data_rule(
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_data_rule(pk: Annotated[list[int], Query(description='数据规则 ID 列表')]) -> ResponseModel: async def delete_data_rules(obj: DeleteDataRuleParam) -> ResponseModel:
count = await data_rule_service.delete(pk=pk) count = await data_rule_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
+4 -3
View File
@@ -6,6 +6,7 @@ from fastapi import APIRouter, Depends, Path, Query
from backend.app.admin.schema.data_scope import ( from backend.app.admin.schema.data_scope import (
CreateDataScopeParam, CreateDataScopeParam,
DeleteDataScopeParam,
GetDataScopeDetail, GetDataScopeDetail,
GetDataScopeWithRelationDetail, GetDataScopeWithRelationDetail,
UpdateDataScopeParam, UpdateDataScopeParam,
@@ -52,7 +53,7 @@ async def get_data_scope_rules(
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_data_scopes( async def get_data_scopes_paged(
db: CurrentSession, db: CurrentSession,
name: Annotated[str | None, Query(description='范围名称')] = None, name: Annotated[str | None, Query(description='范围名称')] = None,
status: Annotated[int | None, Query(description='状态')] = None, status: Annotated[int | None, Query(description='状态')] = None,
@@ -117,8 +118,8 @@ async def update_data_scope_rules(
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_data_scope(pk: Annotated[list[int], Query(description='数据范围 ID 列表')]) -> ResponseModel: async def delete_data_scopes(obj: DeleteDataScopeParam) -> ResponseModel:
count = await data_scope_service.delete(pk=pk) count = await data_scope_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
+3 -3
View File
@@ -20,15 +20,15 @@ async def get_dept(pk: Annotated[int, Path(description='部门 ID')]) -> Respons
return response_base.success(data=data) return response_base.success(data=data)
@router.get('', summary='获取所有部门展示', dependencies=[DependsJwtAuth]) @router.get('', summary='获取部门', dependencies=[DependsJwtAuth])
async def get_all_depts( async def get_dept_tree(
request: Request, request: Request,
name: Annotated[str | None, Query(description='部门名称')] = None, name: Annotated[str | None, Query(description='部门名称')] = None,
leader: Annotated[str | None, Query(description='部门负责人')] = None, leader: Annotated[str | None, Query(description='部门负责人')] = None,
phone: Annotated[str | None, Query(description='联系电话')] = None, phone: Annotated[str | None, Query(description='联系电话')] = None,
status: Annotated[int | None, Query(description='状态')] = None, status: Annotated[int | None, Query(description='状态')] = None,
) -> ResponseSchemaModel[list[dict[str, Any]]]: ) -> ResponseSchemaModel[list[dict[str, Any]]]:
dept = await dept_service.get_dept_tree(request=request, name=name, leader=leader, phone=phone, status=status) dept = await dept_service.get_tree(request=request, name=name, leader=leader, phone=phone, status=status)
return response_base.success(data=dept) return response_base.success(data=dept)
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Annotated
from fastapi import APIRouter, Depends, File, UploadFile
from backend.common.dataclasses import UploadUrl
from backend.common.response.response_schema import ResponseSchemaModel, response_base
from backend.common.security.permission import RequestPermission
from backend.common.security.rbac import DependsRBAC
from backend.utils.file_ops import file_verify, upload_file
router = APIRouter()
@router.post(
'/upload',
summary='文件上传',
dependencies=[
Depends(RequestPermission('sys:file:upload')),
DependsRBAC,
],
)
async def upload_files(file: Annotated[UploadFile, File()]) -> ResponseSchemaModel[UploadUrl]:
file_verify(file)
filename = await upload_file(file)
return response_base.success(data={'url': f'/static/upload/{filename}'})
+4 -4
View File
@@ -14,7 +14,7 @@ from backend.common.security.rbac import DependsRBAC
router = APIRouter() router = APIRouter()
@router.get('/sidebar', summary='获取用户菜单侧边栏', description='适配 vben5', dependencies=[DependsJwtAuth]) @router.get('/sidebar', summary='获取用户菜单侧边栏', description='适配 vben admin v5', dependencies=[DependsJwtAuth])
async def get_user_sidebar(request: Request) -> ResponseSchemaModel[list[dict[str, Any] | None]]: async def get_user_sidebar(request: Request) -> ResponseSchemaModel[list[dict[str, Any] | None]]:
menu = await menu_service.get_sidebar(request=request) menu = await menu_service.get_sidebar(request=request)
return response_base.success(data=menu) return response_base.success(data=menu)
@@ -26,12 +26,12 @@ async def get_menu(pk: Annotated[int, Path(description='菜单 ID')]) -> Respons
return response_base.success(data=data) return response_base.success(data=data)
@router.get('', summary='获取所有菜单展示', dependencies=[DependsJwtAuth]) @router.get('', summary='获取菜单', dependencies=[DependsJwtAuth])
async def get_all_menus( async def get_menu_tree(
title: Annotated[str | None, Query(description='菜单标题')] = None, title: Annotated[str | None, Query(description='菜单标题')] = None,
status: Annotated[int | None, Query(description='状体')] = None, status: Annotated[int | None, Query(description='状体')] = None,
) -> ResponseSchemaModel[list[dict[str, Any]]]: ) -> ResponseSchemaModel[list[dict[str, Any]]]:
menu = await menu_service.get_menu_tree(title=title, status=status) menu = await menu_service.get_tree(title=title, status=status)
return response_base.success(data=menu) return response_base.success(data=menu)
+17 -26
View File
@@ -7,6 +7,7 @@ from fastapi.params import Query
from starlette.responses import StreamingResponse from starlette.responses import StreamingResponse
from backend.app.admin.service.plugin_service import plugin_service from backend.app.admin.service.plugin_service import plugin_service
from backend.common.enums import PluginType
from backend.common.response.response_code import CustomResponseCode from backend.common.response.response_code import CustomResponseCode
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
from backend.common.security.jwt import DependsJwtAuth from backend.common.security.jwt import DependsJwtAuth
@@ -22,37 +23,27 @@ async def get_all_plugins() -> ResponseSchemaModel[list[dict[str, Any]]]:
return response_base.success(data=plugins) return response_base.success(data=plugins)
@router.get('/changes', summary='插件状态是否变更', dependencies=[DependsJwtAuth]) @router.get('/changed', summary='是否存在插件变更', dependencies=[DependsJwtAuth])
async def plugin_changed() -> ResponseSchemaModel[bool]: async def plugin_changed() -> ResponseSchemaModel[bool]:
plugins = await plugin_service.changed() plugins = await plugin_service.changed()
return response_base.success(data=bool(plugins)) return response_base.success(data=bool(plugins))
@router.post( @router.post(
'/zip', '',
summary='安装 zip 插件', summary='安装插件',
description='使用插件 zip 压缩包进行安装', description='使用插件 zip 压缩包或 git 仓库地址进行安装',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:plugin:zip')), Depends(RequestPermission('sys:plugin:install')),
DependsRBAC, DependsRBAC,
], ],
) )
async def install_zip_plugin(file: Annotated[UploadFile, File()]) -> ResponseModel: async def install_plugin(
await plugin_service.install_zip(file=file) type: Annotated[PluginType, Query(description='插件类型')],
return response_base.success(res=CustomResponseCode.PLUGIN_INSTALL_SUCCESS) file: Annotated[UploadFile | None, File()] = None,
repo_url: Annotated[str | None, Query(description='插件 git 仓库地址')] = None,
) -> ResponseModel:
@router.post( await plugin_service.install(type=type, file=file, repo_url=repo_url)
'/git',
summary='安装 git 插件',
description='使用插件 git 仓库地址进行安装,不限制平台;如果需要凭证,需在 git 仓库地址中添加凭证信息',
dependencies=[
Depends(RequestPermission('sys:plugin:git')),
DependsRBAC,
],
)
async def install_git_plugin(repo_url: Annotated[str, Query(description='插件 git 仓库地址')]) -> ResponseModel:
await plugin_service.install_git(repo_url=repo_url)
return response_base.success(res=CustomResponseCode.PLUGIN_INSTALL_SUCCESS) return response_base.success(res=CustomResponseCode.PLUGIN_INSTALL_SUCCESS)
@@ -61,7 +52,7 @@ async def install_git_plugin(repo_url: Annotated[str, Query(description='插件
summary='卸载插件', summary='卸载插件',
description='此操作会直接删除插件依赖,但不会直接删除插件,而是将插件移动到备份目录', description='此操作会直接删除插件依赖,但不会直接删除插件,而是将插件移动到备份目录',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:plugin:del')), Depends(RequestPermission('sys:plugin:uninstall')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -70,11 +61,11 @@ async def uninstall_plugin(plugin: Annotated[str, Path(description='插件名称
return response_base.success(res=CustomResponseCode.PLUGIN_UNINSTALL_SUCCESS) return response_base.success(res=CustomResponseCode.PLUGIN_UNINSTALL_SUCCESS)
@router.post( @router.put(
'/{plugin}/status', '/{plugin}/status',
summary='更新插件状态', summary='更新插件状态',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:plugin:status')), Depends(RequestPermission('sys:plugin:edit')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -83,8 +74,8 @@ async def update_plugin_status(plugin: Annotated[str, Path(description='插件
return response_base.success() return response_base.success()
@router.get('/{plugin}', summary='打包并下载插件', dependencies=[DependsJwtAuth]) @router.get('/{plugin}', summary='下载插件', dependencies=[DependsJwtAuth])
async def build_plugin(plugin: Annotated[str, Path(description='插件名称')]) -> StreamingResponse: async def download_plugin(plugin: Annotated[str, Path(description='插件名称')]) -> StreamingResponse:
bio = await plugin_service.build(plugin=plugin) bio = await plugin_service.build(plugin=plugin)
return StreamingResponse( return StreamingResponse(
bio, bio,
+8 -9
View File
@@ -6,6 +6,7 @@ from fastapi import APIRouter, Depends, Path, Query
from backend.app.admin.schema.role import ( from backend.app.admin.schema.role import (
CreateRoleParam, CreateRoleParam,
DeleteRoleParam,
GetRoleDetail, GetRoleDetail,
GetRoleWithRelationDetail, GetRoleWithRelationDetail,
UpdateRoleMenuParam, UpdateRoleMenuParam,
@@ -29,8 +30,8 @@ async def get_all_roles() -> ResponseSchemaModel[list[GetRoleDetail]]:
return response_base.success(data=data) return response_base.success(data=data)
@router.get('/{pk}/menus', summary='获取角色所有菜单', dependencies=[DependsJwtAuth]) @router.get('/{pk}/menus', summary='获取角色菜单', dependencies=[DependsJwtAuth])
async def get_role_all_menus( async def get_role_menu_tree(
pk: Annotated[int, Path(description='角色 ID')], pk: Annotated[int, Path(description='角色 ID')],
) -> ResponseSchemaModel[list[dict[str, Any] | None]]: ) -> ResponseSchemaModel[list[dict[str, Any] | None]]:
menu = await role_service.get_menu_tree(pk=pk) menu = await role_service.get_menu_tree(pk=pk)
@@ -38,15 +39,13 @@ async def get_role_all_menus(
@router.get('/{pk}/scopes', summary='获取角色所有数据范围', dependencies=[DependsJwtAuth]) @router.get('/{pk}/scopes', summary='获取角色所有数据范围', dependencies=[DependsJwtAuth])
async def get_role_all_scopes(pk: Annotated[int, Path(description='角色 ID')]) -> ResponseSchemaModel[list[int]]: async def get_role_scopes(pk: Annotated[int, Path(description='角色 ID')]) -> ResponseSchemaModel[list[int]]:
rule = await role_service.get_scopes(pk=pk) rule = await role_service.get_scopes(pk=pk)
return response_base.success(data=rule) return response_base.success(data=rule)
@router.get('/{pk}', summary='获取角色详情', dependencies=[DependsJwtAuth]) @router.get('/{pk}', summary='获取角色详情', dependencies=[DependsJwtAuth])
async def get_role( async def get_role(pk: Annotated[int, Path(description='角色 ID')]) -> ResponseSchemaModel[GetRoleWithRelationDetail]:
pk: Annotated[int, Path(description='角色 ID')],
) -> ResponseSchemaModel[GetRoleWithRelationDetail]:
data = await role_service.get(pk=pk) data = await role_service.get(pk=pk)
return response_base.success(data=data) return response_base.success(data=data)
@@ -59,7 +58,7 @@ async def get_role(
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_roles( async def get_roles_paged(
db: CurrentSession, db: CurrentSession,
name: Annotated[str | None, Query(description='角色名称')] = None, name: Annotated[str | None, Query(description='角色名称')] = None,
status: Annotated[int | None, Query(description='状态')] = None, status: Annotated[int | None, Query(description='状态')] = None,
@@ -139,8 +138,8 @@ async def update_role_scopes(
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_role(pk: Annotated[list[int], Query(description='角色 ID 列表')]) -> ResponseModel: async def delete_roles(obj: DeleteRoleParam) -> ResponseModel:
count = await role_service.delete(pk=pk) count = await role_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
-27
View File
@@ -1,27 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Annotated
from fastapi import APIRouter, File, UploadFile
from backend.common.dataclasses import UploadUrl
from backend.common.enums import FileType
from backend.common.response.response_schema import ResponseSchemaModel, response_base
from backend.common.security.jwt import DependsJwtAuth
from backend.utils.file_ops import file_verify, upload_file
router = APIRouter()
@router.post('/image', summary='上传图片', dependencies=[DependsJwtAuth])
async def upload_image(file: Annotated[UploadFile, File()]) -> ResponseSchemaModel[UploadUrl]:
file_verify(file, FileType.image)
filename = await upload_file(file)
return response_base.success(data={'url': f'/static/upload/{filename}'})
@router.post('/video', summary='上传视频', dependencies=[DependsJwtAuth])
async def upload_video(file: Annotated[UploadFile, File()]) -> ResponseSchemaModel[UploadUrl]:
file_verify(file, FileType.video)
filename = await upload_file(file)
return response_base.success(data={'url': f'/static/upload/{filename}'})
+34 -55
View File
@@ -13,6 +13,7 @@ from backend.app.admin.schema.user import (
UpdateUserParam, UpdateUserParam,
) )
from backend.app.admin.service.user_service import user_service from backend.app.admin.service.user_service import user_service
from backend.common.enums import UserPermissionType
from backend.common.pagination import DependsPagination, PageData, paging_data from backend.common.pagination import DependsPagination, PageData, paging_data
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
from backend.common.security.jwt import DependsJwtAuth from backend.common.security.jwt import DependsJwtAuth
@@ -23,42 +24,23 @@ from backend.database.db import CurrentSession
router = APIRouter() router = APIRouter()
@router.post('/add', summary='添加用户', dependencies=[DependsRBAC])
async def add_user(request: Request, obj: AddUserParam) -> ResponseSchemaModel[GetUserInfoWithRelationDetail]:
await user_service.add(request=request, obj=obj)
data = await user_service.get_userinfo(username=obj.username)
return response_base.success(data=data)
@router.post('/{username}/password', summary='密码重置', dependencies=[DependsJwtAuth])
async def password_reset(
username: Annotated[str, Path(description='用户名')], obj: ResetPasswordParam
) -> ResponseModel:
count = await user_service.pwd_reset(username=username, obj=obj)
if count > 0:
return response_base.success()
return response_base.fail()
@router.get('/me', summary='获取当前用户信息', dependencies=[DependsJwtAuth]) @router.get('/me', summary='获取当前用户信息', dependencies=[DependsJwtAuth])
async def get_current_user(request: Request) -> ResponseSchemaModel[GetCurrentUserInfoWithRelationDetail]: async def get_current_user(request: Request) -> ResponseSchemaModel[GetCurrentUserInfoWithRelationDetail]:
data = request.user.model_dump() data = request.user.model_dump()
return response_base.success(data=data) return response_base.success(data=data)
@router.get('/{username}', summary='查看用户信息', dependencies=[DependsJwtAuth]) @router.get('/{pk}', summary='获取用户信息', dependencies=[DependsJwtAuth])
async def get_user( async def get_userinfo(
username: Annotated[str, Path(description='用户')], pk: Annotated[int, Path(description='用户 ID')],
) -> ResponseSchemaModel[GetUserInfoWithRelationDetail]: ) -> ResponseSchemaModel[GetUserInfoWithRelationDetail]:
data = await user_service.get_userinfo(username=username) data = await user_service.get_userinfo(pk=pk)
return response_base.success(data=data) return response_base.success(data=data)
@router.get('/{username}/roles', summary='获取用户所有角色', dependencies=[DependsJwtAuth]) @router.get('/{pk}/roles', summary='获取用户所有角色', dependencies=[DependsJwtAuth])
async def get_user_all_roles( async def get_user_roles(pk: Annotated[int, Path(description='用户 ID')]) -> ResponseSchemaModel[list[GetRoleDetail]]:
username: Annotated[str, Path(description='用户名')], data = await user_service.get_roles(pk=pk)
) -> ResponseSchemaModel[list[GetRoleDetail]]:
data = await user_service.get_roles(username=username)
return response_base.success(data=data) return response_base.success(data=data)
@@ -70,7 +52,7 @@ async def get_user_all_roles(
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_users( async def get_users_paged(
db: CurrentSession, db: CurrentSession,
dept: Annotated[int | None, Query(description='部门 ID')] = None, dept: Annotated[int | None, Query(description='部门 ID')] = None,
username: Annotated[str | None, Query(description='用户名')] = None, username: Annotated[str | None, Query(description='用户名')] = None,
@@ -82,58 +64,55 @@ async def get_pagination_users(
return response_base.success(data=page_data) return response_base.success(data=page_data)
@router.put('/{username}', summary='更新用户信息', dependencies=[DependsJwtAuth]) @router.post('', summary='创建用户', dependencies=[DependsRBAC])
async def create_user(request: Request, obj: AddUserParam) -> ResponseSchemaModel[GetUserInfoWithRelationDetail]:
await user_service.create(request=request, obj=obj)
data = await user_service.get_userinfo(username=obj.username)
return response_base.success(data=data)
@router.put('/{pk}', summary='更新用户信息', dependencies=[DependsJwtAuth])
async def update_user( async def update_user(
request: Request, username: Annotated[str, Path(description='用户')], obj: UpdateUserParam request: Request, pk: Annotated[int, Path(description='用户 ID')], obj: UpdateUserParam
) -> ResponseModel: ) -> ResponseModel:
count = await user_service.update(request=request, username=username, obj=obj) count = await user_service.update(request=request, pk=pk, obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
@router.put('/{pk}/super', summary='修改用户超级权限', dependencies=[DependsRBAC]) @router.put('/{pk}/permissions', summary='更新用户权限', dependencies=[DependsRBAC])
async def super_set(request: Request, pk: Annotated[int, Path(description='用户 ID')]) -> ResponseModel: async def update_user_permission(
count = await user_service.update_permission(request=request, pk=pk) request: Request,
pk: Annotated[int, Path(description='用户 ID')],
type: Annotated[UserPermissionType, Query(description='权限类型')],
) -> ResponseModel:
count = await user_service.update_permission(request=request, pk=pk, type=type)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
@router.put('/{pk}/staff', summary='修改用户后台登录权限', dependencies=[DependsRBAC]) @router.put('/{pk}/password', summary='重置用户密码', dependencies=[DependsJwtAuth])
async def staff_set(request: Request, pk: Annotated[int, Path(description='用户 ID')]) -> ResponseModel: async def reset_user_password(
count = await user_service.update_staff(request=request, pk=pk) pk: Annotated[int, Path(description='用户 ID')], obj: ResetPasswordParam
if count > 0: ) -> ResponseModel:
return response_base.success() count = await user_service.reset_pwd(pk=pk, obj=obj)
return response_base.fail()
@router.put('/{pk}/status', summary='修改用户状态', dependencies=[DependsRBAC])
async def status_set(request: Request, pk: Annotated[int, Path(description='用户 ID')]) -> ResponseModel:
count = await user_service.update_status(request=request, pk=pk)
if count > 0:
return response_base.success()
return response_base.fail()
@router.put('/{pk}/multi', summary='修改用户多端登录状态', dependencies=[DependsRBAC])
async def multi_set(request: Request, pk: Annotated[int, Path(description='用户 ID')]) -> ResponseModel:
count = await user_service.update_multi_login(request=request, pk=pk)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
@router.delete( @router.delete(
path='/{username}', path='/{pk}',
summary='删除用户', summary='删除用户',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:user:del')), Depends(RequestPermission('sys:user:del')),
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_user(username: Annotated[str, Path(description='用户')]) -> ResponseModel: async def delete_user(pk: Annotated[int, Path(description='用户 ID')]) -> ResponseModel:
count = await user_service.delete(username=username) count = await user_service.delete(pk=pk)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
+4 -4
View File
@@ -77,15 +77,15 @@ class CRUDDataRule(CRUDPlus[DataRule]):
""" """
return await self.update_model(db, pk, obj) return await self.update_model(db, pk, obj)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除规则 批量删除规则
:param db: 数据库会话 :param db: 数据库会话
:param pk: 规则 ID 列表 :param pks: 规则 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk) return await self.delete_model_by_column(db, allow_multiple=True, id__in=pks)
data_rule_dao: CRUDDataRule = CRUDDataRule(DataRule) data_rule_dao: CRUDDataRule = CRUDDataRule(DataRule)
+4 -4
View File
@@ -105,15 +105,15 @@ class CRUDDataScope(CRUDPlus[DataScope]):
current_data_scope.rules = rules.scalars().all() current_data_scope.rules = rules.scalars().all()
return len(current_data_scope.rules) return len(current_data_scope.rules)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除数据范围 批量删除数据范围
:param db: 数据库会话 :param db: 数据库会话
:param pk: 范围 ID 列表 :param pks: 范围 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk) return await self.delete_model_by_column(db, allow_multiple=True, id__in=pks)
data_scope_dao: CRUDDataScope = CRUDDataScope(DataScope) data_scope_dao: CRUDDataScope = CRUDDataScope(DataScope)
+4 -4
View File
@@ -41,15 +41,15 @@ class CRUDLoginLog(CRUDPlus[LoginLog]):
""" """
await self.create_model(db, obj, commit=True) await self.create_model(db, obj, commit=True)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除登录日志 批量删除登录日志
:param db: 数据库会话 :param db: 数据库会话
:param pk: 登录日志 ID 列表 :param pks: 登录日志 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk) return await self.delete_model_by_column(db, allow_multiple=True, id__in=pks)
async def delete_all(self, db: AsyncSession) -> int: async def delete_all(self, db: AsyncSession) -> int:
""" """
+3 -4
View File
@@ -50,18 +50,17 @@ class CRUDMenu(CRUDPlus[Menu]):
return await self.select_models_order(db, 'sort', **filters) return await self.select_models_order(db, 'sort', **filters)
async def get_sidebar(self, db: AsyncSession, superuser: bool, menu_ids: list[int | None]) -> Sequence[Menu]: async def get_sidebar(self, db: AsyncSession, menu_ids: list[int] | None) -> Sequence[Menu]:
""" """
获取角色菜单列表 获取用户的菜单侧边栏
:param db: 数据库会话 :param db: 数据库会话
:param superuser: 是否超级管理员
:param menu_ids: 菜单 ID 列表 :param menu_ids: 菜单 ID 列表
:return: :return:
""" """
filters = {'type__in': [0, 1, 3, 4]} filters = {'type__in': [0, 1, 3, 4]}
if not superuser: if menu_ids:
filters['id__in'] = menu_ids filters['id__in'] = menu_ids
return await self.select_models_order(db, 'sort', 'asc', **filters) return await self.select_models_order(db, 'sort', 'asc', **filters)
+4 -4
View File
@@ -41,15 +41,15 @@ class CRUDOperaLogDao(CRUDPlus[OperaLog]):
""" """
await self.create_model(db, obj) await self.create_model(db, obj)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除操作日志 批量删除操作日志
:param db: 数据库会话 :param db: 数据库会话
:param pk: 操作日志 ID 列表 :param pks: 操作日志 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk) return await self.delete_model_by_column(db, allow_multiple=True, id__in=pks)
async def delete_all(self, db: AsyncSession) -> int: async def delete_all(self, db: AsyncSession) -> int:
""" """
+4 -4
View File
@@ -134,15 +134,15 @@ class CRUDRole(CRUDPlus[Role]):
current_role.scopes = scopes.scalars().all() current_role.scopes = scopes.scalars().all()
return len(current_role.scopes) return len(current_role.scopes)
async def delete(self, db: AsyncSession, role_id: list[int]) -> int: async def delete(self, db: AsyncSession, role_ids: list[int]) -> int:
""" """
删除角色 批量删除角色
:param db: 数据库会话 :param db: 数据库会话
:param role_id: 角色 ID 列表 :param role_ids: 角色 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=role_id) return await self.delete_model_by_column(db, allow_multiple=True, id__in=role_ids)
role_dao: CRUDRole = CRUDRole(Role) role_dao: CRUDRole = CRUDRole(Role)
+6
View File
@@ -27,6 +27,12 @@ class UpdateDataRuleParam(DataRuleSchemaBase):
"""更新数据规则参数""" """更新数据规则参数"""
class DeleteDataRuleParam(SchemaBase):
"""删除数据规则参数"""
pks: list[int] = Field(description='规则 ID 列表')
class GetDataRuleDetail(DataRuleSchemaBase): class GetDataRuleDetail(DataRuleSchemaBase):
"""数据规则详情""" """数据规则详情"""
+6
View File
@@ -30,6 +30,12 @@ class UpdateDataScopeRuleParam(SchemaBase):
rules: list[int] = Field(description='数据规则 ID 列表') rules: list[int] = Field(description='数据规则 ID 列表')
class DeleteDataScopeParam(SchemaBase):
"""删除数据范围参数"""
pks: list[int] = Field(description='数据范围 ID 列表')
class GetDataScopeDetail(DataScopeBase): class GetDataScopeDetail(DataScopeBase):
"""数据范围详情""" """数据范围详情"""
+6
View File
@@ -33,6 +33,12 @@ class UpdateLoginLogParam(LoginLogSchemaBase):
"""更新登录日志参数""" """更新登录日志参数"""
class DeleteLoginLogParam(SchemaBase):
"""删除登录日志参数"""
pks: list[int] = Field(description='登录日志 ID 列表')
class GetLoginLogDetail(LoginLogSchemaBase): class GetLoginLogDetail(LoginLogSchemaBase):
"""登录日志详情""" """登录日志详情"""
+6
View File
@@ -41,6 +41,12 @@ class UpdateOperaLogParam(OperaLogSchemaBase):
"""更新操作日志参数""" """更新操作日志参数"""
class DeleteOperaLogParam(SchemaBase):
"""删除操作日志参数"""
pks: list[int] = Field(description='操作日志 ID 列表')
class GetOperaLogDetail(OperaLogSchemaBase): class GetOperaLogDetail(OperaLogSchemaBase):
"""操作日志详情""" """操作日志详情"""
+6
View File
@@ -27,6 +27,12 @@ class UpdateRoleParam(RoleSchemaBase):
"""更新角色参数""" """更新角色参数"""
class DeleteRoleParam(SchemaBase):
"""删除角色参数"""
pks: list[int] = Field(description='角色 ID 列表')
class UpdateRoleMenuParam(SchemaBase): class UpdateRoleMenuParam(SchemaBase):
"""更新角色菜单参数""" """更新角色菜单参数"""
+28 -2
View File
@@ -5,6 +5,7 @@ from fastapi.security import HTTPBasicCredentials
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from starlette.background import BackgroundTask, BackgroundTasks from starlette.background import BackgroundTask, BackgroundTasks
from backend.app.admin.crud.crud_menu import menu_dao
from backend.app.admin.crud.crud_user import user_dao from backend.app.admin.crud.crud_user import user_dao
from backend.app.admin.model import User from backend.app.admin.model import User
from backend.app.admin.schema.token import GetLoginToken, GetNewToken from backend.app.admin.schema.token import GetLoginToken, GetNewToken
@@ -162,9 +163,34 @@ class AuthService:
return data return data
@staticmethod @staticmethod
async def new_token(*, request: Request) -> GetNewToken: async def get_codes(*, request: Request) -> list[str]:
""" """
获取新的访问令牌 获取用户权限码
:param request: FastAPI 请求对象
:return:
"""
codes = set()
if request.user.is_superuser:
async with async_db_session.begin() as db:
menus = await menu_dao.get_all(db, None, None)
for menu in menus:
if menu.perms:
codes.add(*menu.perms.split(','))
else:
roles = request.user.roles
if roles:
for role in roles:
for menu in role.menus:
if menu.perms:
codes.add(*menu.perms.split(','))
return list(codes)
@staticmethod
async def refresh_token(*, request: Request) -> GetNewToken:
"""
刷新令牌
:param request: FastAPI 请求对象 :param request: FastAPI 请求对象
:return: :return:
+10 -5
View File
@@ -6,7 +6,12 @@ from sqlalchemy import Select
from backend.app.admin.crud.crud_data_rule import data_rule_dao from backend.app.admin.crud.crud_data_rule import data_rule_dao
from backend.app.admin.model import DataRule from backend.app.admin.model import DataRule
from backend.app.admin.schema.data_rule import CreateDataRuleParam, GetDataRuleColumnDetail, UpdateDataRuleParam from backend.app.admin.schema.data_rule import (
CreateDataRuleParam,
DeleteDataRuleParam,
GetDataRuleColumnDetail,
UpdateDataRuleParam,
)
from backend.common.exception import errors from backend.common.exception import errors
from backend.core.conf import settings from backend.core.conf import settings
from backend.database.db import async_db_session from backend.database.db import async_db_session
@@ -105,15 +110,15 @@ class DataRuleService:
return count return count
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: DeleteDataRuleParam) -> int:
""" """
删除数据规则 批量删除数据规则
:param pk: 规则 ID 列表 :param obj: 规则 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await data_rule_dao.delete(db, pk) count = await data_rule_dao.delete(db, obj.pks)
return count return count
@@ -6,7 +6,12 @@ from sqlalchemy import Select
from backend.app.admin.crud.crud_data_scope import data_scope_dao from backend.app.admin.crud.crud_data_scope import data_scope_dao
from backend.app.admin.model import DataScope from backend.app.admin.model import DataScope
from backend.app.admin.schema.data_scope import CreateDataScopeParam, UpdateDataScopeParam, UpdateDataScopeRuleParam from backend.app.admin.schema.data_scope import (
CreateDataScopeParam,
DeleteDataScopeParam,
UpdateDataScopeParam,
UpdateDataScopeRuleParam,
)
from backend.common.exception import errors from backend.common.exception import errors
from backend.core.conf import settings from backend.core.conf import settings
from backend.database.db import async_db_session from backend.database.db import async_db_session
@@ -112,17 +117,17 @@ class DataScopeService:
return count return count
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: DeleteDataScopeParam) -> int:
""" """
删除数据范围 批量删除数据范围
:param pk: 范围 ID 列表 :param obj: 范围 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await data_scope_dao.delete(db, pk) count = await data_scope_dao.delete(db, obj.pks)
for _pk in pk: for pk in obj.pks:
data_rule = await data_scope_dao.get(db, _pk) data_rule = await data_scope_dao.get(db, pk)
if data_rule: if data_rule:
for role in await data_rule.awaitable_attrs.roles: for role in await data_rule.awaitable_attrs.roles:
for user in await role.awaitable_attrs.users: for user in await role.awaitable_attrs.users:
+1 -1
View File
@@ -32,7 +32,7 @@ class DeptService:
return dept return dept
@staticmethod @staticmethod
async def get_dept_tree( async def get_tree(
*, request: Request, name: str | None, leader: str | None, phone: str | None, status: int | None *, request: Request, name: str | None, leader: str | None, phone: str | None, status: int | None
) -> list[dict[str, Any]]: ) -> list[dict[str, Any]]:
""" """
@@ -7,7 +7,7 @@ from sqlalchemy import Select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from backend.app.admin.crud.crud_login_log import login_log_dao from backend.app.admin.crud.crud_login_log import login_log_dao
from backend.app.admin.schema.login_log import CreateLoginLogParam from backend.app.admin.schema.login_log import CreateLoginLogParam, DeleteLoginLogParam
from backend.common.log import log from backend.common.log import log
from backend.database.db import async_db_session from backend.database.db import async_db_session
@@ -71,15 +71,15 @@ class LoginLogService:
log.error(f'登录日志创建失败: {e}') log.error(f'登录日志创建失败: {e}')
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: DeleteLoginLogParam) -> int:
""" """
删除登录日志 批量删除登录日志
:param pk: 日志 ID 列表 :param obj: 日志 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await login_log_dao.delete(db, pk) count = await login_log_dao.delete(db, obj.pks)
return count return count
@staticmethod @staticmethod
+12 -16
View File
@@ -32,7 +32,7 @@ class MenuService:
return menu return menu
@staticmethod @staticmethod
async def get_menu_tree(*, title: str | None, status: int | None) -> list[dict[str, Any]]: async def get_tree(*, title: str | None, status: int | None) -> list[dict[str, Any]]:
""" """
获取菜单树形结构 获取菜单树形结构
@@ -54,21 +54,17 @@ class MenuService:
:return: :return:
""" """
async with async_db_session() as db: async with async_db_session() as db:
roles = request.user.roles if request.user.is_superuser:
menu_tree = [] menu_data = await menu_dao.get_sidebar(db, None)
if roles: else:
unique_menus = {} roles = request.user.roles
for role in roles: menu_ids = set()
for menu in role.menus: if roles:
unique_menus[menu.id] = menu for role in roles:
all_ids = set(unique_menus.keys()) for menu in role.menus:
valid_menu_ids = [ menu_ids.add(menu.id)
menu_id menu_data = await menu_dao.get_sidebar(db, list(menu_ids))
for menu_id, menu in unique_menus.items() menu_tree = get_vben5_tree_data(menu_data)
if menu.parent_id is None or menu.parent_id in all_ids
]
menu_data = await menu_dao.get_sidebar(db, request.user.is_superuser, valid_menu_ids)
menu_tree = get_vben5_tree_data(menu_data)
return menu_tree return menu_tree
@staticmethod @staticmethod
@@ -3,7 +3,7 @@
from sqlalchemy import Select from sqlalchemy import Select
from backend.app.admin.crud.crud_opera_log import opera_log_dao from backend.app.admin.crud.crud_opera_log import opera_log_dao
from backend.app.admin.schema.opera_log import CreateOperaLogParam from backend.app.admin.schema.opera_log import CreateOperaLogParam, DeleteOperaLogParam
from backend.database.db import async_db_session from backend.database.db import async_db_session
@@ -34,15 +34,15 @@ class OperaLogService:
await opera_log_dao.create(db, obj) await opera_log_dao.create(db, obj)
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: DeleteOperaLogParam) -> int:
""" """
删除操作日志 批量删除操作日志
:param pk: 日志 ID 列表 :param obj: 日志 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await opera_log_dao.delete(db, pk) count = await opera_log_dao.delete(db, obj.pks)
return count return count
@staticmethod @staticmethod
+20 -2
View File
@@ -11,7 +11,7 @@ from typing import Any
from dulwich import porcelain from dulwich import porcelain
from fastapi import UploadFile from fastapi import UploadFile
from backend.common.enums import StatusType from backend.common.enums import PluginType, StatusType
from backend.common.exception import errors from backend.common.exception import errors
from backend.common.log import log from backend.common.log import log
from backend.core.conf import settings from backend.core.conf import settings
@@ -41,7 +41,7 @@ class PluginService:
@staticmethod @staticmethod
async def changed() -> str | None: async def changed() -> str | None:
"""插件状态是否变更""" """检查插件是否发生变更"""
return await redis_client.get(f'{settings.PLUGIN_REDIS_PREFIX}:changed') return await redis_client.get(f'{settings.PLUGIN_REDIS_PREFIX}:changed')
@staticmethod @staticmethod
@@ -113,6 +113,24 @@ class PluginService:
await install_requirements_async(repo_name) await install_requirements_async(repo_name)
await redis_client.set(f'{settings.PLUGIN_REDIS_PREFIX}:changed', 'ture') await redis_client.set(f'{settings.PLUGIN_REDIS_PREFIX}:changed', 'ture')
async def install(self, *, type: PluginType, file: UploadFile | None = None, repo_url: str | None = None):
"""
安装插件
:param type: 插件类型
:param file: 插件 zip 压缩包
:param repo_url: git 仓库地址
:return:
"""
if type == PluginType.zip:
if not file:
raise errors.ForbiddenError(msg='ZIP 压缩包不能为空')
await self.install_zip(file=file)
elif type == PluginType.git:
if not repo_url:
raise errors.ForbiddenError(msg='Git 仓库地址不能为空')
await self.install_git(repo_url=repo_url)
@staticmethod @staticmethod
async def uninstall(*, plugin: str): async def uninstall(*, plugin: str):
""" """
+7 -6
View File
@@ -10,6 +10,7 @@ from backend.app.admin.crud.crud_role import role_dao
from backend.app.admin.model import Role from backend.app.admin.model import Role
from backend.app.admin.schema.role import ( from backend.app.admin.schema.role import (
CreateRoleParam, CreateRoleParam,
DeleteRoleParam,
UpdateRoleMenuParam, UpdateRoleMenuParam,
UpdateRoleParam, UpdateRoleParam,
UpdateRoleScopeParam, UpdateRoleScopeParam,
@@ -166,17 +167,17 @@ class RoleService:
return count return count
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: DeleteRoleParam) -> int:
""" """
删除角色 批量删除角色
:param pk: 角色 ID 列表 :param obj: 角色 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await role_dao.delete(db, pk) count = await role_dao.delete(db, obj.pks)
for _pk in pk: for pk in obj.pks:
role = await role_dao.get(db, _pk) role = await role_dao.get(db, pk)
if role: if role:
for user in await role.awaitable_attrs.users: for user in await role.awaitable_attrs.users:
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
+97 -86
View File
@@ -16,6 +16,7 @@ from backend.app.admin.schema.user import (
ResetPasswordParam, ResetPasswordParam,
UpdateUserParam, UpdateUserParam,
) )
from backend.common.enums import UserPermissionType
from backend.common.exception import errors from backend.common.exception import errors
from backend.common.security.jwt import get_hash_password, get_token, jwt_decode, password_verify, superuser_verify from backend.common.security.jwt import get_hash_password, get_token, jwt_decode, password_verify, superuser_verify
from backend.core.conf import settings from backend.core.conf import settings
@@ -27,86 +28,30 @@ class UserService:
"""用户服务类""" """用户服务类"""
@staticmethod @staticmethod
async def add(*, request: Request, obj: AddUserParam) -> None: async def get_userinfo(*, pk: int | None = None, username: str | None = None) -> User:
"""
添加新用户
:param request: FastAPI 请求对象
:param obj: 用户添加参数
:return:
"""
async with async_db_session.begin() as db:
superuser_verify(request)
username = await user_dao.get_by_username(db, obj.username)
if username:
raise errors.ForbiddenError(msg='用户已注册')
obj.nickname = obj.nickname if obj.nickname else f'#{random.randrange(88888, 99999)}'
nickname = await user_dao.get_by_nickname(db, obj.nickname)
if nickname:
raise errors.ForbiddenError(msg='昵称已注册')
if not obj.password:
raise errors.ForbiddenError(msg='密码为空')
dept = await dept_dao.get(db, obj.dept_id)
if not dept:
raise errors.NotFoundError(msg='部门不存在')
for role_id in obj.roles:
role = await role_dao.get(db, role_id)
if not role:
raise errors.NotFoundError(msg='角色不存在')
await user_dao.add(db, obj)
@staticmethod
async def pwd_reset(*, username: str, obj: ResetPasswordParam) -> int:
"""
重置用户密码
:param username: 用户名
:param obj: 密码重置参数
:return:
"""
async with async_db_session.begin() as db:
user = await user_dao.get_by_username(db, username)
if not user:
raise errors.NotFoundError(msg='用户不存在')
if not password_verify(obj.old_password, user.password):
raise errors.ForbiddenError(msg='原密码错误')
if obj.new_password != obj.confirm_password:
raise errors.ForbiddenError(msg='密码输入不一致')
new_pwd = get_hash_password(obj.new_password, user.salt)
count = await user_dao.reset_password(db, user.id, new_pwd)
key_prefix = [
f'{settings.TOKEN_REDIS_PREFIX}:{user.id}',
f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{user.id}',
f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}',
]
for prefix in key_prefix:
await redis_client.delete_prefix(prefix)
return count
@staticmethod
async def get_userinfo(*, username: str) -> User:
""" """
获取用户信息 获取用户信息
:param pk: 用户 ID
:param username: 用户名 :param username: 用户名
:return: :return:
""" """
async with async_db_session() as db: async with async_db_session() as db:
user = await user_dao.get_with_relation(db, username=username) user = await user_dao.get_with_relation(db, user_id=pk, username=username)
if not user: if not user:
raise errors.NotFoundError(msg='用户不存在') raise errors.NotFoundError(msg='用户不存在')
return user return user
@staticmethod @staticmethod
async def get_roles(*, username: str) -> Sequence[Role]: async def get_roles(*, pk: int) -> Sequence[Role]:
""" """
获取用户所有角色 获取用户所有角色
:param username: 用户 :param pk: 用户 ID
:return: :return:
""" """
async with async_db_session() as db: async with async_db_session() as db:
user = await user_dao.get_with_relation(db, username=username) user = await user_dao.get_with_relation(db, user_id=pk)
if not user: if not user:
raise errors.NotFoundError(msg='用户不存在') raise errors.NotFoundError(msg='用户不存在')
return user.roles return user.roles
@@ -125,41 +70,58 @@ class UserService:
return await user_dao.get_list(dept=dept, username=username, phone=phone, status=status) return await user_dao.get_list(dept=dept, username=username, phone=phone, status=status)
@staticmethod @staticmethod
async def update(*, request: Request, username: str, obj: UpdateUserParam) -> int: async def create(*, request: Request, obj: AddUserParam) -> None:
"""
创建用户
:param request: FastAPI 请求对象
:param obj: 用户添加参数
:return:
"""
async with async_db_session.begin() as db:
superuser_verify(request)
if await user_dao.get_by_username(db, obj.username):
raise errors.ForbiddenError(msg='用户名已注册')
obj.nickname = obj.nickname if obj.nickname else f'#{random.randrange(88888, 99999)}'
if not obj.password:
raise errors.ForbiddenError(msg='密码不允许为空')
if not await dept_dao.get(db, obj.dept_id):
raise errors.NotFoundError(msg='部门不存在')
for role_id in obj.roles:
if not await role_dao.get(db, role_id):
raise errors.NotFoundError(msg='角色不存在')
await user_dao.add(db, obj)
@staticmethod
async def update(*, request: Request, pk: int, obj: UpdateUserParam) -> int:
""" """
更新用户信息 更新用户信息
:param request: FastAPI 请求对象 :param request: FastAPI 请求对象
:param username: 用户 :param pk: 用户 ID
:param obj: 用户更新参数 :param obj: 用户更新参数
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
if request.user.username != username: user = await user_dao.get_with_relation(db, user_id=pk)
raise errors.ForbiddenError(msg='你只能修改自己的信息')
user = await user_dao.get_with_relation(db, username=username)
if not user: if not user:
raise errors.NotFoundError(msg='用户不存在') raise errors.NotFoundError(msg='用户不存在')
if user.username != obj.username: if request.user.username != user.username:
_username = await user_dao.get_by_username(db, obj.username) raise errors.ForbiddenError(msg='只能修改自己的信息')
if _username: if obj.username != user.username:
if await user_dao.get_by_username(db, obj.username):
raise errors.ForbiddenError(msg='用户名已注册') raise errors.ForbiddenError(msg='用户名已注册')
if user.nickname != obj.nickname:
nickname = await user_dao.get_by_nickname(db, obj.nickname)
if nickname:
raise errors.ForbiddenError(msg='昵称已注册')
for role_id in obj.roles: for role_id in obj.roles:
role = await role_dao.get(db, role_id) if not await role_dao.get(db, role_id):
if not role:
raise errors.NotFoundError(msg='角色不存在') raise errors.NotFoundError(msg='角色不存在')
count = await user_dao.update(db, user, obj) count = await user_dao.update(db, user, obj)
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
return count return count
@staticmethod @staticmethod
async def update_permission(*, request: Request, pk: int) -> int: async def update_superuser(*, request: Request, pk: int) -> int:
""" """
更新用户权限 更新用户管理员状态
:param request: FastAPI 请求对象 :param request: FastAPI 请求对象
:param pk: 用户 ID :param pk: 用户 ID
@@ -171,7 +133,7 @@ class UserService:
if not user: if not user:
raise errors.NotFoundError(msg='用户不存在') raise errors.NotFoundError(msg='用户不存在')
if pk == request.user.id: if pk == request.user.id:
raise errors.ForbiddenError(msg='非法操作') raise errors.ForbiddenError(msg='禁止修改自身权限')
super_status = await user_dao.get_super(db, pk) super_status = await user_dao.get_super(db, pk)
count = await user_dao.set_super(db, pk, not super_status) count = await user_dao.set_super(db, pk, not super_status)
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
@@ -192,7 +154,7 @@ class UserService:
if not user: if not user:
raise errors.NotFoundError(msg='用户不存在') raise errors.NotFoundError(msg='用户不存在')
if pk == request.user.id: if pk == request.user.id:
raise errors.ForbiddenError(msg='非法操作') raise errors.ForbiddenError(msg='禁止修改自身权限')
staff_status = await user_dao.get_staff(db, pk) staff_status = await user_dao.get_staff(db, pk)
count = await user_dao.set_staff(db, pk, not staff_status) count = await user_dao.set_staff(db, pk, not staff_status)
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
@@ -213,7 +175,7 @@ class UserService:
if not user: if not user:
raise errors.NotFoundError(msg='用户不存在') raise errors.NotFoundError(msg='用户不存在')
if pk == request.user.id: if pk == request.user.id:
raise errors.ForbiddenError(msg='非法操作') raise errors.ForbiddenError(msg='禁止修改自身权限')
status = await user_dao.get_status(db, pk) status = await user_dao.get_status(db, pk)
count = await user_dao.set_status(db, pk, 0 if status == 1 else 1) count = await user_dao.set_status(db, pk, 0 if status == 1 else 1)
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
@@ -251,16 +213,65 @@ class UserService:
await redis_client.delete_prefix(key_prefix) await redis_client.delete_prefix(key_prefix)
return count return count
@staticmethod async def update_permission(self, *, request: Request, pk: int, type: UserPermissionType) -> int:
async def delete(*, username: str) -> int:
""" """
删除用户 更新用户权限
:param username: 用户名 :param request: FastAPI 请求对象
:param pk: 用户 ID
:param type: 权限类型
:return:
"""
if type == UserPermissionType.superuser:
count = await self.update_superuser(request=request, pk=pk)
elif type == UserPermissionType.staff:
count = await self.update_staff(request=request, pk=pk)
elif type == UserPermissionType.status:
count = await self.update_status(request=request, pk=pk)
elif type == UserPermissionType.multi_login:
count = await self.update_multi_login(request=request, pk=pk)
else:
raise errors.ForbiddenError(msg='权限类型不存在')
return count
@staticmethod
async def reset_pwd(*, pk: int, obj: ResetPasswordParam) -> int:
"""
重置用户密码
:param pk: 用户 ID
:param obj: 密码重置参数
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
user = await user_dao.get_by_username(db, username) user = await user_dao.get(db, pk)
if not user:
raise errors.NotFoundError(msg='用户不存在')
if not password_verify(obj.old_password, user.password):
raise errors.ForbiddenError(msg='原密码错误')
if obj.new_password != obj.confirm_password:
raise errors.ForbiddenError(msg='密码输入不一致')
new_pwd = get_hash_password(obj.new_password, user.salt)
count = await user_dao.reset_password(db, user.id, new_pwd)
key_prefix = [
f'{settings.TOKEN_REDIS_PREFIX}:{user.id}',
f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{user.id}',
f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}',
]
for prefix in key_prefix:
await redis_client.delete_prefix(prefix)
return count
@staticmethod
async def delete(*, pk: int) -> int:
"""
删除用户
:param pk: 用户 ID
:return:
"""
async with async_db_session.begin() as db:
user = await user_dao.get(db, pk)
if not user: if not user:
raise errors.NotFoundError(msg='用户不存在') raise errors.NotFoundError(msg='用户不存在')
count = await user_dao.delete(db, user.id) count = await user_dao.delete(db, user.id)
+11 -11
View File
@@ -14,12 +14,6 @@ from backend.common.security.rbac import DependsRBAC
router = APIRouter() router = APIRouter()
@router.get('', summary='获取可执行任务', dependencies=[DependsJwtAuth])
async def get_all_tasks() -> ResponseSchemaModel[list[str]]:
tasks = await task_service.get_list()
return response_base.success(data=tasks)
@router.get( @router.get(
'/{tid}', '/{tid}',
summary='获取任务详情', summary='获取任务详情',
@@ -27,12 +21,18 @@ async def get_all_tasks() -> ResponseSchemaModel[list[str]]:
description='此接口被视为作废,建议使用 flower 查看任务详情', description='此接口被视为作废,建议使用 flower 查看任务详情',
dependencies=[DependsJwtAuth], dependencies=[DependsJwtAuth],
) )
async def get_task_detail(tid: Annotated[str, Path(description='任务 UUID')]) -> ResponseSchemaModel[TaskResult]: async def get_task(tid: Annotated[str, Path(description='任务 UUID')]) -> ResponseSchemaModel[TaskResult]:
status = task_service.get_detail(tid=tid) status = task_service.get(tid=tid)
return response_base.success(data=status) return response_base.success(data=status)
@router.post( @router.get('', summary='获取所有任务', dependencies=[DependsJwtAuth])
async def get_all_tasks() -> ResponseSchemaModel[list[str]]:
tasks = await task_service.get_all()
return response_base.success(data=tasks)
@router.delete(
'/{tid}', '/{tid}',
summary='撤销任务', summary='撤销任务',
dependencies=[ dependencies=[
@@ -46,8 +46,8 @@ async def revoke_task(tid: Annotated[str, Path(description='任务 UUID')]) -> R
@router.post( @router.post(
'', '/runs',
summary='行任务', summary='行任务',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:task:run')), Depends(RequestPermission('sys:task:run')),
DependsRBAC, DependsRBAC,
+10 -10
View File
@@ -11,16 +11,7 @@ from backend.common.exception import errors
class TaskService: class TaskService:
@staticmethod @staticmethod
async def get_list() -> list[str]: def get(*, tid: str) -> TaskResult:
"""获取所有已注册的 Celery 任务列表"""
registered_tasks = await run_in_threadpool(celery_app.control.inspect().registered)
if not registered_tasks:
raise errors.ForbiddenError(msg='Celery 服务未启动')
tasks = list(registered_tasks.values())[0]
return tasks
@staticmethod
def get_detail(*, tid: str) -> TaskResult:
""" """
获取指定任务的详细信息 获取指定任务的详细信息
@@ -43,6 +34,15 @@ class TaskService:
queue=result.queue, queue=result.queue,
) )
@staticmethod
async def get_all() -> list[str]:
"""获取所有已注册的 Celery 任务列表"""
registered_tasks = await run_in_threadpool(celery_app.control.inspect().registered)
if not registered_tasks:
raise errors.ForbiddenError(msg='Celery 服务未启动')
tasks = list(registered_tasks.values())[0]
return tasks
@staticmethod @staticmethod
def revoke(*, tid: str) -> None: def revoke(*, tid: str) -> None:
""" """
+16
View File
@@ -121,3 +121,19 @@ class FileType(StrEnum):
image = 'image' image = 'image'
video = 'video' video = 'video'
class PluginType(StrEnum):
"""插件类型"""
zip = 'zip'
git = 'git'
class UserPermissionType(StrEnum):
"""用户权限类型"""
superuser = 'superuser'
staff = 'staff'
status = 'status'
multi_login = 'multi_login'
+1 -1
View File
@@ -231,7 +231,7 @@ def superuser_verify(request: Request) -> bool:
""" """
superuser = request.user.is_superuser superuser = request.user.is_superuser
if not superuser or not request.user.is_staff: if not superuser or not request.user.is_staff:
raise errors.AuthorizationError raise errors.AuthorizationError()
return superuser return superuser
+5 -8
View File
@@ -64,23 +64,20 @@ async def filter_data_permission(db: AsyncSession, request: Request) -> ColumnEl
return or_(1 == 1) return or_(1 == 1)
# 获取数据范围 # 获取数据范围
unique_data_scopes = {} data_scope_ids = set()
for role in request.user.roles: for role in request.user.roles:
for scope in role.scopes: for scope in role.scopes:
if scope.status: if scope.status:
unique_data_scopes[scope.id] = scope data_scope_ids.add(scope.id)
# 转换为列表
data_scopes = list(unique_data_scopes.values())
# 无规则用户不做过滤 # 无规则用户不做过滤
if not data_scopes: if not list(data_scope_ids):
return or_(1 == 1) return or_(1 == 1)
# 获取数据范围规则 # 获取数据范围规则
unique_data_rules = {} unique_data_rules = {}
for data_scope in data_scopes: for data_scope_id in list(data_scope_ids):
data_scope_with_relation = await data_scope_dao.get_with_relation(db, data_scope.id) data_scope_with_relation = await data_scope_dao.get_with_relation(db, data_scope_id)
for rule in data_scope_with_relation.rules: for rule in data_scope_with_relation.rules:
unique_data_rules[rule.id] = rule unique_data_rules[rule.id] = rule
+1 -1
View File
@@ -9,6 +9,6 @@ from backend.plugin.code_generator.api.v1.gen import router as gen_router
v1 = APIRouter(prefix=f'{settings.FASTAPI_API_V1_PATH}/gen', tags=['代码生成']) v1 = APIRouter(prefix=f'{settings.FASTAPI_API_V1_PATH}/gen', tags=['代码生成'])
v1.include_router(gen_router, prefix='/tables')
v1.include_router(business_router, prefix='/businesses') v1.include_router(business_router, prefix='/businesses')
v1.include_router(model_router, prefix='/models') v1.include_router(model_router, prefix='/models')
v1.include_router(gen_router, prefix='/codes')
@@ -20,12 +20,6 @@ from backend.plugin.code_generator.service.column_service import gen_model_servi
router = APIRouter() router = APIRouter()
@router.get('/all', summary='获取所有代码生成业务', dependencies=[DependsJwtAuth])
async def get_all_businesses() -> ResponseSchemaModel[list[GetGenBusinessDetail]]:
data = await gen_business_service.get_all()
return response_base.success(data=data)
@router.get('/{pk}', summary='获取代码生成业务详情', dependencies=[DependsJwtAuth]) @router.get('/{pk}', summary='获取代码生成业务详情', dependencies=[DependsJwtAuth])
async def get_business( async def get_business(
pk: Annotated[int, Path(description='业务 ID')], pk: Annotated[int, Path(description='业务 ID')],
@@ -34,11 +28,17 @@ async def get_business(
return response_base.success(data=data) return response_base.success(data=data)
@router.get('', summary='获取所有代码生成业务', dependencies=[DependsJwtAuth])
async def get_all_businesses() -> ResponseSchemaModel[list[GetGenBusinessDetail]]:
data = await gen_business_service.get_all()
return response_base.success(data=data)
@router.get('/{pk}/models', summary='获取代码生成业务所有模型', dependencies=[DependsJwtAuth]) @router.get('/{pk}/models', summary='获取代码生成业务所有模型', dependencies=[DependsJwtAuth])
async def get_business_all_models( async def get_business_all_models(
pk: Annotated[int, Path(description='业务 ID')], pk: Annotated[int, Path(description='业务 ID')],
) -> ResponseSchemaModel[list[GetGenModelDetail]]: ) -> ResponseSchemaModel[list[GetGenModelDetail]]:
data = await gen_model_service.get_by_business(business_id=pk) data = await gen_model_service.get_models(business_id=pk)
return response_base.success(data=data) return response_base.success(data=data)
@@ -47,7 +47,7 @@ async def get_business_all_models(
summary='创建代码生成业务', summary='创建代码生成业务',
deprecated=True, deprecated=True,
dependencies=[ dependencies=[
Depends(RequestPermission('gen:code:business:add')), Depends(RequestPermission('codegen:business:add')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -60,7 +60,7 @@ async def create_business(obj: CreateGenBusinessParam) -> ResponseModel:
'/{pk}', '/{pk}',
summary='更新代码生成业务', summary='更新代码生成业务',
dependencies=[ dependencies=[
Depends(RequestPermission('gen:code:business:edit')), Depends(RequestPermission('codegen:business:edit')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -77,7 +77,7 @@ async def update_business(
'/{pk}', '/{pk}',
summary='删除代码生成业务', summary='删除代码生成业务',
dependencies=[ dependencies=[
Depends(RequestPermission('gen:code:business:del')), Depends(RequestPermission('codegen:business:del')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -30,7 +30,7 @@ async def get_model(pk: Annotated[int, Path(description='模型 ID')]) -> Respon
'', '',
summary='创建代码生成模型', summary='创建代码生成模型',
dependencies=[ dependencies=[
Depends(RequestPermission('gen:code:model:add')), Depends(RequestPermission('codegen:model:add')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -43,7 +43,7 @@ async def create_model(obj: CreateGenModelParam) -> ResponseModel:
'/{pk}', '/{pk}',
summary='更新代码生成模型', summary='更新代码生成模型',
dependencies=[ dependencies=[
Depends(RequestPermission('gen:code:model:edit')), Depends(RequestPermission('codegen:model:edit')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -58,7 +58,7 @@ async def update_model(pk: Annotated[int, Path(description='模型 ID')], obj: U
'/{pk}', '/{pk}',
summary='删除代码生成模型', summary='删除代码生成模型',
dependencies=[ dependencies=[
Depends(RequestPermission('gen:code:model:del')), Depends(RequestPermission('codegen:model:del')),
DependsRBAC, DependsRBAC,
], ],
) )
+8 -8
View File
@@ -16,7 +16,7 @@ from backend.plugin.code_generator.service.gen_service import gen_service
router = APIRouter() router = APIRouter()
@router.get('', summary='获取数据库表') @router.get('/tables', summary='获取数据库表')
async def get_all_tables( async def get_all_tables(
table_schema: Annotated[str, Query(description='数据库名')] = 'fba', table_schema: Annotated[str, Query(description='数据库名')] = 'fba',
) -> ResponseSchemaModel[list[str]]: ) -> ResponseSchemaModel[list[str]]:
@@ -25,10 +25,10 @@ async def get_all_tables(
@router.post( @router.post(
'/import', '/imports',
summary='导入代码生成业务和模型列', summary='导入代码生成业务和模型列',
dependencies=[ dependencies=[
Depends(RequestPermission('gen:code:import')), Depends(RequestPermission('codegen:table:import')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -37,24 +37,24 @@ async def import_table(obj: ImportParam) -> ResponseModel:
return response_base.success() return response_base.success()
@router.get('/{pk}/preview', summary='生成代码预览', dependencies=[DependsJwtAuth]) @router.get('/{pk}/previews', summary='代码生成预览', dependencies=[DependsJwtAuth])
async def preview_code(pk: Annotated[int, Path(description='业务 ID')]) -> ResponseSchemaModel[dict[str, bytes]]: async def preview_code(pk: Annotated[int, Path(description='业务 ID')]) -> ResponseSchemaModel[dict[str, bytes]]:
data = await gen_service.preview(pk=pk) data = await gen_service.preview(pk=pk)
return response_base.success(data=data) return response_base.success(data=data)
@router.get('/{pk}/code/path', summary='获取代码生成路径', dependencies=[DependsJwtAuth]) @router.get('/{pk}/paths', summary='获取代码生成路径', dependencies=[DependsJwtAuth])
async def generate_path(pk: Annotated[int, Path(description='业务 ID')]) -> ResponseSchemaModel[list[str]]: async def get_generate_paths(pk: Annotated[int, Path(description='业务 ID')]) -> ResponseSchemaModel[list[str]]:
data = await gen_service.get_generate_path(pk=pk) data = await gen_service.get_generate_path(pk=pk)
return response_base.success(data=data) return response_base.success(data=data)
@router.post( @router.post(
'/{pk}/code', '/{pk}/generation',
summary='代码生成', summary='代码生成',
description='文件磁盘写入,请谨慎操作', description='文件磁盘写入,请谨慎操作',
dependencies=[ dependencies=[
Depends(RequestPermission('gen:code:write')), Depends(RequestPermission('codegen:local:write')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -36,7 +36,7 @@ class GenModelService:
return types return types
@staticmethod @staticmethod
async def get_by_business(*, business_id: int) -> Sequence[GenColumn]: async def get_models(*, business_id: int) -> Sequence[GenColumn]:
""" """
获取指定业务的所有模型 获取指定业务的所有模型
@@ -99,7 +99,7 @@ class GenService:
:param business: 业务对象 :param business: 业务对象
:return: :return:
""" """
gen_models = await gen_model_service.get_by_business(business_id=business.id) gen_models = await gen_model_service.get_models(business_id=business.id)
if not gen_models: if not gen_models:
raise errors.NotFoundError(msg='代码生成模型表为空') raise errors.NotFoundError(msg='代码生成模型表为空')
@@ -4,7 +4,12 @@ from typing import Annotated
from fastapi import APIRouter, Depends, Path, Query from fastapi import APIRouter, Depends, Path, Query
from backend.app.{{ app_name }}.schema.{{ table_name }} import Create{{ schema_name }}Param, Get{{ schema_name }}Detail, Update{{ schema_name }}Param from backend.app.{{ app_name }}.schema.{{ table_name }} import (
Create{{ schema_name }}Param,
Delete{{ schema_name }}Param,
Get{{ schema_name }}Detail,
Update{{ schema_name }}Param,
)
from backend.app.{{ app_name }}.service.{{ table_name }}_service import {{ table_name }}_service from backend.app.{{ app_name }}.service.{{ table_name }}_service import {{ table_name }}_service
from backend.common.pagination import DependsPagination, PageData, paging_data from backend.common.pagination import DependsPagination, PageData, paging_data
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
@@ -30,7 +35,7 @@ async def get_{{ table_name }}(pk: Annotated[int, Path(description='{{ doc_comme
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_{{ table_name }}s(db: CurrentSession) -> ResponseSchemaModel[PageData[Get{{ schema_name }}Detail]]: async def get_{{ table_name }}s_paged(db: CurrentSession) -> ResponseSchemaModel[PageData[Get{{ schema_name }}Detail]]:
{{ table_name }}_select = await {{ table_name }}_service.get_select() {{ table_name }}_select = await {{ table_name }}_service.get_select()
page_data = await paging_data(db, {{ table_name }}_select) page_data = await paging_data(db, {{ table_name }}_select)
return response_base.success(data=page_data) return response_base.success(data=page_data)
@@ -72,8 +77,8 @@ async def update_{{ table_name }}(pk: Annotated[int, Path(description='{{ doc_co
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_{{ table_name }}(pk: Annotated[list[int], Query(description='{{ doc_comment }} ID 列表')]) -> ResponseModel: async def delete_{{ table_name }}s(obj: Delete{{ schema_name }}Param) -> ResponseModel:
count = await {{ table_name }}_service.delete(pk=pk) count = await {{ table_name }}_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
@@ -55,15 +55,15 @@ class CRUD{{ class_name }}(CRUDPlus[{{ schema_name }}]):
""" """
return await self.update_model(db, pk, obj) return await self.update_model(db, pk, obj)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除{{ doc_comment }} 批量删除{{ doc_comment }}
:param db: 数据库会话 :param db: 数据库会话
:param pk: {{ doc_comment }} ID :param pks: {{ doc_comment }} ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk) return await self.delete_model_by_column(db, allow_multiple=True, id__in=pks)
{{ instance_name }}_dao: CRUD{{ class_name }} = CRUD{{ class_name }}({{ class_name }}) {{ instance_name }}_dao: CRUD{{ class_name }} = CRUD{{ class_name }}({{ class_name }})
@@ -23,6 +23,10 @@ class Update{{ schema_name }}Param({{ schema_name }}SchemaBase):
"""更新{{ doc_comment }}参数""" """更新{{ doc_comment }}参数"""
class Delete{{ schema_name }}Param({{ schema_name }}SchemaBase):
"""删除{{ doc_comment }}参数"""
class Get{{ schema_name }}Detail({{ schema_name }}SchemaBase): class Get{{ schema_name }}Detail({{ schema_name }}SchemaBase):
"""{{ doc_comment }}详情""" """{{ doc_comment }}详情"""
@@ -6,7 +6,7 @@ from sqlalchemy import Select
from backend.app.{{ app_name }}.crud.crud_{{ table_name }} import {{ table_name }}_dao from backend.app.{{ app_name }}.crud.crud_{{ table_name }} import {{ table_name }}_dao
from backend.app.{{ app_name }}.model import {{ class_name }} from backend.app.{{ app_name }}.model import {{ class_name }}
from backend.app.{{ app_name }}.schema.{{ table_name }} import Create{{ schema_name }}Param, Update{{ schema_name }}Param from backend.app.{{ app_name }}.schema.{{ table_name }} import Create{{ schema_name }}Param, Delete{{ schema_name }}Param, Update{{ schema_name }}Param
from backend.common.exception import errors from backend.common.exception import errors
from backend.database.db import async_db_session from backend.database.db import async_db_session
@@ -63,15 +63,15 @@ class {{ class_name }}Service:
return count return count
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: Delete{{ schema_name }}Param) -> int:
""" """
删除{{ doc_comment }} 删除{{ doc_comment }}
:param pk: {{ doc_comment }} ID 列表 :param obj: {{ doc_comment }} ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await {{ table_name }}_dao.delete(db, pk) count = await {{ table_name }}_dao.delete(db, obj.pks)
return count return count
+6 -64
View File
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends, Path, Query from fastapi import APIRouter, Body, Depends, Path, Query
from backend.common.pagination import DependsPagination, PageData, paging_data from backend.common.pagination import DependsPagination, PageData, paging_data
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
@@ -13,7 +13,6 @@ from backend.database.db import CurrentSession
from backend.plugin.config.schema.config import ( from backend.plugin.config.schema.config import (
CreateConfigParam, CreateConfigParam,
GetConfigDetail, GetConfigDetail,
SaveBuiltInConfigParam,
UpdateConfigParam, UpdateConfigParam,
) )
from backend.plugin.config.service.config_service import config_service from backend.plugin.config.service.config_service import config_service
@@ -21,66 +20,9 @@ from backend.plugin.config.service.config_service import config_service
router = APIRouter() router = APIRouter()
@router.get('/website', summary='获取网站参数配置', dependencies=[DependsJwtAuth])
async def get_website_config() -> ResponseSchemaModel[list[GetConfigDetail]]:
config = await config_service.get_built_in_config('website')
return response_base.success(data=config)
@router.post(
'/website',
summary='保存网站参数配置',
dependencies=[
Depends(RequestPermission('sys:config:website:add')),
DependsRBAC,
],
)
async def save_website_config(objs: list[SaveBuiltInConfigParam]) -> ResponseModel:
await config_service.save_built_in_config(objs, 'website')
return response_base.success()
@router.get('/protocol', summary='获取用户协议', dependencies=[DependsJwtAuth])
async def get_protocol_config() -> ResponseSchemaModel[list[GetConfigDetail]]:
config = await config_service.get_built_in_config('protocol')
return response_base.success(data=config)
@router.post(
'/protocol',
summary='保存用户协议',
dependencies=[
Depends(RequestPermission('sys:config:protocol:add')),
DependsRBAC,
],
)
async def save_protocol_config(objs: list[SaveBuiltInConfigParam]) -> ResponseModel:
await config_service.save_built_in_config(objs, 'protocol')
return response_base.success()
@router.get('/policy', summary='获取用户政策', dependencies=[DependsJwtAuth])
async def get_policy_config() -> ResponseSchemaModel[list[GetConfigDetail]]:
config = await config_service.get_built_in_config('policy')
return response_base.success(data=config)
@router.post(
'/policy',
summary='保存用户政策',
dependencies=[
Depends(RequestPermission('sys:config:policy:add')),
DependsRBAC,
],
)
async def save_policy_config(objs: list[SaveBuiltInConfigParam]) -> ResponseModel:
await config_service.save_built_in_config(objs, 'policy')
return response_base.success()
@router.get('/{pk}', summary='获取参数配置详情', dependencies=[DependsJwtAuth]) @router.get('/{pk}', summary='获取参数配置详情', dependencies=[DependsJwtAuth])
async def get_config(pk: Annotated[int, Path(description='参数配置 ID')]) -> ResponseSchemaModel[GetConfigDetail]: async def get_config(pk: Annotated[int, Path(description='参数配置 ID')]) -> ResponseSchemaModel[GetConfigDetail]:
config = await config_service.get(pk) config = await config_service.get(pk=pk)
return response_base.success(data=config) return response_base.success(data=config)
@@ -92,10 +34,10 @@ async def get_config(pk: Annotated[int, Path(description='参数配置 ID')]) ->
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_configs( async def get_configs_paged(
db: CurrentSession, db: CurrentSession,
name: Annotated[str | None, Query(description='参数配置名称')] = None, name: Annotated[str | None, Query(description='参数配置名称')] = None,
type: Annotated[str | None, Query()] = None, type: Annotated[str | None, Query(description='参数配置类型')] = None,
) -> ResponseSchemaModel[PageData[GetConfigDetail]]: ) -> ResponseSchemaModel[PageData[GetConfigDetail]]:
config_select = await config_service.get_select(name=name, type=type) config_select = await config_service.get_select(name=name, type=type)
page_data = await paging_data(db, config_select) page_data = await paging_data(db, config_select)
@@ -138,8 +80,8 @@ async def update_config(pk: Annotated[int, Path(description='参数配置 ID')],
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_config(pk: Annotated[list[int], Query(description='参数配置 ID 列表')]) -> ResponseModel: async def delete_configs(pks: Annotated[list[int], Body(description='参数配置 ID 列表')]) -> ResponseModel:
count = await config_service.delete(pk=pk) count = await config_service.delete(pks=pks)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
+4 -26
View File
@@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Sequence
from sqlalchemy import Select from sqlalchemy import Select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@@ -24,27 +23,6 @@ class CRUDConfig(CRUDPlus[Config]):
""" """
return await self.select_model_by_column(db, id=pk, type__not_in=settings.CONFIG_BUILT_IN_TYPES) return await self.select_model_by_column(db, id=pk, type__not_in=settings.CONFIG_BUILT_IN_TYPES)
async def get_by_type(self, db: AsyncSession, type: str) -> Sequence[Config]:
"""
通过类型获取参数配置
:param db: 数据库会话
:param type: 参数配置类型
:return:
"""
return await self.select_models(db, type=type)
async def get_by_key_and_type(self, db: AsyncSession, key: str, type: str) -> Config | None:
"""
通过键名和类型获取参数配置
:param db: 数据库会话
:param key: 参数配置键名
:param type: 参数配置类型
:return:
"""
return await self.select_model_by_column(db, key=key, type=type)
async def get_by_key(self, db: AsyncSession, key: str) -> Config | None: async def get_by_key(self, db: AsyncSession, key: str) -> Config | None:
""" """
通过键名获取参数配置 通过键名获取参数配置
@@ -93,16 +71,16 @@ class CRUDConfig(CRUDPlus[Config]):
""" """
return await self.update_model(db, pk, obj) return await self.update_model(db, pk, obj)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除参数配置 批量删除参数配置
:param db: 数据库会话 :param db: 数据库会话
:param pk: 参数配置 ID 列表 :param pks: 参数配置 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column( return await self.delete_model_by_column(
db, allow_multiple=True, id__in=pk, type__not_in=settings.CONFIG_BUILT_IN_TYPES db, allow_multiple=True, id__in=pks, type__not_in=settings.CONFIG_BUILT_IN_TYPES
) )
-8
View File
@@ -7,14 +7,6 @@ from pydantic import ConfigDict, Field
from backend.common.schema import SchemaBase from backend.common.schema import SchemaBase
class SaveBuiltInConfigParam(SchemaBase):
"""保存内置参数配置参数"""
name: str = Field(description='参数配置名称')
key: str = Field(description='参数配置键名')
value: str = Field(description='参数配置值')
class ConfigSchemaBase(SchemaBase): class ConfigSchemaBase(SchemaBase):
"""参数配置基础模型""" """参数配置基础模型"""
@@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Sequence
from sqlalchemy import Select from sqlalchemy import Select
@@ -11,7 +10,6 @@ from backend.plugin.config.crud.crud_config import config_dao
from backend.plugin.config.model import Config from backend.plugin.config.model import Config
from backend.plugin.config.schema.config import ( from backend.plugin.config.schema.config import (
CreateConfigParam, CreateConfigParam,
SaveBuiltInConfigParam,
UpdateConfigParam, UpdateConfigParam,
) )
@@ -20,37 +18,7 @@ class ConfigService:
"""参数配置服务类""" """参数配置服务类"""
@staticmethod @staticmethod
async def get_built_in_config(type: str) -> Sequence[Config]: async def get(*, pk: int) -> Config:
"""
获取内置参数配置
:param type: 参数配置类型
:return:
"""
async with async_db_session() as db:
return await config_dao.get_by_type(db, type)
@staticmethod
async def save_built_in_config(objs: list[SaveBuiltInConfigParam], type: str) -> None:
"""
保存内置参数配置
:param objs: 参数配置参数列表
:param type: 参数配置类型
:return:
"""
async with async_db_session.begin() as db:
for obj in objs:
config = await config_dao.get_by_key_and_type(db, obj.key, type)
if config is None:
if await config_dao.get_by_key(db, obj.key):
raise errors.ForbiddenError(msg=f'参数配置 {obj.key} 已存在')
await config_dao.create_model(db, obj, flush=True, type=type)
else:
await config_dao.update_model(db, config.id, obj, type=type)
@staticmethod
async def get(pk: int) -> Config:
""" """
获取参数配置详情 获取参数配置详情
@@ -111,15 +79,15 @@ class ConfigService:
return count return count
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, pks: list[int]) -> int:
""" """
删除参数配置 批量删除参数配置
:param pk: 参数配置 ID 列表 :param pks: 参数配置 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await config_dao.delete(db, pk) count = await config_dao.delete(db, pks)
return count return count
+12 -11
View File
@@ -12,6 +12,7 @@ from backend.common.security.rbac import DependsRBAC
from backend.database.db import CurrentSession from backend.database.db import CurrentSession
from backend.plugin.dict.schema.dict_data import ( from backend.plugin.dict.schema.dict_data import (
CreateDictDataParam, CreateDictDataParam,
DeleteDictDataParam,
GetDictDataDetail, GetDictDataDetail,
GetDictDataWithRelation, GetDictDataWithRelation,
UpdateDictDataParam, UpdateDictDataParam,
@@ -21,7 +22,7 @@ from backend.plugin.dict.service.dict_data_service import dict_data_service
router = APIRouter() router = APIRouter()
@router.get('/{pk}', summary='获取字典详情', dependencies=[DependsJwtAuth]) @router.get('/{pk}', summary='获取字典数据详情', dependencies=[DependsJwtAuth])
async def get_dict_data( async def get_dict_data(
pk: Annotated[int, Path(description='字典数据 ID')], pk: Annotated[int, Path(description='字典数据 ID')],
) -> ResponseSchemaModel[GetDictDataWithRelation]: ) -> ResponseSchemaModel[GetDictDataWithRelation]:
@@ -31,13 +32,13 @@ async def get_dict_data(
@router.get( @router.get(
'', '',
summary='分页获取所有字典', summary='分页获取所有字典数据',
dependencies=[ dependencies=[
DependsJwtAuth, DependsJwtAuth,
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_dict_datas( async def get_dict_datas_paged(
db: CurrentSession, db: CurrentSession,
label: Annotated[str | None, Query(description='字典数据标签')] = None, label: Annotated[str | None, Query(description='字典数据标签')] = None,
value: Annotated[str | None, Query(description='字典数据键值')] = None, value: Annotated[str | None, Query(description='字典数据键值')] = None,
@@ -50,9 +51,9 @@ async def get_pagination_dict_datas(
@router.post( @router.post(
'', '',
summary='创建字典', summary='创建字典数据',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:dict:data:add')), Depends(RequestPermission('dict:data:add')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -63,9 +64,9 @@ async def create_dict_data(obj: CreateDictDataParam) -> ResponseModel:
@router.put( @router.put(
'/{pk}', '/{pk}',
summary='更新字典', summary='更新字典数据',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:dict:data:edit')), Depends(RequestPermission('dict:data:edit')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -80,14 +81,14 @@ async def update_dict_data(
@router.delete( @router.delete(
'', '',
summary='批量删除字典', summary='批量删除字典数据',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:dict:data:del')), Depends(RequestPermission('dict:data:del')),
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_dict_data(pk: Annotated[list[int], Query(description='字典数据 ID 列表')]) -> ResponseModel: async def delete_dict_datas(obj: DeleteDictDataParam) -> ResponseModel:
count = await dict_data_service.delete(pk=pk) count = await dict_data_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
+12 -7
View File
@@ -10,7 +10,12 @@ from backend.common.security.jwt import DependsJwtAuth
from backend.common.security.permission import RequestPermission from backend.common.security.permission import RequestPermission
from backend.common.security.rbac import DependsRBAC from backend.common.security.rbac import DependsRBAC
from backend.database.db import CurrentSession from backend.database.db import CurrentSession
from backend.plugin.dict.schema.dict_type import CreateDictTypeParam, GetDictTypeDetail, UpdateDictTypeParam from backend.plugin.dict.schema.dict_type import (
CreateDictTypeParam,
DeleteDictTypeParam,
GetDictTypeDetail,
UpdateDictTypeParam,
)
from backend.plugin.dict.service.dict_type_service import dict_type_service from backend.plugin.dict.service.dict_type_service import dict_type_service
router = APIRouter() router = APIRouter()
@@ -24,7 +29,7 @@ router = APIRouter()
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_dict_types( async def get_dict_types_paged(
db: CurrentSession, db: CurrentSession,
name: Annotated[str | None, Query(description='字典类型名称')] = None, name: Annotated[str | None, Query(description='字典类型名称')] = None,
code: Annotated[str | None, Query(description='字典类型编码')] = None, code: Annotated[str | None, Query(description='字典类型编码')] = None,
@@ -39,7 +44,7 @@ async def get_pagination_dict_types(
'', '',
summary='创建字典类型', summary='创建字典类型',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:dict:type:add')), Depends(RequestPermission('dict:type:add')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -52,7 +57,7 @@ async def create_dict_type(obj: CreateDictTypeParam) -> ResponseModel:
'/{pk}', '/{pk}',
summary='更新字典类型', summary='更新字典类型',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:dict:type:edit')), Depends(RequestPermission('dict:type:edit')),
DependsRBAC, DependsRBAC,
], ],
) )
@@ -69,12 +74,12 @@ async def update_dict_type(
'', '',
summary='批量删除字典类型', summary='批量删除字典类型',
dependencies=[ dependencies=[
Depends(RequestPermission('sys:dict:type:del')), Depends(RequestPermission('dict:type:del')),
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_dict_type(pk: Annotated[list[int], Query(description='字典类型 ID 列表')]) -> ResponseModel: async def delete_dict_types(obj: DeleteDictTypeParam) -> ResponseModel:
count = await dict_type_service.delete(pk=pk) count = await dict_type_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
+4 -4
View File
@@ -72,15 +72,15 @@ class CRUDDictData(CRUDPlus[DictData]):
""" """
return await self.update_model(db, pk, obj) return await self.update_model(db, pk, obj)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除字典数据 批量删除字典数据
:param db: 数据库会话 :param db: 数据库会话
:param pk: 字典数据 ID 列表 :param pks: 字典数据 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk) return await self.delete_model_by_column(db, allow_multiple=True, id__in=pks)
async def get_with_relation(self, db: AsyncSession, pk: int) -> DictData | None: async def get_with_relation(self, db: AsyncSession, pk: int) -> DictData | None:
""" """
+4 -4
View File
@@ -72,15 +72,15 @@ class CRUDDictType(CRUDPlus[DictType]):
""" """
return await self.update_model(db, pk, obj) return await self.update_model(db, pk, obj)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除字典类型 批量删除字典类型
:param db: 数据库会话 :param db: 数据库会话
:param pk: 字典类型 ID 列表 :param pks: 字典类型 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk) return await self.delete_model_by_column(db, allow_multiple=True, id__in=pks)
dict_type_dao: CRUDDictType = CRUDDictType(DictType) dict_type_dao: CRUDDictType = CRUDDictType(DictType)
+6
View File
@@ -28,6 +28,12 @@ class UpdateDictDataParam(DictDataSchemaBase):
"""更新字典数据参数""" """更新字典数据参数"""
class DeleteDictDataParam(SchemaBase):
"""删除字典数据参数"""
pks: list[int] = Field(description='字典数据 ID 列表')
class GetDictDataDetail(DictDataSchemaBase): class GetDictDataDetail(DictDataSchemaBase):
"""字典数据详情""" """字典数据详情"""
+6
View File
@@ -25,6 +25,12 @@ class UpdateDictTypeParam(DictTypeSchemaBase):
"""更新字典类型参数""" """更新字典类型参数"""
class DeleteDictTypeParam(SchemaBase):
"""删除字典类型参数"""
pks: list[int] = Field(description='字典类型 ID 列表')
class GetDictTypeDetail(DictTypeSchemaBase): class GetDictTypeDetail(DictTypeSchemaBase):
"""字典类型详情""" """字典类型详情"""
@@ -7,7 +7,7 @@ from backend.database.db import async_db_session
from backend.plugin.dict.crud.crud_dict_data import dict_data_dao from backend.plugin.dict.crud.crud_dict_data import dict_data_dao
from backend.plugin.dict.crud.crud_dict_type import dict_type_dao from backend.plugin.dict.crud.crud_dict_type import dict_type_dao
from backend.plugin.dict.model import DictData from backend.plugin.dict.model import DictData
from backend.plugin.dict.schema.dict_data import CreateDictDataParam, UpdateDictDataParam from backend.plugin.dict.schema.dict_data import CreateDictDataParam, DeleteDictDataParam, UpdateDictDataParam
class DictDataService: class DictDataService:
@@ -79,15 +79,15 @@ class DictDataService:
return count return count
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: DeleteDictDataParam) -> int:
""" """
删除字典数据 批量删除字典数据
:param pk: 字典数据 ID 列表 :param obj: 字典数据 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await dict_data_dao.delete(db, pk) count = await dict_data_dao.delete(db, obj.pks)
return count return count
@@ -5,7 +5,7 @@ from sqlalchemy import Select
from backend.common.exception import errors from backend.common.exception import errors
from backend.database.db import async_db_session from backend.database.db import async_db_session
from backend.plugin.dict.crud.crud_dict_type import dict_type_dao from backend.plugin.dict.crud.crud_dict_type import dict_type_dao
from backend.plugin.dict.schema.dict_type import CreateDictTypeParam, UpdateDictTypeParam from backend.plugin.dict.schema.dict_type import CreateDictTypeParam, DeleteDictTypeParam, UpdateDictTypeParam
class DictTypeService: class DictTypeService:
@@ -57,15 +57,15 @@ class DictTypeService:
return count return count
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: DeleteDictTypeParam) -> int:
""" """
删除字典类型 批量删除字典类型
:param pk: 字典类型 ID 列表 :param obj: 字典类型 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await dict_type_dao.delete(db, pk) count = await dict_type_dao.delete(db, obj.pks)
return count return count
+6 -8
View File
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends, Path, Query, Request from fastapi import APIRouter, Depends, Path
from backend.common.pagination import DependsPagination, PageData, paging_data from backend.common.pagination import DependsPagination, PageData, paging_data
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
@@ -10,16 +10,14 @@ from backend.common.security.jwt import DependsJwtAuth
from backend.common.security.permission import RequestPermission from backend.common.security.permission import RequestPermission
from backend.common.security.rbac import DependsRBAC from backend.common.security.rbac import DependsRBAC
from backend.database.db import CurrentSession from backend.database.db import CurrentSession
from backend.plugin.notice.schema.notice import CreateNoticeParam, GetNoticeDetail, UpdateNoticeParam from backend.plugin.notice.schema.notice import CreateNoticeParam, DeleteNoticeParam, GetNoticeDetail, UpdateNoticeParam
from backend.plugin.notice.service.notice_service import notice_service from backend.plugin.notice.service.notice_service import notice_service
router = APIRouter() router = APIRouter()
@router.get('/{pk}', summary='获取通知公告详情', dependencies=[DependsJwtAuth]) @router.get('/{pk}', summary='获取通知公告详情', dependencies=[DependsJwtAuth])
async def get_notice( async def get_notice(pk: Annotated[int, Path(description='通知公告 ID')]) -> ResponseSchemaModel[GetNoticeDetail]:
request: Request, pk: Annotated[int, Path(description='通知公告 ID')]
) -> ResponseSchemaModel[GetNoticeDetail]:
notice = await notice_service.get(pk=pk) notice = await notice_service.get(pk=pk)
return response_base.success(data=notice) return response_base.success(data=notice)
@@ -32,7 +30,7 @@ async def get_notice(
DependsPagination, DependsPagination,
], ],
) )
async def get_pagination_notices(db: CurrentSession) -> ResponseSchemaModel[PageData[GetNoticeDetail]]: async def get_notices_paged(db: CurrentSession) -> ResponseSchemaModel[PageData[GetNoticeDetail]]:
notice_select = await notice_service.get_select() notice_select = await notice_service.get_select()
page_data = await paging_data(db, notice_select) page_data = await paging_data(db, notice_select)
return response_base.success(data=page_data) return response_base.success(data=page_data)
@@ -74,8 +72,8 @@ async def update_notice(pk: Annotated[int, Path(description='通知公告 ID')],
DependsRBAC, DependsRBAC,
], ],
) )
async def delete_notice(pk: Annotated[list[int], Query(description='通知公告 ID 列表')]) -> ResponseModel: async def delete_notices(obj: DeleteNoticeParam) -> ResponseModel:
count = await notice_service.delete(pk=pk) count = await notice_service.delete(obj=obj)
if count > 0: if count > 0:
return response_base.success() return response_base.success()
return response_base.fail() return response_base.fail()
+4 -4
View File
@@ -57,15 +57,15 @@ class CRUDNotice(CRUDPlus[Notice]):
""" """
return await self.update_model(db, pk, obj) return await self.update_model(db, pk, obj)
async def delete(self, db: AsyncSession, pk: list[int]) -> int: async def delete(self, db: AsyncSession, pks: list[int]) -> int:
""" """
删除通知公告 批量删除通知公告
:param db: 数据库会话 :param db: 数据库会话
:param pk: 通知公告 ID 列表 :param pks: 通知公告 ID 列表
:return: :return:
""" """
return await self.delete_model_by_column(db, allow_multiple=True, id__in=pk) return await self.delete_model_by_column(db, allow_multiple=True, id__in=pks)
notice_dao: CRUDNotice = CRUDNotice(Notice) notice_dao: CRUDNotice = CRUDNotice(Notice)
+6
View File
@@ -27,6 +27,12 @@ class UpdateNoticeParam(NoticeSchemaBase):
"""更新通知公告参数""" """更新通知公告参数"""
class DeleteNoticeParam(SchemaBase):
"""删除通知公告参数"""
pks: list[int] = Field(description='通知公告 ID 列表')
class GetNoticeDetail(NoticeSchemaBase): class GetNoticeDetail(NoticeSchemaBase):
"""通知公告详情""" """通知公告详情"""
@@ -8,7 +8,7 @@ from backend.common.exception import errors
from backend.database.db import async_db_session from backend.database.db import async_db_session
from backend.plugin.notice.crud.crud_notice import notice_dao from backend.plugin.notice.crud.crud_notice import notice_dao
from backend.plugin.notice.model import Notice from backend.plugin.notice.model import Notice
from backend.plugin.notice.schema.notice import CreateNoticeParam, UpdateNoticeParam from backend.plugin.notice.schema.notice import CreateNoticeParam, DeleteNoticeParam, UpdateNoticeParam
class NoticeService: class NoticeService:
@@ -68,15 +68,15 @@ class NoticeService:
return count return count
@staticmethod @staticmethod
async def delete(*, pk: list[int]) -> int: async def delete(*, obj: DeleteNoticeParam) -> int:
""" """
删除通知公告 批量删除通知公告
:param pk: 通知公告 ID 列表 :param obj: 通知公告 ID 列表
:return: :return:
""" """
async with async_db_session.begin() as db: async with async_db_session.begin() as db:
count = await notice_dao.delete(db, pk) count = await notice_dao.delete(db, obj.pks)
return count return count
+2 -2
View File
@@ -17,7 +17,7 @@ _github_oauth2 = FastAPIOAuth20(_github_client, redirect_route_name='github_logi
@router.get('', summary='获取 Github 授权链接') @router.get('', summary='获取 Github 授权链接')
async def github_oauth2(request: Request) -> ResponseSchemaModel[str]: async def get_github_oauth2_url(request: Request) -> ResponseSchemaModel[str]:
auth_url = await _github_client.get_authorization_url(redirect_uri=f'{request.url}/callback') auth_url = await _github_client.get_authorization_url(redirect_uri=f'{request.url}/callback')
return response_base.success(data=auth_url) return response_base.success(data=auth_url)
@@ -28,7 +28,7 @@ async def github_oauth2(request: Request) -> ResponseSchemaModel[str]:
description='Github 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户', description='Github 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户',
dependencies=[Depends(RateLimiter(times=5, minutes=1))], dependencies=[Depends(RateLimiter(times=5, minutes=1))],
) )
async def github_login( async def github_oauth2_callback(
request: Request, request: Request,
response: Response, response: Response,
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
+2 -2
View File
@@ -20,7 +20,7 @@ _linux_do_oauth2 = FastAPIOAuth20(_linux_do_client, redirect_route_name='linux_d
@router.get('', summary='获取 LinuxDo 授权链接') @router.get('', summary='获取 LinuxDo 授权链接')
async def linux_do_oauth2(request: Request) -> ResponseSchemaModel[str]: async def get_linux_do_oauth2_url(request: Request) -> ResponseSchemaModel[str]:
auth_url = await _linux_do_client.get_authorization_url(redirect_uri=f'{request.url}/callback') auth_url = await _linux_do_client.get_authorization_url(redirect_uri=f'{request.url}/callback')
return response_base.success(data=auth_url) return response_base.success(data=auth_url)
@@ -31,7 +31,7 @@ async def linux_do_oauth2(request: Request) -> ResponseSchemaModel[str]:
description='LinuxDo 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户', description='LinuxDo 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户',
dependencies=[Depends(RateLimiter(times=5, minutes=1))], dependencies=[Depends(RateLimiter(times=5, minutes=1))],
) )
async def linux_do_login( async def linux_do_oauth2_callback(
request: Request, request: Request,
response: Response, response: Response,
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
+29 -33
View File
@@ -12,7 +12,7 @@ values (1, '概览', 'Dashboard', 'dashboard', 0, 'ant-design:dashboard-outline
(8, '工作台', 'Workspace', 'workspace', 1, 'carbon:workspace', 1, '/dashboard/workspace/index', null, 1, 1, 1, '', null, 1, '2025-06-09 17:57:09', null), (8, '工作台', 'Workspace', 'workspace', 1, 'carbon:workspace', 1, '/dashboard/workspace/index', null, 1, 1, 1, '', null, 1, '2025-06-09 17:57:09', null),
(9, '文档', 'Document', 'document', 1, 'lucide:book-open-text', 4, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://fastapi-practices.github.io/fastapi_best_architecture_docs', null, 6, '2025-06-09 17:59:44', null), (9, '文档', 'Document', 'document', 1, 'lucide:book-open-text', 4, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://fastapi-practices.github.io/fastapi_best_architecture_docs', null, 6, '2025-06-09 17:59:44', null),
(10, 'Github', 'Github', 'github', 2, 'ant-design:github-filled', 4, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://github.com/fastapi-practices/fastapi_best_architecture', null, 6, '2025-06-09 18:00:50', null), (10, 'Github', 'Github', 'github', 2, 'ant-design:github-filled', 4, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://github.com/fastapi-practices/fastapi_best_architecture', null, 6, '2025-06-09 18:00:50', null),
(11, 'Apifox', 'Apifox', 'apifox', 3, 'simple-icons:apifox', 3, null, '/_core/fallback/iframe.vue', 1, 1, 1, 'https://apifox.com/apidoc/shared-28a93f02-730b-4f33-bb5e-4dad92058cc0', null, 6, '2025-06-09 18:01:39', null), (11, 'Apifox', 'Apifox', 'apifox', 3, 'simple-icons:apifox', 3, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://apifox.com/apidoc/shared-28a93f02-730b-4f33-bb5e-4dad92058cc0', null, 6, '2025-06-09 18:01:39', null),
(12, '部门管理', 'SysDept', 'sys-dept', 1, 'mingcute:department-line', 1, '/system/dept/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:03:17', null), (12, '部门管理', 'SysDept', 'sys-dept', 1, 'mingcute:department-line', 1, '/system/dept/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:03:17', null),
(13, '用户管理', 'SysUser', 'sys-user', 2, 'ant-design:user-outlined', 1, '/system/user/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:03:54', null), (13, '用户管理', 'SysUser', 'sys-user', 2, 'ant-design:user-outlined', 1, '/system/user/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:03:54', null),
(14, '角色管理', 'SysRole', 'sys-role', 3, 'carbon:user-role', 1, '/system/role/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:04:47', null), (14, '角色管理', 'SysRole', 'sys-role', 3, 'carbon:user-role', 1, '/system/role/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:04:47', null),
@@ -51,38 +51,34 @@ values (1, '概览', 'Dashboard', 'dashboard', 0, 'ant-design:dashboard-outline
(47, '新增', 'AddSysDataRule', null, 0, null, 2, null, 'data:rule:add', 1, 0, 1, '', null, 18, '2025-06-09 18:35:54', null), (47, '新增', 'AddSysDataRule', null, 0, null, 2, null, 'data:rule:add', 1, 0, 1, '', null, 18, '2025-06-09 18:35:54', null),
(48, '修改', 'EditSysDataRule', null, 0, null, 2, null, 'data:rule:edit', 1, 0, 1, '', null, 18, '2025-06-09 18:36:19', null), (48, '修改', 'EditSysDataRule', null, 0, null, 2, null, 'data:rule:edit', 1, 0, 1, '', null, 18, '2025-06-09 18:36:19', null),
(49, '删除', 'DeleteSysDataRule', null, 0, null, 2, null, 'data:rule:del', 1, 0, 1, '', null, 18, '2025-06-09 18:36:44', null), (49, '删除', 'DeleteSysDataRule', null, 0, null, 2, null, 'data:rule:del', 1, 0, 1, '', null, 18, '2025-06-09 18:36:44', null),
(50, '安装zip插件', 'InstallZipSysPlugin', null, 0, null, 2, null, 'sys:plugin:zip', 1, 0, 1, '', null, 19, '2025-06-09 18:38:14', null), (50, '安装插件', 'InstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:install', 1, 0, 1, '', null, 19, '2025-06-09 18:38:14', null),
(51, '安装git插件', 'InstallGitSysPlugin', null, 0, null, 2, null, 'sys:plugin:git', 1, 0, 1, '', null, 19, '2025-06-09 18:38:43', null), (51, '卸载', 'UninstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:uninstall', 1, 0, 1, '', null, 19, '2025-06-09 18:39:08', null),
(52, '卸载', 'UninstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:del', 1, 0, 1, '', null, 19, '2025-06-09 18:39:08', null), (52, '修改', 'EditSysPlugin', null, 0, null, 2, null, 'sys:plugin:edit', 1, 0, 1, '', null, 19, '2025-06-09 18:39:47', null),
(53, '修改', 'EditSysPlugin', null, 0, null, 2, null, 'sys:plugin:status', 1, 0, 1, '', null, 19, '2025-06-09 18:39:47', null), (53, '新增', 'AddSysConfig', null, 0, null, 2, null, 'sys:config:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:52', null),
(54, '新增网站参数', 'AddWebsiteSysConfig', null, 0, null, 2, null, 'sys:config:website:add', 1, 0, 1, '', null, 20, '2025-06-09 18:43:30', null), (54, '修改', 'EditSysConfig', null, 0, null, 2, null, 'sys:config:edit', 1, 0, 1, '', null, 20, '2025-06-09 18:46:13', null),
(55, '新增用户协议', 'AddProtocolSysConfig', null, 0, null, 2, null, 'sys:config:protocol:add', 1, 0, 1, '', null, 20, '2025-06-09 18:44:13', null), (55, '删除', 'DeleteSysConfig', null, 0, null, 2, null, 'sys:config:del', 1, 0, 1, '', null, 20, '2025-06-09 18:46:36', null),
(56, '新增用户政策', 'AddPolicySysConfig', null, 0, null, 2, null, 'sys:config:policy:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:28', null), (56, '新增类型', 'AddSysDictType', null, 0, null, 2, null, 'dict:type:add', 1, 0, 1, '', null, 21, '2025-06-09 18:48:17', null),
(57, '新增', 'AddSysConfig', null, 0, null, 2, null, 'sys:config:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:52', null), (57, '修改类型', 'EditSysDictType', null, 0, null, 2, null, 'dict:type:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:48:49', null),
(58, '修改', 'EditSysConfig', null, 0, null, 2, null, 'sys:config:edit', 1, 0, 1, '', null, 20, '2025-06-09 18:46:13', null), (58, '删除类型', 'DeleteSysDictType', null, 0, null, 2, null, 'dict:type:del', 1, 0, 1, '', null, 21, '2025-06-09 18:49:23', null),
(59, '删除', 'DeleteSysConfig', null, 0, null, 2, null, 'sys:config:del', 1, 0, 1, '', null, 20, '2025-06-09 18:46:36', null), (59, '新增', 'AddSysDictData', null, 0, null, 2, null, 'dict:data:add', 1, 0, 1, '', null, 21, '2025-06-09 18:50:01', null),
(60, '新增类型', 'AddSysDictType', null, 0, null, 2, null, 'sys:dict:type:add', 1, 0, 1, '', null, 21, '2025-06-09 18:48:17', null), (60, '修改', 'EditSysDictData', null, 0, null, 2, null, 'dict:data:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:50:26', null),
(61, '修改类型', 'EditSysDictType', null, 0, null, 2, null, 'sys:dict:type:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:48:49', null), (61, '删除', 'DeleteSysDictData', null, 0, null, 2, null, 'dict:data:del', 1, 0, 1, '', null, 21, '2025-06-09 18:50:48', null),
(62, '删除类型', 'DeleteSysDictType', null, 0, null, 2, null, 'sys:dict:type:del', 1, 0, 1, '', null, 21, '2025-06-09 18:49:23', null), (62, '新增', 'AddSysNotice', null, 0, null, 2, null, 'sys:notice:add', 1, 0, 1, '', null, 22, '2025-06-09 18:51:22', null),
(63, '新增', 'AddSysDictData', null, 0, null, 2, null, 'sys:dict:data:add', 1, 0, 1, '', null, 21, '2025-06-09 18:50:01', null), (63, '修改', 'EditSysNotice', null, 0, null, 2, null, 'sys:notice:edit', 1, 0, 1, '', null, 22, '2025-06-09 18:51:45', null),
(64, '修改', 'EditSysDictData', null, 0, null, 2, null, 'sys:dict:data:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:50:26', null), (64, '删除', 'DeleteSysNotice', null, 0, null, 2, null, 'sys:notice:del', 1, 0, 1, '', null, 22, '2025-06-09 18:52:10', null),
(65, '删除', 'DeleteSysDictData', null, 0, null, 2, null, 'sys:dict:data:del', 1, 0, 1, '', null, 21, '2025-06-09 18:50:48', null), (65, '新增业务', 'AddSysGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:add', 1, 0, 1, '', null, 23, '2025-06-09 18:53:07', null),
(66, '新增', 'AddSysNotice', null, 0, null, 2, null, 'sys:notice:add', 1, 0, 1, '', null, 22, '2025-06-09 18:51:22', null), (66, '修改业务', 'EditGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:53:45', null),
(67, '修改', 'EditSysNotice', null, 0, null, 2, null, 'sys:notice:edit', 1, 0, 1, '', null, 22, '2025-06-09 18:51:45', null), (67, '删除业务', 'DeleteGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:del', 1, 0, 1, '', null, 23, '2025-06-09 18:54:11', null),
(68, '删除', 'DeleteSysNotice', null, 0, null, 2, null, 'sys:notice:del', 1, 0, 1, '', null, 22, '2025-06-09 18:52:10', null), (68, '新增模型', 'AddGenCodeModel', null, 0, null, 2, null, 'codegen:model:add', 1, 0, 1, '', null, 23, '2025-06-09 18:54:45', null),
(69, '新增业务', 'AddSysGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:add', 1, 0, 1, '', null, 23, '2025-06-09 18:53:07', null), (69, '修改模型', 'EditGenCodeModel', null, 0, null, 2, null, 'codegen:model:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:55:08', null),
(70, '修改业务', 'EditGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:53:45', null), (70, '删除模型', 'DeleteGenCodeModel', null, 0, null, 2, null, 'codegen:model:del', 1, 0, 1, '', null, 23, '2025-06-09 18:55:35', null),
(71, '删除业务', 'DeleteGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:del', 1, 0, 1, '', null, 23, '2025-06-09 18:54:11', null), (71, '导入', 'ImportGenCode', null, 0, null, 2, null, 'codegen:table:import', 1, 0, 1, '', null, 23, '2025-06-09 18:58:16', null),
(72, '新增模型', 'AddGenCodeModel', null, 0, null, 2, null, 'gen:code:model:add', 1, 0, 1, '', null, 23, '2025-06-09 18:54:45', null), (72, '写入', 'WriteGenCode', null, 0, null, 2, null, 'codegen:local:write', 1, 0, 1, '', null, 23, '2025-06-09 19:01:22', null),
(73, '修改模型', 'EditGenCodeModel', null, 0, null, 2, null, 'gen:code:model:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:55:08', null), (73, '删除', 'DeleteSysLoginLog', null, 0, null, 2, null, 'log:login:del', 1, 0, 1, '', null, 25, '2025-06-09 19:02:21', null),
(74, '删除模型', 'DeleteGenCodeModel', null, 0, null, 2, null, 'gen:code:model:del', 1, 0, 1, '', null, 23, '2025-06-09 18:55:35', null), (74, '清空', 'EmptyLoginLog', null, 0, null, 2, null, 'log:login:clear', 1, 0, 1, '', null, 25, '2025-06-09 19:02:50', null),
(75, '导入', 'ImportGenCode', null, 0, null, 2, null, 'gen:code:import', 1, 0, 1, '', null, 23, '2025-06-09 18:58:16', null), (75, '删除', 'DeleteOperaLog', null, 0, null, 2, null, 'log:opera:del', 1, 0, 1, '', null, 26, '2025-06-09 19:03:13', null),
(76, '写入', 'WriteGenCode', null, 0, null, 2, null, 'gen:code:write', 1, 0, 1, '', null, 23, '2025-06-09 19:01:22', null), (76, '清空', 'EmptyOperaLog', null, 0, null, 2, null, 'log:opera:clear', 1, 0, 1, '', null, 26, '2025-06-09 19:03:40', null),
(77, '删除', 'DeleteSysLoginLog', null, 0, null, 2, null, 'log:login:del', 1, 0, 1, '', null, 25, '2025-06-09 19:02:21', null), (77, '下线', 'KickSysToken', null, 0, null, 2, null, 'sys:session:delete', 1, 0, 1, '', null, 27, '2025-06-09 19:04:52', null);
(78, '清空', 'EmptyLoginLog', null, 0, null, 2, null, 'log:login:empty', 1, 0, 1, '', null, 25, '2025-06-09 19:02:50', null),
(79, '删除', 'DeleteOperaLog', null, 0, null, 2, null, 'log:opera:del', 1, 0, 1, '', null, 26, '2025-06-09 19:03:13', null),
(80, '清空', 'EmptyOperaLog', null, 0, null, 2, null, 'log:opera:empty', 1, 0, 1, '', null, 26, '2025-06-09 19:03:40', null),
(81, '下线', 'KickSysToken', null, 0, null, 2, null, 'sys:token:kick', 1, 0, 1, '', null, 27, '2025-06-09 19:04:52', null);
insert into sys_role (id, name, status, is_filter_scopes, remark, created_time, updated_time) insert into sys_role (id, name, status, is_filter_scopes, remark, created_time, updated_time)
values (1, '测试', 1, 1, null, '2025-05-26 17:13:45', null); values (1, '测试', 1, 1, null, '2025-05-26 17:13:45', null);
+29 -33
View File
@@ -12,7 +12,7 @@ values (1, '概览', 'Dashboard', 'dashboard', 0, 'ant-design:dashboard-outline
(8, '工作台', 'Workspace', 'workspace', 1, 'carbon:workspace', 1, '/dashboard/workspace/index', null, 1, 1, 1, '', null, 1, '2025-06-09 17:57:09', null), (8, '工作台', 'Workspace', 'workspace', 1, 'carbon:workspace', 1, '/dashboard/workspace/index', null, 1, 1, 1, '', null, 1, '2025-06-09 17:57:09', null),
(9, '文档', 'Document', 'document', 1, 'lucide:book-open-text', 4, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://fastapi-practices.github.io/fastapi_best_architecture_docs', null, 6, '2025-06-09 17:59:44', null), (9, '文档', 'Document', 'document', 1, 'lucide:book-open-text', 4, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://fastapi-practices.github.io/fastapi_best_architecture_docs', null, 6, '2025-06-09 17:59:44', null),
(10, 'Github', 'Github', 'github', 2, 'ant-design:github-filled', 4, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://github.com/fastapi-practices/fastapi_best_architecture', null, 6, '2025-06-09 18:00:50', null), (10, 'Github', 'Github', 'github', 2, 'ant-design:github-filled', 4, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://github.com/fastapi-practices/fastapi_best_architecture', null, 6, '2025-06-09 18:00:50', null),
(11, 'Apifox', 'Apifox', 'apifox', 3, 'simple-icons:apifox', 3, null, '/_core/fallback/iframe.vue', 1, 1, 1, 'https://apifox.com/apidoc/shared-28a93f02-730b-4f33-bb5e-4dad92058cc0', null, 6, '2025-06-09 18:01:39', null), (11, 'Apifox', 'Apifox', 'apifox', 3, 'simple-icons:apifox', 3, '/_core/fallback/iframe.vue', null, 1, 1, 1, 'https://apifox.com/apidoc/shared-28a93f02-730b-4f33-bb5e-4dad92058cc0', null, 6, '2025-06-09 18:01:39', null),
(12, '部门管理', 'SysDept', 'sys-dept', 1, 'mingcute:department-line', 1, '/system/dept/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:03:17', null), (12, '部门管理', 'SysDept', 'sys-dept', 1, 'mingcute:department-line', 1, '/system/dept/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:03:17', null),
(13, '用户管理', 'SysUser', 'sys-user', 2, 'ant-design:user-outlined', 1, '/system/user/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:03:54', null), (13, '用户管理', 'SysUser', 'sys-user', 2, 'ant-design:user-outlined', 1, '/system/user/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:03:54', null),
(14, '角色管理', 'SysRole', 'sys-role', 3, 'carbon:user-role', 1, '/system/role/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:04:47', null), (14, '角色管理', 'SysRole', 'sys-role', 3, 'carbon:user-role', 1, '/system/role/index', null, 1, 1, 1, '', null, 2, '2025-06-09 18:04:47', null),
@@ -51,38 +51,34 @@ values (1, '概览', 'Dashboard', 'dashboard', 0, 'ant-design:dashboard-outline
(47, '新增', 'AddSysDataRule', null, 0, null, 2, null, 'data:rule:add', 1, 0, 1, '', null, 18, '2025-06-09 18:35:54', null), (47, '新增', 'AddSysDataRule', null, 0, null, 2, null, 'data:rule:add', 1, 0, 1, '', null, 18, '2025-06-09 18:35:54', null),
(48, '修改', 'EditSysDataRule', null, 0, null, 2, null, 'data:rule:edit', 1, 0, 1, '', null, 18, '2025-06-09 18:36:19', null), (48, '修改', 'EditSysDataRule', null, 0, null, 2, null, 'data:rule:edit', 1, 0, 1, '', null, 18, '2025-06-09 18:36:19', null),
(49, '删除', 'DeleteSysDataRule', null, 0, null, 2, null, 'data:rule:del', 1, 0, 1, '', null, 18, '2025-06-09 18:36:44', null), (49, '删除', 'DeleteSysDataRule', null, 0, null, 2, null, 'data:rule:del', 1, 0, 1, '', null, 18, '2025-06-09 18:36:44', null),
(50, '安装zip插件', 'InstallZipSysPlugin', null, 0, null, 2, null, 'sys:plugin:zip', 1, 0, 1, '', null, 19, '2025-06-09 18:38:14', null), (50, '安装插件', 'InstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:install', 1, 0, 1, '', null, 19, '2025-06-09 18:38:14', null),
(51, '安装git插件', 'InstallGitSysPlugin', null, 0, null, 2, null, 'sys:plugin:git', 1, 0, 1, '', null, 19, '2025-06-09 18:38:43', null), (51, '卸载', 'UninstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:uninstall', 1, 0, 1, '', null, 19, '2025-06-09 18:39:08', null),
(52, '卸载', 'UninstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:del', 1, 0, 1, '', null, 19, '2025-06-09 18:39:08', null), (52, '修改', 'EditSysPlugin', null, 0, null, 2, null, 'sys:plugin:edit', 1, 0, 1, '', null, 19, '2025-06-09 18:39:47', null),
(53, '修改', 'EditSysPlugin', null, 0, null, 2, null, 'sys:plugin:status', 1, 0, 1, '', null, 19, '2025-06-09 18:39:47', null), (53, '新增', 'AddSysConfig', null, 0, null, 2, null, 'sys:config:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:52', null),
(54, '新增网站参数', 'AddWebsiteSysConfig', null, 0, null, 2, null, 'sys:config:website:add', 1, 0, 1, '', null, 20, '2025-06-09 18:43:30', null), (54, '修改', 'EditSysConfig', null, 0, null, 2, null, 'sys:config:edit', 1, 0, 1, '', null, 20, '2025-06-09 18:46:13', null),
(55, '新增用户协议', 'AddProtocolSysConfig', null, 0, null, 2, null, 'sys:config:protocol:add', 1, 0, 1, '', null, 20, '2025-06-09 18:44:13', null), (55, '删除', 'DeleteSysConfig', null, 0, null, 2, null, 'sys:config:del', 1, 0, 1, '', null, 20, '2025-06-09 18:46:36', null),
(56, '新增用户政策', 'AddPolicySysConfig', null, 0, null, 2, null, 'sys:config:policy:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:28', null), (56, '新增类型', 'AddSysDictType', null, 0, null, 2, null, 'dict:type:add', 1, 0, 1, '', null, 21, '2025-06-09 18:48:17', null),
(57, '新增', 'AddSysConfig', null, 0, null, 2, null, 'sys:config:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:52', null), (57, '修改类型', 'EditSysDictType', null, 0, null, 2, null, 'dict:type:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:48:49', null),
(58, '修改', 'EditSysConfig', null, 0, null, 2, null, 'sys:config:edit', 1, 0, 1, '', null, 20, '2025-06-09 18:46:13', null), (58, '删除类型', 'DeleteSysDictType', null, 0, null, 2, null, 'dict:type:del', 1, 0, 1, '', null, 21, '2025-06-09 18:49:23', null),
(59, '删除', 'DeleteSysConfig', null, 0, null, 2, null, 'sys:config:del', 1, 0, 1, '', null, 20, '2025-06-09 18:46:36', null), (59, '新增', 'AddSysDictData', null, 0, null, 2, null, 'dict:data:add', 1, 0, 1, '', null, 21, '2025-06-09 18:50:01', null),
(60, '新增类型', 'AddSysDictType', null, 0, null, 2, null, 'sys:dict:type:add', 1, 0, 1, '', null, 21, '2025-06-09 18:48:17', null), (60, '修改', 'EditSysDictData', null, 0, null, 2, null, 'dict:data:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:50:26', null),
(61, '修改类型', 'EditSysDictType', null, 0, null, 2, null, 'sys:dict:type:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:48:49', null), (61, '删除', 'DeleteSysDictData', null, 0, null, 2, null, 'dict:data:del', 1, 0, 1, '', null, 21, '2025-06-09 18:50:48', null),
(62, '删除类型', 'DeleteSysDictType', null, 0, null, 2, null, 'sys:dict:type:del', 1, 0, 1, '', null, 21, '2025-06-09 18:49:23', null), (62, '新增', 'AddSysNotice', null, 0, null, 2, null, 'sys:notice:add', 1, 0, 1, '', null, 22, '2025-06-09 18:51:22', null),
(63, '新增', 'AddSysDictData', null, 0, null, 2, null, 'sys:dict:data:add', 1, 0, 1, '', null, 21, '2025-06-09 18:50:01', null), (63, '修改', 'EditSysNotice', null, 0, null, 2, null, 'sys:notice:edit', 1, 0, 1, '', null, 22, '2025-06-09 18:51:45', null),
(64, '修改', 'EditSysDictData', null, 0, null, 2, null, 'sys:dict:data:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:50:26', null), (64, '删除', 'DeleteSysNotice', null, 0, null, 2, null, 'sys:notice:del', 1, 0, 1, '', null, 22, '2025-06-09 18:52:10', null),
(65, '删除', 'DeleteSysDictData', null, 0, null, 2, null, 'sys:dict:data:del', 1, 0, 1, '', null, 21, '2025-06-09 18:50:48', null), (65, '新增业务', 'AddSysGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:add', 1, 0, 1, '', null, 23, '2025-06-09 18:53:07', null),
(66, '新增', 'AddSysNotice', null, 0, null, 2, null, 'sys:notice:add', 1, 0, 1, '', null, 22, '2025-06-09 18:51:22', null), (66, '修改业务', 'EditGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:53:45', null),
(67, '修改', 'EditSysNotice', null, 0, null, 2, null, 'sys:notice:edit', 1, 0, 1, '', null, 22, '2025-06-09 18:51:45', null), (67, '删除业务', 'DeleteGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:del', 1, 0, 1, '', null, 23, '2025-06-09 18:54:11', null),
(68, '删除', 'DeleteSysNotice', null, 0, null, 2, null, 'sys:notice:del', 1, 0, 1, '', null, 22, '2025-06-09 18:52:10', null), (68, '新增模型', 'AddGenCodeModel', null, 0, null, 2, null, 'codegen:model:add', 1, 0, 1, '', null, 23, '2025-06-09 18:54:45', null),
(69, '新增业务', 'AddSysGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:add', 1, 0, 1, '', null, 23, '2025-06-09 18:53:07', null), (69, '修改模型', 'EditGenCodeModel', null, 0, null, 2, null, 'codegen:model:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:55:08', null),
(70, '修改业务', 'EditGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:53:45', null), (70, '删除模型', 'DeleteGenCodeModel', null, 0, null, 2, null, 'codegen:model:del', 1, 0, 1, '', null, 23, '2025-06-09 18:55:35', null),
(71, '删除业务', 'DeleteGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:del', 1, 0, 1, '', null, 23, '2025-06-09 18:54:11', null), (71, '导入', 'ImportGenCode', null, 0, null, 2, null, 'codegen:table:import', 1, 0, 1, '', null, 23, '2025-06-09 18:58:16', null),
(72, '新增模型', 'AddGenCodeModel', null, 0, null, 2, null, 'gen:code:model:add', 1, 0, 1, '', null, 23, '2025-06-09 18:54:45', null), (72, '写入', 'WriteGenCode', null, 0, null, 2, null, 'codegen:local:write', 1, 0, 1, '', null, 23, '2025-06-09 19:01:22', null),
(73, '修改模型', 'EditGenCodeModel', null, 0, null, 2, null, 'gen:code:model:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:55:08', null), (73, '删除', 'DeleteSysLoginLog', null, 0, null, 2, null, 'log:login:del', 1, 0, 1, '', null, 25, '2025-06-09 19:02:21', null),
(74, '删除模型', 'DeleteGenCodeModel', null, 0, null, 2, null, 'gen:code:model:del', 1, 0, 1, '', null, 23, '2025-06-09 18:55:35', null), (74, '清空', 'EmptyLoginLog', null, 0, null, 2, null, 'log:login:clear', 1, 0, 1, '', null, 25, '2025-06-09 19:02:50', null),
(75, '导入', 'ImportGenCode', null, 0, null, 2, null, 'gen:code:import', 1, 0, 1, '', null, 23, '2025-06-09 18:58:16', null), (75, '删除', 'DeleteOperaLog', null, 0, null, 2, null, 'log:opera:del', 1, 0, 1, '', null, 26, '2025-06-09 19:03:13', null),
(76, '写入', 'WriteGenCode', null, 0, null, 2, null, 'gen:code:write', 1, 0, 1, '', null, 23, '2025-06-09 19:01:22', null), (76, '清空', 'EmptyOperaLog', null, 0, null, 2, null, 'log:opera:clear', 1, 0, 1, '', null, 26, '2025-06-09 19:03:40', null),
(77, '删除', 'DeleteSysLoginLog', null, 0, null, 2, null, 'log:login:del', 1, 0, 1, '', null, 25, '2025-06-09 19:02:21', null), (77, '下线', 'KickSysToken', null, 0, null, 2, null, 'sys:session:delete', 1, 0, 1, '', null, 27, '2025-06-09 19:04:52', null);
(78, '清空', 'EmptyLoginLog', null, 0, null, 2, null, 'log:login:empty', 1, 0, 1, '', null, 25, '2025-06-09 19:02:50', null),
(79, '删除', 'DeleteOperaLog', null, 0, null, 2, null, 'log:opera:del', 1, 0, 1, '', null, 26, '2025-06-09 19:03:13', null),
(80, '清空', 'EmptyOperaLog', null, 0, null, 2, null, 'log:opera:empty', 1, 0, 1, '', null, 26, '2025-06-09 19:03:40', null),
(81, '下线', 'KickSysToken', null, 0, null, 2, null, 'sys:token:kick', 1, 0, 1, '', null, 27, '2025-06-09 19:04:52', null);
insert into sys_role (id, name, status, is_filter_scopes, remark, created_time, updated_time) insert into sys_role (id, name, status, is_filter_scopes, remark, created_time, updated_time)
values (1, '测试', 1, 1, null, '2025-05-26 17:13:45', null); values (1, '测试', 1, 1, null, '2025-05-26 17:13:45', null);
+3 -4
View File
@@ -28,12 +28,11 @@ def build_filename(file: UploadFile) -> str:
return new_filename return new_filename
def file_verify(file: UploadFile, file_type: FileType) -> None: def file_verify(file: UploadFile) -> None:
""" """
文件验证 文件验证
:param file: FastAPI 上传文件对象 :param file: FastAPI 上传文件对象
:param file_type: 文件类型枚举
:return: :return:
""" """
filename = file.filename filename = file.filename
@@ -41,12 +40,12 @@ def file_verify(file: UploadFile, file_type: FileType) -> None:
if not file_ext: if not file_ext:
raise errors.ForbiddenError(msg='未知的文件类型') raise errors.ForbiddenError(msg='未知的文件类型')
if file_type == FileType.image: if file_ext == FileType.image:
if file_ext not in settings.UPLOAD_IMAGE_EXT_INCLUDE: if file_ext not in settings.UPLOAD_IMAGE_EXT_INCLUDE:
raise errors.ForbiddenError(msg='此图片格式暂不支持') raise errors.ForbiddenError(msg='此图片格式暂不支持')
if file.size > settings.UPLOAD_IMAGE_SIZE_MAX: if file.size > settings.UPLOAD_IMAGE_SIZE_MAX:
raise errors.ForbiddenError(msg='图片超出最大限制,请重新选择') raise errors.ForbiddenError(msg='图片超出最大限制,请重新选择')
elif file_type == FileType.video: elif file_ext == FileType.video:
if file_ext not in settings.UPLOAD_VIDEO_EXT_INCLUDE: if file_ext not in settings.UPLOAD_VIDEO_EXT_INCLUDE:
raise errors.ForbiddenError(msg='此视频格式暂不支持') raise errors.ForbiddenError(msg='此视频格式暂不支持')
if file.size > settings.UPLOAD_VIDEO_SIZE_MAX: if file.size > settings.UPLOAD_VIDEO_SIZE_MAX: