mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-21 05:02:49 +00:00
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:
@@ -8,4 +8,4 @@ from backend.app.admin.api.v1.auth.captcha import router as captcha_router
|
||||
router = APIRouter(prefix='/auth')
|
||||
|
||||
router.include_router(auth_router, tags=['授权'])
|
||||
router.include_router(captcha_router, prefix='/captcha', tags=['验证码'])
|
||||
router.include_router(captcha_router, tags=['验证码'])
|
||||
|
||||
@@ -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.service.auth_service import auth_service
|
||||
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
||||
from backend.common.security.jwt import DependsJwtAuth
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@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)
|
||||
return GetSwaggerToken(access_token=token, user=user) # type: ignore
|
||||
return GetSwaggerToken(access_token=token, user=user)
|
||||
|
||||
|
||||
@router.post(
|
||||
@@ -27,20 +28,26 @@ async def swagger_login(obj: Annotated[HTTPBasicCredentials, Depends()]) -> GetS
|
||||
description='json 格式登录, 仅支持在第三方api工具调试, 例如: postman',
|
||||
dependencies=[Depends(RateLimiter(times=5, minutes=1))],
|
||||
)
|
||||
async def user_login(
|
||||
async def login(
|
||||
request: Request, response: Response, obj: AuthLoginParam, background_tasks: BackgroundTasks
|
||||
) -> ResponseSchemaModel[GetLoginToken]:
|
||||
data = await auth_service.login(request=request, response=response, obj=obj, background_tasks=background_tasks)
|
||||
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]:
|
||||
data = await auth_service.new_token(request=request)
|
||||
data = await auth_service.refresh_token(request=request)
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@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)
|
||||
return response_base.success()
|
||||
|
||||
@@ -14,7 +14,7 @@ router = APIRouter()
|
||||
|
||||
|
||||
@router.get(
|
||||
'',
|
||||
'/captcha',
|
||||
summary='获取登录验证码',
|
||||
dependencies=[Depends(RateLimiter(times=5, seconds=10))],
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Annotated
|
||||
|
||||
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.common.pagination import DependsPagination, PageData, paging_data
|
||||
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
||||
@@ -24,7 +24,7 @@ router = APIRouter()
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_login_logs(
|
||||
async def get_login_logs_paged(
|
||||
db: CurrentSession,
|
||||
username: Annotated[str | None, Query(description='用户名')] = None,
|
||||
status: Annotated[int | None, Query(description='状态')] = None,
|
||||
@@ -43,8 +43,8 @@ async def get_pagination_login_logs(
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_login_log(pk: Annotated[list[int], Query(description='登录日志 ID 列表')]) -> ResponseModel:
|
||||
count = await login_log_service.delete(pk=pk)
|
||||
async def delete_login_logs(obj: DeleteLoginLogParam) -> ResponseModel:
|
||||
count = await login_log_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
@@ -54,7 +54,7 @@ async def delete_login_log(pk: Annotated[list[int], Query(description='登录日
|
||||
'/all',
|
||||
summary='清空登录日志',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('log:login:empty')),
|
||||
Depends(RequestPermission('log:login:clear')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Annotated
|
||||
|
||||
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.common.pagination import DependsPagination, PageData, paging_data
|
||||
from backend.common.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
||||
@@ -24,7 +24,7 @@ router = APIRouter()
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_opera_logs(
|
||||
async def get_opera_logs_paged(
|
||||
db: CurrentSession,
|
||||
username: Annotated[str | None, Query(description='用户名')] = None,
|
||||
status: Annotated[int | None, Query(description='状态')] = None,
|
||||
@@ -43,8 +43,8 @@ async def get_pagination_opera_logs(
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_opera_log(pk: Annotated[list[int], Query(description='操作日志 ID 列表')]) -> ResponseModel:
|
||||
count = await opera_log_service.delete(pk=pk)
|
||||
async def delete_opera_logs(obj: DeleteOperaLogParam) -> ResponseModel:
|
||||
count = await opera_log_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
@@ -54,7 +54,7 @@ async def delete_opera_log(pk: Annotated[list[int], Query(description='操作日
|
||||
'/all',
|
||||
summary='清空操作日志',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('log:opera:empty')),
|
||||
Depends(RequestPermission('log:opera:clear')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -10,4 +10,4 @@ router = APIRouter(prefix='/monitors')
|
||||
|
||||
router.include_router(redis_router, prefix='/redis', tags=['redis监控'])
|
||||
router.include_router(server_router, prefix='/server', tags=['服务器监控'])
|
||||
router.include_router(token_router, prefix='/online', tags=['在线用户'])
|
||||
router.include_router(token_router, prefix='/sessions', tags=['会话监控'])
|
||||
|
||||
@@ -19,7 +19,7 @@ router = APIRouter()
|
||||
|
||||
|
||||
@router.get('', summary='获取在线用户', dependencies=[DependsJwtAuth])
|
||||
async def get_online(
|
||||
async def get_sessions(
|
||||
username: Annotated[str | None, Query(description='用户名')] = None,
|
||||
) -> ResponseSchemaModel[list[GetTokenDetail]]:
|
||||
token_keys = await redis_client.keys(f'{settings.TOKEN_REDIS_PREFIX}:*')
|
||||
@@ -75,13 +75,13 @@ async def get_online(
|
||||
|
||||
@router.delete(
|
||||
'/{pk}',
|
||||
summary='踢下线',
|
||||
summary='强制下线',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:token:kick')),
|
||||
Depends(RequestPermission('sys:session:delete')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def kick_out(
|
||||
async def delete_session(
|
||||
request: Request,
|
||||
pk: Annotated[int, Path(description='用户 ID')],
|
||||
session_uuid: Annotated[str, Query(description='会话 UUID')],
|
||||
|
||||
@@ -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_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.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.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.upload import router as upload_router
|
||||
from backend.app.admin.api.v1.sys.user import router as user_router
|
||||
|
||||
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(data_rule_router, prefix='/data-rules', 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=['系统插件'])
|
||||
|
||||
@@ -6,6 +6,7 @@ from fastapi import APIRouter, Depends, Path, Query
|
||||
|
||||
from backend.app.admin.schema.data_rule import (
|
||||
CreateDataRuleParam,
|
||||
DeleteDataRuleParam,
|
||||
GetDataRuleColumnDetail,
|
||||
GetDataRuleDetail,
|
||||
UpdateDataRuleParam,
|
||||
@@ -57,7 +58,7 @@ async def get_data_rule(
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_data_rules(
|
||||
async def get_data_rules_paged(
|
||||
db: CurrentSession, name: Annotated[str | None, Query(description='规则名称')] = None
|
||||
) -> ResponseSchemaModel[PageData[GetDataRuleDetail]]:
|
||||
data_rule_select = await data_rule_service.get_select(name=name)
|
||||
@@ -103,8 +104,8 @@ async def update_data_rule(
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_data_rule(pk: Annotated[list[int], Query(description='数据规则 ID 列表')]) -> ResponseModel:
|
||||
count = await data_rule_service.delete(pk=pk)
|
||||
async def delete_data_rules(obj: DeleteDataRuleParam) -> ResponseModel:
|
||||
count = await data_rule_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -6,6 +6,7 @@ from fastapi import APIRouter, Depends, Path, Query
|
||||
|
||||
from backend.app.admin.schema.data_scope import (
|
||||
CreateDataScopeParam,
|
||||
DeleteDataScopeParam,
|
||||
GetDataScopeDetail,
|
||||
GetDataScopeWithRelationDetail,
|
||||
UpdateDataScopeParam,
|
||||
@@ -52,7 +53,7 @@ async def get_data_scope_rules(
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_data_scopes(
|
||||
async def get_data_scopes_paged(
|
||||
db: CurrentSession,
|
||||
name: Annotated[str | None, Query(description='范围名称')] = None,
|
||||
status: Annotated[int | None, Query(description='状态')] = None,
|
||||
@@ -117,8 +118,8 @@ async def update_data_scope_rules(
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_data_scope(pk: Annotated[list[int], Query(description='数据范围 ID 列表')]) -> ResponseModel:
|
||||
count = await data_scope_service.delete(pk=pk)
|
||||
async def delete_data_scopes(obj: DeleteDataScopeParam) -> ResponseModel:
|
||||
count = await data_scope_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -20,15 +20,15 @@ async def get_dept(pk: Annotated[int, Path(description='部门 ID')]) -> Respons
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@router.get('', summary='获取所有部门展示树', dependencies=[DependsJwtAuth])
|
||||
async def get_all_depts(
|
||||
@router.get('', summary='获取部门树', dependencies=[DependsJwtAuth])
|
||||
async def get_dept_tree(
|
||||
request: Request,
|
||||
name: Annotated[str | None, Query(description='部门名称')] = None,
|
||||
leader: Annotated[str | None, Query(description='部门负责人')] = None,
|
||||
phone: Annotated[str | None, Query(description='联系电话')] = None,
|
||||
status: Annotated[int | None, Query(description='状态')] = None,
|
||||
) -> 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)
|
||||
|
||||
|
||||
|
||||
@@ -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}'})
|
||||
@@ -14,7 +14,7 @@ from backend.common.security.rbac import DependsRBAC
|
||||
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]]:
|
||||
menu = await menu_service.get_sidebar(request=request)
|
||||
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)
|
||||
|
||||
|
||||
@router.get('', summary='获取所有菜单展示树', dependencies=[DependsJwtAuth])
|
||||
async def get_all_menus(
|
||||
@router.get('', summary='获取菜单树', dependencies=[DependsJwtAuth])
|
||||
async def get_menu_tree(
|
||||
title: Annotated[str | None, Query(description='菜单标题')] = None,
|
||||
status: Annotated[int | None, Query(description='状体')] = None,
|
||||
) -> 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)
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from fastapi.params import Query
|
||||
from starlette.responses import StreamingResponse
|
||||
|
||||
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_schema import ResponseModel, ResponseSchemaModel, response_base
|
||||
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)
|
||||
|
||||
|
||||
@router.get('/changes', summary='插件状态是否变更', dependencies=[DependsJwtAuth])
|
||||
@router.get('/changed', summary='是否存在插件变更', dependencies=[DependsJwtAuth])
|
||||
async def plugin_changed() -> ResponseSchemaModel[bool]:
|
||||
plugins = await plugin_service.changed()
|
||||
return response_base.success(data=bool(plugins))
|
||||
|
||||
|
||||
@router.post(
|
||||
'/zip',
|
||||
summary='安装 zip 插件',
|
||||
description='使用插件 zip 压缩包进行安装',
|
||||
'',
|
||||
summary='安装插件',
|
||||
description='使用插件 zip 压缩包或 git 仓库地址进行安装',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:plugin:zip')),
|
||||
Depends(RequestPermission('sys:plugin:install')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def install_zip_plugin(file: Annotated[UploadFile, File()]) -> ResponseModel:
|
||||
await plugin_service.install_zip(file=file)
|
||||
return response_base.success(res=CustomResponseCode.PLUGIN_INSTALL_SUCCESS)
|
||||
|
||||
|
||||
@router.post(
|
||||
'/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)
|
||||
async def install_plugin(
|
||||
type: Annotated[PluginType, Query(description='插件类型')],
|
||||
file: Annotated[UploadFile | None, File()] = None,
|
||||
repo_url: Annotated[str | None, Query(description='插件 git 仓库地址')] = None,
|
||||
) -> ResponseModel:
|
||||
await plugin_service.install(type=type, file=file, repo_url=repo_url)
|
||||
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='卸载插件',
|
||||
description='此操作会直接删除插件依赖,但不会直接删除插件,而是将插件移动到备份目录',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:plugin:del')),
|
||||
Depends(RequestPermission('sys:plugin:uninstall')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -70,11 +61,11 @@ async def uninstall_plugin(plugin: Annotated[str, Path(description='插件名称
|
||||
return response_base.success(res=CustomResponseCode.PLUGIN_UNINSTALL_SUCCESS)
|
||||
|
||||
|
||||
@router.post(
|
||||
@router.put(
|
||||
'/{plugin}/status',
|
||||
summary='更新插件状态',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:plugin:status')),
|
||||
Depends(RequestPermission('sys:plugin:edit')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -83,8 +74,8 @@ async def update_plugin_status(plugin: Annotated[str, Path(description='插件
|
||||
return response_base.success()
|
||||
|
||||
|
||||
@router.get('/{plugin}', summary='打包并下载插件', dependencies=[DependsJwtAuth])
|
||||
async def build_plugin(plugin: Annotated[str, Path(description='插件名称')]) -> StreamingResponse:
|
||||
@router.get('/{plugin}', summary='下载插件', dependencies=[DependsJwtAuth])
|
||||
async def download_plugin(plugin: Annotated[str, Path(description='插件名称')]) -> StreamingResponse:
|
||||
bio = await plugin_service.build(plugin=plugin)
|
||||
return StreamingResponse(
|
||||
bio,
|
||||
|
||||
@@ -6,6 +6,7 @@ from fastapi import APIRouter, Depends, Path, Query
|
||||
|
||||
from backend.app.admin.schema.role import (
|
||||
CreateRoleParam,
|
||||
DeleteRoleParam,
|
||||
GetRoleDetail,
|
||||
GetRoleWithRelationDetail,
|
||||
UpdateRoleMenuParam,
|
||||
@@ -29,8 +30,8 @@ async def get_all_roles() -> ResponseSchemaModel[list[GetRoleDetail]]:
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@router.get('/{pk}/menus', summary='获取角色所有菜单', dependencies=[DependsJwtAuth])
|
||||
async def get_role_all_menus(
|
||||
@router.get('/{pk}/menus', summary='获取角色菜单树', dependencies=[DependsJwtAuth])
|
||||
async def get_role_menu_tree(
|
||||
pk: Annotated[int, Path(description='角色 ID')],
|
||||
) -> ResponseSchemaModel[list[dict[str, Any] | None]]:
|
||||
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])
|
||||
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)
|
||||
return response_base.success(data=rule)
|
||||
|
||||
|
||||
@router.get('/{pk}', summary='获取角色详情', dependencies=[DependsJwtAuth])
|
||||
async def get_role(
|
||||
pk: Annotated[int, Path(description='角色 ID')],
|
||||
) -> ResponseSchemaModel[GetRoleWithRelationDetail]:
|
||||
async def get_role(pk: Annotated[int, Path(description='角色 ID')]) -> ResponseSchemaModel[GetRoleWithRelationDetail]:
|
||||
data = await role_service.get(pk=pk)
|
||||
return response_base.success(data=data)
|
||||
|
||||
@@ -59,7 +58,7 @@ async def get_role(
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_roles(
|
||||
async def get_roles_paged(
|
||||
db: CurrentSession,
|
||||
name: Annotated[str | None, Query(description='角色名称')] = None,
|
||||
status: Annotated[int | None, Query(description='状态')] = None,
|
||||
@@ -139,8 +138,8 @@ async def update_role_scopes(
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_role(pk: Annotated[list[int], Query(description='角色 ID 列表')]) -> ResponseModel:
|
||||
count = await role_service.delete(pk=pk)
|
||||
async def delete_roles(obj: DeleteRoleParam) -> ResponseModel:
|
||||
count = await role_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -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}'})
|
||||
@@ -13,6 +13,7 @@ from backend.app.admin.schema.user import (
|
||||
UpdateUserParam,
|
||||
)
|
||||
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.response.response_schema import ResponseModel, ResponseSchemaModel, response_base
|
||||
from backend.common.security.jwt import DependsJwtAuth
|
||||
@@ -23,42 +24,23 @@ from backend.database.db import CurrentSession
|
||||
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])
|
||||
async def get_current_user(request: Request) -> ResponseSchemaModel[GetCurrentUserInfoWithRelationDetail]:
|
||||
data = request.user.model_dump()
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@router.get('/{username}', summary='查看用户信息', dependencies=[DependsJwtAuth])
|
||||
async def get_user(
|
||||
username: Annotated[str, Path(description='用户名')],
|
||||
@router.get('/{pk}', summary='获取用户信息', dependencies=[DependsJwtAuth])
|
||||
async def get_userinfo(
|
||||
pk: Annotated[int, Path(description='用户 ID')],
|
||||
) -> ResponseSchemaModel[GetUserInfoWithRelationDetail]:
|
||||
data = await user_service.get_userinfo(username=username)
|
||||
data = await user_service.get_userinfo(pk=pk)
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@router.get('/{username}/roles', summary='获取用户所有角色', dependencies=[DependsJwtAuth])
|
||||
async def get_user_all_roles(
|
||||
username: Annotated[str, Path(description='用户名')],
|
||||
) -> ResponseSchemaModel[list[GetRoleDetail]]:
|
||||
data = await user_service.get_roles(username=username)
|
||||
@router.get('/{pk}/roles', summary='获取用户所有角色', dependencies=[DependsJwtAuth])
|
||||
async def get_user_roles(pk: Annotated[int, Path(description='用户 ID')]) -> ResponseSchemaModel[list[GetRoleDetail]]:
|
||||
data = await user_service.get_roles(pk=pk)
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@@ -70,7 +52,7 @@ async def get_user_all_roles(
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_users(
|
||||
async def get_users_paged(
|
||||
db: CurrentSession,
|
||||
dept: Annotated[int | None, Query(description='部门 ID')] = None,
|
||||
username: Annotated[str | None, Query(description='用户名')] = None,
|
||||
@@ -82,58 +64,55 @@ async def get_pagination_users(
|
||||
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(
|
||||
request: Request, username: Annotated[str, Path(description='用户名')], obj: UpdateUserParam
|
||||
request: Request, pk: Annotated[int, Path(description='用户 ID')], obj: UpdateUserParam
|
||||
) -> 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:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
|
||||
@router.put('/{pk}/super', summary='修改用户超级权限', dependencies=[DependsRBAC])
|
||||
async def super_set(request: Request, pk: Annotated[int, Path(description='用户 ID')]) -> ResponseModel:
|
||||
count = await user_service.update_permission(request=request, pk=pk)
|
||||
@router.put('/{pk}/permissions', summary='更新用户权限', dependencies=[DependsRBAC])
|
||||
async def update_user_permission(
|
||||
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:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
|
||||
@router.put('/{pk}/staff', summary='修改用户后台登录权限', dependencies=[DependsRBAC])
|
||||
async def staff_set(request: Request, pk: Annotated[int, Path(description='用户 ID')]) -> ResponseModel:
|
||||
count = await user_service.update_staff(request=request, pk=pk)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
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)
|
||||
@router.put('/{pk}/password', summary='重置用户密码', dependencies=[DependsJwtAuth])
|
||||
async def reset_user_password(
|
||||
pk: Annotated[int, Path(description='用户 ID')], obj: ResetPasswordParam
|
||||
) -> ResponseModel:
|
||||
count = await user_service.reset_pwd(pk=pk, obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
|
||||
@router.delete(
|
||||
path='/{username}',
|
||||
path='/{pk}',
|
||||
summary='删除用户',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:user:del')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_user(username: Annotated[str, Path(description='用户名')]) -> ResponseModel:
|
||||
count = await user_service.delete(username=username)
|
||||
async def delete_user(pk: Annotated[int, Path(description='用户 ID')]) -> ResponseModel:
|
||||
count = await user_service.delete(pk=pk)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -77,15 +77,15 @@ class CRUDDataRule(CRUDPlus[DataRule]):
|
||||
"""
|
||||
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 pk: 规则 ID 列表
|
||||
:param pks: 规则 ID 列表
|
||||
: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)
|
||||
|
||||
@@ -105,15 +105,15 @@ class CRUDDataScope(CRUDPlus[DataScope]):
|
||||
current_data_scope.rules = rules.scalars().all()
|
||||
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 pk: 范围 ID 列表
|
||||
:param pks: 范围 ID 列表
|
||||
: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)
|
||||
|
||||
@@ -41,15 +41,15 @@ class CRUDLoginLog(CRUDPlus[LoginLog]):
|
||||
"""
|
||||
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 pk: 登录日志 ID 列表
|
||||
:param pks: 登录日志 ID 列表
|
||||
: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:
|
||||
"""
|
||||
|
||||
@@ -50,18 +50,17 @@ class CRUDMenu(CRUDPlus[Menu]):
|
||||
|
||||
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 superuser: 是否超级管理员
|
||||
:param menu_ids: 菜单 ID 列表
|
||||
:return:
|
||||
"""
|
||||
filters = {'type__in': [0, 1, 3, 4]}
|
||||
|
||||
if not superuser:
|
||||
if menu_ids:
|
||||
filters['id__in'] = menu_ids
|
||||
|
||||
return await self.select_models_order(db, 'sort', 'asc', **filters)
|
||||
|
||||
@@ -41,15 +41,15 @@ class CRUDOperaLogDao(CRUDPlus[OperaLog]):
|
||||
"""
|
||||
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 pk: 操作日志 ID 列表
|
||||
:param pks: 操作日志 ID 列表
|
||||
: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:
|
||||
"""
|
||||
|
||||
@@ -134,15 +134,15 @@ class CRUDRole(CRUDPlus[Role]):
|
||||
current_role.scopes = scopes.scalars().all()
|
||||
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 role_id: 角色 ID 列表
|
||||
:param role_ids: 角色 ID 列表
|
||||
: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)
|
||||
|
||||
@@ -27,6 +27,12 @@ class UpdateDataRuleParam(DataRuleSchemaBase):
|
||||
"""更新数据规则参数"""
|
||||
|
||||
|
||||
class DeleteDataRuleParam(SchemaBase):
|
||||
"""删除数据规则参数"""
|
||||
|
||||
pks: list[int] = Field(description='规则 ID 列表')
|
||||
|
||||
|
||||
class GetDataRuleDetail(DataRuleSchemaBase):
|
||||
"""数据规则详情"""
|
||||
|
||||
|
||||
@@ -30,6 +30,12 @@ class UpdateDataScopeRuleParam(SchemaBase):
|
||||
rules: list[int] = Field(description='数据规则 ID 列表')
|
||||
|
||||
|
||||
class DeleteDataScopeParam(SchemaBase):
|
||||
"""删除数据范围参数"""
|
||||
|
||||
pks: list[int] = Field(description='数据范围 ID 列表')
|
||||
|
||||
|
||||
class GetDataScopeDetail(DataScopeBase):
|
||||
"""数据范围详情"""
|
||||
|
||||
|
||||
@@ -33,6 +33,12 @@ class UpdateLoginLogParam(LoginLogSchemaBase):
|
||||
"""更新登录日志参数"""
|
||||
|
||||
|
||||
class DeleteLoginLogParam(SchemaBase):
|
||||
"""删除登录日志参数"""
|
||||
|
||||
pks: list[int] = Field(description='登录日志 ID 列表')
|
||||
|
||||
|
||||
class GetLoginLogDetail(LoginLogSchemaBase):
|
||||
"""登录日志详情"""
|
||||
|
||||
|
||||
@@ -41,6 +41,12 @@ class UpdateOperaLogParam(OperaLogSchemaBase):
|
||||
"""更新操作日志参数"""
|
||||
|
||||
|
||||
class DeleteOperaLogParam(SchemaBase):
|
||||
"""删除操作日志参数"""
|
||||
|
||||
pks: list[int] = Field(description='操作日志 ID 列表')
|
||||
|
||||
|
||||
class GetOperaLogDetail(OperaLogSchemaBase):
|
||||
"""操作日志详情"""
|
||||
|
||||
|
||||
@@ -27,6 +27,12 @@ class UpdateRoleParam(RoleSchemaBase):
|
||||
"""更新角色参数"""
|
||||
|
||||
|
||||
class DeleteRoleParam(SchemaBase):
|
||||
"""删除角色参数"""
|
||||
|
||||
pks: list[int] = Field(description='角色 ID 列表')
|
||||
|
||||
|
||||
class UpdateRoleMenuParam(SchemaBase):
|
||||
"""更新角色菜单参数"""
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from fastapi.security import HTTPBasicCredentials
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
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.model import User
|
||||
from backend.app.admin.schema.token import GetLoginToken, GetNewToken
|
||||
@@ -162,9 +163,34 @@ class AuthService:
|
||||
return data
|
||||
|
||||
@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 请求对象
|
||||
:return:
|
||||
|
||||
@@ -6,7 +6,12 @@ from sqlalchemy import Select
|
||||
|
||||
from backend.app.admin.crud.crud_data_rule import data_rule_dao
|
||||
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.core.conf import settings
|
||||
from backend.database.db import async_db_session
|
||||
@@ -105,15 +110,15 @@ class DataRuleService:
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: DeleteDataRuleParam) -> int:
|
||||
"""
|
||||
删除数据规则
|
||||
批量删除数据规则
|
||||
|
||||
:param pk: 规则 ID 列表
|
||||
:param obj: 规则 ID 列表
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,12 @@ from sqlalchemy import Select
|
||||
|
||||
from backend.app.admin.crud.crud_data_scope import data_scope_dao
|
||||
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.core.conf import settings
|
||||
from backend.database.db import async_db_session
|
||||
@@ -112,17 +117,17 @@ class DataScopeService:
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: DeleteDataScopeParam) -> int:
|
||||
"""
|
||||
删除数据范围
|
||||
批量删除数据范围
|
||||
|
||||
:param pk: 范围 ID 列表
|
||||
:param obj: 范围 ID 列表
|
||||
:return:
|
||||
"""
|
||||
async with async_db_session.begin() as db:
|
||||
count = await data_scope_dao.delete(db, pk)
|
||||
for _pk in pk:
|
||||
data_rule = await data_scope_dao.get(db, _pk)
|
||||
count = await data_scope_dao.delete(db, obj.pks)
|
||||
for pk in obj.pks:
|
||||
data_rule = await data_scope_dao.get(db, pk)
|
||||
if data_rule:
|
||||
for role in await data_rule.awaitable_attrs.roles:
|
||||
for user in await role.awaitable_attrs.users:
|
||||
|
||||
@@ -32,7 +32,7 @@ class DeptService:
|
||||
return dept
|
||||
|
||||
@staticmethod
|
||||
async def get_dept_tree(
|
||||
async def get_tree(
|
||||
*, request: Request, name: str | None, leader: str | None, phone: str | None, status: int | None
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
|
||||
@@ -7,7 +7,7 @@ from sqlalchemy import Select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
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.database.db import async_db_session
|
||||
|
||||
@@ -71,15 +71,15 @@ class LoginLogService:
|
||||
log.error(f'登录日志创建失败: {e}')
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: DeleteLoginLogParam) -> int:
|
||||
"""
|
||||
删除登录日志
|
||||
批量删除登录日志
|
||||
|
||||
:param pk: 日志 ID 列表
|
||||
:param obj: 日志 ID 列表
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -32,7 +32,7 @@ class MenuService:
|
||||
return menu
|
||||
|
||||
@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,20 +54,16 @@ class MenuService:
|
||||
:return:
|
||||
"""
|
||||
async with async_db_session() as db:
|
||||
if request.user.is_superuser:
|
||||
menu_data = await menu_dao.get_sidebar(db, None)
|
||||
else:
|
||||
roles = request.user.roles
|
||||
menu_tree = []
|
||||
menu_ids = set()
|
||||
if roles:
|
||||
unique_menus = {}
|
||||
for role in roles:
|
||||
for menu in role.menus:
|
||||
unique_menus[menu.id] = menu
|
||||
all_ids = set(unique_menus.keys())
|
||||
valid_menu_ids = [
|
||||
menu_id
|
||||
for menu_id, menu in unique_menus.items()
|
||||
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_ids.add(menu.id)
|
||||
menu_data = await menu_dao.get_sidebar(db, list(menu_ids))
|
||||
menu_tree = get_vben5_tree_data(menu_data)
|
||||
return menu_tree
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from sqlalchemy import Select
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -34,15 +34,15 @@ class OperaLogService:
|
||||
await opera_log_dao.create(db, obj)
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: DeleteOperaLogParam) -> int:
|
||||
"""
|
||||
删除操作日志
|
||||
批量删除操作日志
|
||||
|
||||
:param pk: 日志 ID 列表
|
||||
:param obj: 日志 ID 列表
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -11,7 +11,7 @@ from typing import Any
|
||||
from dulwich import porcelain
|
||||
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.log import log
|
||||
from backend.core.conf import settings
|
||||
@@ -41,7 +41,7 @@ class PluginService:
|
||||
|
||||
@staticmethod
|
||||
async def changed() -> str | None:
|
||||
"""插件状态是否变更"""
|
||||
"""检查插件是否发生变更"""
|
||||
return await redis_client.get(f'{settings.PLUGIN_REDIS_PREFIX}:changed')
|
||||
|
||||
@staticmethod
|
||||
@@ -113,6 +113,24 @@ class PluginService:
|
||||
await install_requirements_async(repo_name)
|
||||
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
|
||||
async def uninstall(*, plugin: str):
|
||||
"""
|
||||
|
||||
@@ -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.schema.role import (
|
||||
CreateRoleParam,
|
||||
DeleteRoleParam,
|
||||
UpdateRoleMenuParam,
|
||||
UpdateRoleParam,
|
||||
UpdateRoleScopeParam,
|
||||
@@ -166,17 +167,17 @@ class RoleService:
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: DeleteRoleParam) -> int:
|
||||
"""
|
||||
删除角色
|
||||
批量删除角色
|
||||
|
||||
:param pk: 角色 ID 列表
|
||||
:param obj: 角色 ID 列表
|
||||
:return:
|
||||
"""
|
||||
async with async_db_session.begin() as db:
|
||||
count = await role_dao.delete(db, pk)
|
||||
for _pk in pk:
|
||||
role = await role_dao.get(db, _pk)
|
||||
count = await role_dao.delete(db, obj.pks)
|
||||
for pk in obj.pks:
|
||||
role = await role_dao.get(db, pk)
|
||||
if role:
|
||||
for user in await role.awaitable_attrs.users:
|
||||
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
|
||||
|
||||
@@ -16,6 +16,7 @@ from backend.app.admin.schema.user import (
|
||||
ResetPasswordParam,
|
||||
UpdateUserParam,
|
||||
)
|
||||
from backend.common.enums import UserPermissionType
|
||||
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.core.conf import settings
|
||||
@@ -27,86 +28,30 @@ class UserService:
|
||||
"""用户服务类"""
|
||||
|
||||
@staticmethod
|
||||
async def add(*, request: Request, obj: AddUserParam) -> None:
|
||||
"""
|
||||
添加新用户
|
||||
|
||||
: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:
|
||||
async def get_userinfo(*, pk: int | None = None, username: str | None = None) -> User:
|
||||
"""
|
||||
获取用户信息
|
||||
|
||||
:param pk: 用户 ID
|
||||
:param username: 用户名
|
||||
:return:
|
||||
"""
|
||||
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:
|
||||
raise errors.NotFoundError(msg='用户不存在')
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
async def get_roles(*, username: str) -> Sequence[Role]:
|
||||
async def get_roles(*, pk: int) -> Sequence[Role]:
|
||||
"""
|
||||
获取用户所有角色
|
||||
|
||||
:param username: 用户名
|
||||
:param pk: 用户 ID
|
||||
:return:
|
||||
"""
|
||||
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:
|
||||
raise errors.NotFoundError(msg='用户不存在')
|
||||
return user.roles
|
||||
@@ -125,41 +70,58 @@ class UserService:
|
||||
return await user_dao.get_list(dept=dept, username=username, phone=phone, status=status)
|
||||
|
||||
@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 username: 用户名
|
||||
:param pk: 用户 ID
|
||||
:param obj: 用户更新参数
|
||||
:return:
|
||||
"""
|
||||
async with async_db_session.begin() as db:
|
||||
if request.user.username != username:
|
||||
raise errors.ForbiddenError(msg='你只能修改自己的信息')
|
||||
user = await user_dao.get_with_relation(db, username=username)
|
||||
user = await user_dao.get_with_relation(db, user_id=pk)
|
||||
if not user:
|
||||
raise errors.NotFoundError(msg='用户不存在')
|
||||
if user.username != obj.username:
|
||||
_username = await user_dao.get_by_username(db, obj.username)
|
||||
if _username:
|
||||
if request.user.username != user.username:
|
||||
raise errors.ForbiddenError(msg='只能修改自己的信息')
|
||||
if obj.username != user.username:
|
||||
if await user_dao.get_by_username(db, obj.username):
|
||||
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:
|
||||
role = await role_dao.get(db, role_id)
|
||||
if not role:
|
||||
if not await role_dao.get(db, role_id):
|
||||
raise errors.NotFoundError(msg='角色不存在')
|
||||
count = await user_dao.update(db, user, obj)
|
||||
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def update_permission(*, request: Request, pk: int) -> int:
|
||||
async def update_superuser(*, request: Request, pk: int) -> int:
|
||||
"""
|
||||
更新用户权限
|
||||
更新用户管理员状态
|
||||
|
||||
:param request: FastAPI 请求对象
|
||||
:param pk: 用户 ID
|
||||
@@ -171,7 +133,7 @@ class UserService:
|
||||
if not user:
|
||||
raise errors.NotFoundError(msg='用户不存在')
|
||||
if pk == request.user.id:
|
||||
raise errors.ForbiddenError(msg='非法操作')
|
||||
raise errors.ForbiddenError(msg='禁止修改自身权限')
|
||||
super_status = await user_dao.get_super(db, pk)
|
||||
count = await user_dao.set_super(db, pk, not super_status)
|
||||
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
|
||||
@@ -192,7 +154,7 @@ class UserService:
|
||||
if not user:
|
||||
raise errors.NotFoundError(msg='用户不存在')
|
||||
if pk == request.user.id:
|
||||
raise errors.ForbiddenError(msg='非法操作')
|
||||
raise errors.ForbiddenError(msg='禁止修改自身权限')
|
||||
staff_status = await user_dao.get_staff(db, pk)
|
||||
count = await user_dao.set_staff(db, pk, not staff_status)
|
||||
await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}')
|
||||
@@ -213,7 +175,7 @@ class UserService:
|
||||
if not user:
|
||||
raise errors.NotFoundError(msg='用户不存在')
|
||||
if pk == request.user.id:
|
||||
raise errors.ForbiddenError(msg='非法操作')
|
||||
raise errors.ForbiddenError(msg='禁止修改自身权限')
|
||||
status = await user_dao.get_status(db, pk)
|
||||
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}')
|
||||
@@ -251,16 +213,65 @@ class UserService:
|
||||
await redis_client.delete_prefix(key_prefix)
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, username: str) -> int:
|
||||
async def update_permission(self, *, request: Request, pk: int, type: UserPermissionType) -> 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:
|
||||
"""
|
||||
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:
|
||||
raise errors.NotFoundError(msg='用户不存在')
|
||||
count = await user_dao.delete(db, user.id)
|
||||
|
||||
@@ -14,12 +14,6 @@ from backend.common.security.rbac import DependsRBAC
|
||||
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(
|
||||
'/{tid}',
|
||||
summary='获取任务详情',
|
||||
@@ -27,12 +21,18 @@ async def get_all_tasks() -> ResponseSchemaModel[list[str]]:
|
||||
description='此接口被视为作废,建议使用 flower 查看任务详情',
|
||||
dependencies=[DependsJwtAuth],
|
||||
)
|
||||
async def get_task_detail(tid: Annotated[str, Path(description='任务 UUID')]) -> ResponseSchemaModel[TaskResult]:
|
||||
status = task_service.get_detail(tid=tid)
|
||||
async def get_task(tid: Annotated[str, Path(description='任务 UUID')]) -> ResponseSchemaModel[TaskResult]:
|
||||
status = task_service.get(tid=tid)
|
||||
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}',
|
||||
summary='撤销任务',
|
||||
dependencies=[
|
||||
@@ -46,8 +46,8 @@ async def revoke_task(tid: Annotated[str, Path(description='任务 UUID')]) -> R
|
||||
|
||||
|
||||
@router.post(
|
||||
'',
|
||||
summary='执行任务',
|
||||
'/runs',
|
||||
summary='运行任务',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:task:run')),
|
||||
DependsRBAC,
|
||||
|
||||
@@ -11,16 +11,7 @@ from backend.common.exception import errors
|
||||
|
||||
class TaskService:
|
||||
@staticmethod
|
||||
async def get_list() -> 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
|
||||
def get_detail(*, tid: str) -> TaskResult:
|
||||
def get(*, tid: str) -> TaskResult:
|
||||
"""
|
||||
获取指定任务的详细信息
|
||||
|
||||
@@ -43,6 +34,15 @@ class TaskService:
|
||||
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
|
||||
def revoke(*, tid: str) -> None:
|
||||
"""
|
||||
|
||||
@@ -121,3 +121,19 @@ class FileType(StrEnum):
|
||||
|
||||
image = 'image'
|
||||
video = 'video'
|
||||
|
||||
|
||||
class PluginType(StrEnum):
|
||||
"""插件类型"""
|
||||
|
||||
zip = 'zip'
|
||||
git = 'git'
|
||||
|
||||
|
||||
class UserPermissionType(StrEnum):
|
||||
"""用户权限类型"""
|
||||
|
||||
superuser = 'superuser'
|
||||
staff = 'staff'
|
||||
status = 'status'
|
||||
multi_login = 'multi_login'
|
||||
|
||||
@@ -231,7 +231,7 @@ def superuser_verify(request: Request) -> bool:
|
||||
"""
|
||||
superuser = request.user.is_superuser
|
||||
if not superuser or not request.user.is_staff:
|
||||
raise errors.AuthorizationError
|
||||
raise errors.AuthorizationError()
|
||||
return superuser
|
||||
|
||||
|
||||
|
||||
@@ -64,23 +64,20 @@ async def filter_data_permission(db: AsyncSession, request: Request) -> ColumnEl
|
||||
return or_(1 == 1)
|
||||
|
||||
# 获取数据范围
|
||||
unique_data_scopes = {}
|
||||
data_scope_ids = set()
|
||||
for role in request.user.roles:
|
||||
for scope in role.scopes:
|
||||
if scope.status:
|
||||
unique_data_scopes[scope.id] = scope
|
||||
|
||||
# 转换为列表
|
||||
data_scopes = list(unique_data_scopes.values())
|
||||
data_scope_ids.add(scope.id)
|
||||
|
||||
# 无规则用户不做过滤
|
||||
if not data_scopes:
|
||||
if not list(data_scope_ids):
|
||||
return or_(1 == 1)
|
||||
|
||||
# 获取数据范围规则
|
||||
unique_data_rules = {}
|
||||
for data_scope in data_scopes:
|
||||
data_scope_with_relation = await data_scope_dao.get_with_relation(db, data_scope.id)
|
||||
for data_scope_id in list(data_scope_ids):
|
||||
data_scope_with_relation = await data_scope_dao.get_with_relation(db, data_scope_id)
|
||||
for rule in data_scope_with_relation.rules:
|
||||
unique_data_rules[rule.id] = rule
|
||||
|
||||
|
||||
@@ -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.include_router(gen_router, prefix='/tables')
|
||||
v1.include_router(business_router, prefix='/businesses')
|
||||
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.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])
|
||||
async def get_business(
|
||||
pk: Annotated[int, Path(description='业务 ID')],
|
||||
@@ -34,11 +28,17 @@ async def get_business(
|
||||
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])
|
||||
async def get_business_all_models(
|
||||
pk: Annotated[int, Path(description='业务 ID')],
|
||||
) -> 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)
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ async def get_business_all_models(
|
||||
summary='创建代码生成业务',
|
||||
deprecated=True,
|
||||
dependencies=[
|
||||
Depends(RequestPermission('gen:code:business:add')),
|
||||
Depends(RequestPermission('codegen:business:add')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -60,7 +60,7 @@ async def create_business(obj: CreateGenBusinessParam) -> ResponseModel:
|
||||
'/{pk}',
|
||||
summary='更新代码生成业务',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('gen:code:business:edit')),
|
||||
Depends(RequestPermission('codegen:business:edit')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -77,7 +77,7 @@ async def update_business(
|
||||
'/{pk}',
|
||||
summary='删除代码生成业务',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('gen:code:business:del')),
|
||||
Depends(RequestPermission('codegen:business:del')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -30,7 +30,7 @@ async def get_model(pk: Annotated[int, Path(description='模型 ID')]) -> Respon
|
||||
'',
|
||||
summary='创建代码生成模型',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('gen:code:model:add')),
|
||||
Depends(RequestPermission('codegen:model:add')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -43,7 +43,7 @@ async def create_model(obj: CreateGenModelParam) -> ResponseModel:
|
||||
'/{pk}',
|
||||
summary='更新代码生成模型',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('gen:code:model:edit')),
|
||||
Depends(RequestPermission('codegen:model:edit')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -58,7 +58,7 @@ async def update_model(pk: Annotated[int, Path(description='模型 ID')], obj: U
|
||||
'/{pk}',
|
||||
summary='删除代码生成模型',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('gen:code:model:del')),
|
||||
Depends(RequestPermission('codegen:model:del')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ from backend.plugin.code_generator.service.gen_service import gen_service
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get('', summary='获取数据库表')
|
||||
@router.get('/tables', summary='获取数据库表')
|
||||
async def get_all_tables(
|
||||
table_schema: Annotated[str, Query(description='数据库名')] = 'fba',
|
||||
) -> ResponseSchemaModel[list[str]]:
|
||||
@@ -25,10 +25,10 @@ async def get_all_tables(
|
||||
|
||||
|
||||
@router.post(
|
||||
'/import',
|
||||
'/imports',
|
||||
summary='导入代码生成业务和模型列',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('gen:code:import')),
|
||||
Depends(RequestPermission('codegen:table:import')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -37,24 +37,24 @@ async def import_table(obj: ImportParam) -> ResponseModel:
|
||||
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]]:
|
||||
data = await gen_service.preview(pk=pk)
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@router.get('/{pk}/code/path', summary='获取代码生成路径', dependencies=[DependsJwtAuth])
|
||||
async def generate_path(pk: Annotated[int, Path(description='业务 ID')]) -> ResponseSchemaModel[list[str]]:
|
||||
@router.get('/{pk}/paths', summary='获取代码生成路径', dependencies=[DependsJwtAuth])
|
||||
async def get_generate_paths(pk: Annotated[int, Path(description='业务 ID')]) -> ResponseSchemaModel[list[str]]:
|
||||
data = await gen_service.get_generate_path(pk=pk)
|
||||
return response_base.success(data=data)
|
||||
|
||||
|
||||
@router.post(
|
||||
'/{pk}/code',
|
||||
'/{pk}/generation',
|
||||
summary='代码生成',
|
||||
description='文件磁盘写入,请谨慎操作',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('gen:code:write')),
|
||||
Depends(RequestPermission('codegen:local:write')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ class GenModelService:
|
||||
return types
|
||||
|
||||
@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: 业务对象
|
||||
: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:
|
||||
raise errors.NotFoundError(msg='代码生成模型表为空')
|
||||
|
||||
|
||||
@@ -4,7 +4,12 @@ from typing import Annotated
|
||||
|
||||
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.common.pagination import DependsPagination, PageData, paging_data
|
||||
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,
|
||||
],
|
||||
)
|
||||
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()
|
||||
page_data = await paging_data(db, {{ table_name }}_select)
|
||||
return response_base.success(data=page_data)
|
||||
@@ -72,8 +77,8 @@ async def update_{{ table_name }}(pk: Annotated[int, Path(description='{{ doc_co
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_{{ table_name }}(pk: Annotated[list[int], Query(description='{{ doc_comment }} ID 列表')]) -> ResponseModel:
|
||||
count = await {{ table_name }}_service.delete(pk=pk)
|
||||
async def delete_{{ table_name }}s(obj: Delete{{ schema_name }}Param) -> ResponseModel:
|
||||
count = await {{ table_name }}_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -55,15 +55,15 @@ class CRUD{{ class_name }}(CRUDPlus[{{ schema_name }}]):
|
||||
"""
|
||||
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 pk: {{ doc_comment }} ID
|
||||
:param pks: {{ doc_comment }} ID 列表
|
||||
: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 }})
|
||||
|
||||
@@ -23,6 +23,10 @@ class Update{{ schema_name }}Param({{ schema_name }}SchemaBase):
|
||||
"""更新{{ doc_comment }}参数"""
|
||||
|
||||
|
||||
class Delete{{ schema_name }}Param({{ schema_name }}SchemaBase):
|
||||
"""删除{{ doc_comment }}参数"""
|
||||
|
||||
|
||||
class Get{{ schema_name }}Detail({{ schema_name }}SchemaBase):
|
||||
"""{{ 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 }}.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.database.db import async_db_session
|
||||
|
||||
@@ -63,15 +63,15 @@ class {{ class_name }}Service:
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: Delete{{ schema_name }}Param) -> int:
|
||||
"""
|
||||
删除{{ doc_comment }}
|
||||
|
||||
:param pk: {{ doc_comment }} ID 列表
|
||||
:param obj: {{ doc_comment }} ID 列表
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
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.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 (
|
||||
CreateConfigParam,
|
||||
GetConfigDetail,
|
||||
SaveBuiltInConfigParam,
|
||||
UpdateConfigParam,
|
||||
)
|
||||
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.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])
|
||||
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)
|
||||
|
||||
|
||||
@@ -92,10 +34,10 @@ async def get_config(pk: Annotated[int, Path(description='参数配置 ID')]) ->
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_configs(
|
||||
async def get_configs_paged(
|
||||
db: CurrentSession,
|
||||
name: Annotated[str | None, Query(description='参数配置名称')] = None,
|
||||
type: Annotated[str | None, Query()] = None,
|
||||
type: Annotated[str | None, Query(description='参数配置类型')] = None,
|
||||
) -> ResponseSchemaModel[PageData[GetConfigDetail]]:
|
||||
config_select = await config_service.get_select(name=name, type=type)
|
||||
page_data = await paging_data(db, config_select)
|
||||
@@ -138,8 +80,8 @@ async def update_config(pk: Annotated[int, Path(description='参数配置 ID')],
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_config(pk: Annotated[list[int], Query(description='参数配置 ID 列表')]) -> ResponseModel:
|
||||
count = await config_service.delete(pk=pk)
|
||||
async def delete_configs(pks: Annotated[list[int], Body(description='参数配置 ID 列表')]) -> ResponseModel:
|
||||
count = await config_service.delete(pks=pks)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Sequence
|
||||
|
||||
from sqlalchemy import Select
|
||||
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)
|
||||
|
||||
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:
|
||||
"""
|
||||
通过键名获取参数配置
|
||||
@@ -93,16 +71,16 @@ class CRUDConfig(CRUDPlus[Config]):
|
||||
"""
|
||||
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 pk: 参数配置 ID 列表
|
||||
:param pks: 参数配置 ID 列表
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -7,14 +7,6 @@ from pydantic import ConfigDict, Field
|
||||
from backend.common.schema import SchemaBase
|
||||
|
||||
|
||||
class SaveBuiltInConfigParam(SchemaBase):
|
||||
"""保存内置参数配置参数"""
|
||||
|
||||
name: str = Field(description='参数配置名称')
|
||||
key: str = Field(description='参数配置键名')
|
||||
value: str = Field(description='参数配置值')
|
||||
|
||||
|
||||
class ConfigSchemaBase(SchemaBase):
|
||||
"""参数配置基础模型"""
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from typing import Sequence
|
||||
|
||||
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.schema.config import (
|
||||
CreateConfigParam,
|
||||
SaveBuiltInConfigParam,
|
||||
UpdateConfigParam,
|
||||
)
|
||||
|
||||
@@ -20,37 +18,7 @@ class ConfigService:
|
||||
"""参数配置服务类"""
|
||||
|
||||
@staticmethod
|
||||
async def get_built_in_config(type: str) -> Sequence[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:
|
||||
async def get(*, pk: int) -> Config:
|
||||
"""
|
||||
获取参数配置详情
|
||||
|
||||
@@ -111,15 +79,15 @@ class ConfigService:
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, pks: list[int]) -> int:
|
||||
"""
|
||||
删除参数配置
|
||||
批量删除参数配置
|
||||
|
||||
:param pk: 参数配置 ID 列表
|
||||
:param pks: 参数配置 ID 列表
|
||||
:return:
|
||||
"""
|
||||
async with async_db_session.begin() as db:
|
||||
count = await config_dao.delete(db, pk)
|
||||
count = await config_dao.delete(db, pks)
|
||||
return count
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from backend.common.security.rbac import DependsRBAC
|
||||
from backend.database.db import CurrentSession
|
||||
from backend.plugin.dict.schema.dict_data import (
|
||||
CreateDictDataParam,
|
||||
DeleteDictDataParam,
|
||||
GetDictDataDetail,
|
||||
GetDictDataWithRelation,
|
||||
UpdateDictDataParam,
|
||||
@@ -21,7 +22,7 @@ from backend.plugin.dict.service.dict_data_service import dict_data_service
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get('/{pk}', summary='获取字典详情', dependencies=[DependsJwtAuth])
|
||||
@router.get('/{pk}', summary='获取字典数据详情', dependencies=[DependsJwtAuth])
|
||||
async def get_dict_data(
|
||||
pk: Annotated[int, Path(description='字典数据 ID')],
|
||||
) -> ResponseSchemaModel[GetDictDataWithRelation]:
|
||||
@@ -31,13 +32,13 @@ async def get_dict_data(
|
||||
|
||||
@router.get(
|
||||
'',
|
||||
summary='分页获取所有字典',
|
||||
summary='分页获取所有字典数据',
|
||||
dependencies=[
|
||||
DependsJwtAuth,
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_dict_datas(
|
||||
async def get_dict_datas_paged(
|
||||
db: CurrentSession,
|
||||
label: 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(
|
||||
'',
|
||||
summary='创建字典',
|
||||
summary='创建字典数据',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:dict:data:add')),
|
||||
Depends(RequestPermission('dict:data:add')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -63,9 +64,9 @@ async def create_dict_data(obj: CreateDictDataParam) -> ResponseModel:
|
||||
|
||||
@router.put(
|
||||
'/{pk}',
|
||||
summary='更新字典',
|
||||
summary='更新字典数据',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:dict:data:edit')),
|
||||
Depends(RequestPermission('dict:data:edit')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -80,14 +81,14 @@ async def update_dict_data(
|
||||
|
||||
@router.delete(
|
||||
'',
|
||||
summary='批量删除字典',
|
||||
summary='批量删除字典数据',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:dict:data:del')),
|
||||
Depends(RequestPermission('dict:data:del')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_dict_data(pk: Annotated[list[int], Query(description='字典数据 ID 列表')]) -> ResponseModel:
|
||||
count = await dict_data_service.delete(pk=pk)
|
||||
async def delete_dict_datas(obj: DeleteDictDataParam) -> ResponseModel:
|
||||
count = await dict_data_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -10,7 +10,12 @@ from backend.common.security.jwt import DependsJwtAuth
|
||||
from backend.common.security.permission import RequestPermission
|
||||
from backend.common.security.rbac import DependsRBAC
|
||||
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
|
||||
|
||||
router = APIRouter()
|
||||
@@ -24,7 +29,7 @@ router = APIRouter()
|
||||
DependsPagination,
|
||||
],
|
||||
)
|
||||
async def get_pagination_dict_types(
|
||||
async def get_dict_types_paged(
|
||||
db: CurrentSession,
|
||||
name: Annotated[str | None, Query(description='字典类型名称')] = None,
|
||||
code: Annotated[str | None, Query(description='字典类型编码')] = None,
|
||||
@@ -39,7 +44,7 @@ async def get_pagination_dict_types(
|
||||
'',
|
||||
summary='创建字典类型',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:dict:type:add')),
|
||||
Depends(RequestPermission('dict:type:add')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -52,7 +57,7 @@ async def create_dict_type(obj: CreateDictTypeParam) -> ResponseModel:
|
||||
'/{pk}',
|
||||
summary='更新字典类型',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:dict:type:edit')),
|
||||
Depends(RequestPermission('dict:type:edit')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
@@ -69,12 +74,12 @@ async def update_dict_type(
|
||||
'',
|
||||
summary='批量删除字典类型',
|
||||
dependencies=[
|
||||
Depends(RequestPermission('sys:dict:type:del')),
|
||||
Depends(RequestPermission('dict:type:del')),
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_dict_type(pk: Annotated[list[int], Query(description='字典类型 ID 列表')]) -> ResponseModel:
|
||||
count = await dict_type_service.delete(pk=pk)
|
||||
async def delete_dict_types(obj: DeleteDictTypeParam) -> ResponseModel:
|
||||
count = await dict_type_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -72,15 +72,15 @@ class CRUDDictData(CRUDPlus[DictData]):
|
||||
"""
|
||||
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 pk: 字典数据 ID 列表
|
||||
:param pks: 字典数据 ID 列表
|
||||
: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:
|
||||
"""
|
||||
|
||||
@@ -72,15 +72,15 @@ class CRUDDictType(CRUDPlus[DictType]):
|
||||
"""
|
||||
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 pk: 字典类型 ID 列表
|
||||
:param pks: 字典类型 ID 列表
|
||||
: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)
|
||||
|
||||
@@ -28,6 +28,12 @@ class UpdateDictDataParam(DictDataSchemaBase):
|
||||
"""更新字典数据参数"""
|
||||
|
||||
|
||||
class DeleteDictDataParam(SchemaBase):
|
||||
"""删除字典数据参数"""
|
||||
|
||||
pks: list[int] = Field(description='字典数据 ID 列表')
|
||||
|
||||
|
||||
class GetDictDataDetail(DictDataSchemaBase):
|
||||
"""字典数据详情"""
|
||||
|
||||
|
||||
@@ -25,6 +25,12 @@ class UpdateDictTypeParam(DictTypeSchemaBase):
|
||||
"""更新字典类型参数"""
|
||||
|
||||
|
||||
class DeleteDictTypeParam(SchemaBase):
|
||||
"""删除字典类型参数"""
|
||||
|
||||
pks: list[int] = Field(description='字典类型 ID 列表')
|
||||
|
||||
|
||||
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_type import dict_type_dao
|
||||
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:
|
||||
@@ -79,15 +79,15 @@ class DictDataService:
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: DeleteDictDataParam) -> int:
|
||||
"""
|
||||
删除字典数据
|
||||
批量删除字典数据
|
||||
|
||||
:param pk: 字典数据 ID 列表
|
||||
:param obj: 字典数据 ID 列表
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from sqlalchemy import Select
|
||||
from backend.common.exception import errors
|
||||
from backend.database.db import async_db_session
|
||||
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:
|
||||
@@ -57,15 +57,15 @@ class DictTypeService:
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: DeleteDictTypeParam) -> int:
|
||||
"""
|
||||
删除字典类型
|
||||
批量删除字典类型
|
||||
|
||||
:param pk: 字典类型 ID 列表
|
||||
:param obj: 字典类型 ID 列表
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
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.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.rbac import DependsRBAC
|
||||
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
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get('/{pk}', summary='获取通知公告详情', dependencies=[DependsJwtAuth])
|
||||
async def get_notice(
|
||||
request: Request, pk: Annotated[int, Path(description='通知公告 ID')]
|
||||
) -> ResponseSchemaModel[GetNoticeDetail]:
|
||||
async def get_notice(pk: Annotated[int, Path(description='通知公告 ID')]) -> ResponseSchemaModel[GetNoticeDetail]:
|
||||
notice = await notice_service.get(pk=pk)
|
||||
return response_base.success(data=notice)
|
||||
|
||||
@@ -32,7 +30,7 @@ async def get_notice(
|
||||
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()
|
||||
page_data = await paging_data(db, notice_select)
|
||||
return response_base.success(data=page_data)
|
||||
@@ -74,8 +72,8 @@ async def update_notice(pk: Annotated[int, Path(description='通知公告 ID')],
|
||||
DependsRBAC,
|
||||
],
|
||||
)
|
||||
async def delete_notice(pk: Annotated[list[int], Query(description='通知公告 ID 列表')]) -> ResponseModel:
|
||||
count = await notice_service.delete(pk=pk)
|
||||
async def delete_notices(obj: DeleteNoticeParam) -> ResponseModel:
|
||||
count = await notice_service.delete(obj=obj)
|
||||
if count > 0:
|
||||
return response_base.success()
|
||||
return response_base.fail()
|
||||
|
||||
@@ -57,15 +57,15 @@ class CRUDNotice(CRUDPlus[Notice]):
|
||||
"""
|
||||
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 pk: 通知公告 ID 列表
|
||||
:param pks: 通知公告 ID 列表
|
||||
: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)
|
||||
|
||||
@@ -27,6 +27,12 @@ class UpdateNoticeParam(NoticeSchemaBase):
|
||||
"""更新通知公告参数"""
|
||||
|
||||
|
||||
class DeleteNoticeParam(SchemaBase):
|
||||
"""删除通知公告参数"""
|
||||
|
||||
pks: list[int] = Field(description='通知公告 ID 列表')
|
||||
|
||||
|
||||
class GetNoticeDetail(NoticeSchemaBase):
|
||||
"""通知公告详情"""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from backend.common.exception import errors
|
||||
from backend.database.db import async_db_session
|
||||
from backend.plugin.notice.crud.crud_notice import notice_dao
|
||||
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:
|
||||
@@ -68,15 +68,15 @@ class NoticeService:
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
async def delete(*, pk: list[int]) -> int:
|
||||
async def delete(*, obj: DeleteNoticeParam) -> int:
|
||||
"""
|
||||
删除通知公告
|
||||
批量删除通知公告
|
||||
|
||||
:param pk: 通知公告 ID 列表
|
||||
:param obj: 通知公告 ID 列表
|
||||
:return:
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ _github_oauth2 = FastAPIOAuth20(_github_client, redirect_route_name='github_logi
|
||||
|
||||
|
||||
@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')
|
||||
return response_base.success(data=auth_url)
|
||||
|
||||
@@ -28,7 +28,7 @@ async def github_oauth2(request: Request) -> ResponseSchemaModel[str]:
|
||||
description='Github 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户',
|
||||
dependencies=[Depends(RateLimiter(times=5, minutes=1))],
|
||||
)
|
||||
async def github_login(
|
||||
async def github_oauth2_callback(
|
||||
request: Request,
|
||||
response: Response,
|
||||
background_tasks: BackgroundTasks,
|
||||
|
||||
@@ -20,7 +20,7 @@ _linux_do_oauth2 = FastAPIOAuth20(_linux_do_client, redirect_route_name='linux_d
|
||||
|
||||
|
||||
@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')
|
||||
return response_base.success(data=auth_url)
|
||||
|
||||
@@ -31,7 +31,7 @@ async def linux_do_oauth2(request: Request) -> ResponseSchemaModel[str]:
|
||||
description='LinuxDo 授权后,自动重定向到当前地址并获取用户信息,通过用户信息自动创建系统用户',
|
||||
dependencies=[Depends(RateLimiter(times=5, minutes=1))],
|
||||
)
|
||||
async def linux_do_login(
|
||||
async def linux_do_oauth2_callback(
|
||||
request: Request,
|
||||
response: Response,
|
||||
background_tasks: BackgroundTasks,
|
||||
|
||||
@@ -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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
@@ -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),
|
||||
(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),
|
||||
(50, '安装zip插件', 'InstallZipSysPlugin', null, 0, null, 2, null, 'sys:plugin:zip', 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),
|
||||
(52, '卸载', 'UninstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:del', 1, 0, 1, '', null, 19, '2025-06-09 18:39:08', null),
|
||||
(53, '修改', 'EditSysPlugin', null, 0, null, 2, null, 'sys:plugin:status', 1, 0, 1, '', null, 19, '2025-06-09 18:39:47', null),
|
||||
(54, '新增网站参数', 'AddWebsiteSysConfig', null, 0, null, 2, null, 'sys:config:website:add', 1, 0, 1, '', null, 20, '2025-06-09 18:43:30', null),
|
||||
(55, '新增用户协议', 'AddProtocolSysConfig', null, 0, null, 2, null, 'sys:config:protocol:add', 1, 0, 1, '', null, 20, '2025-06-09 18:44:13', null),
|
||||
(56, '新增用户政策', 'AddPolicySysConfig', null, 0, null, 2, null, 'sys:config:policy:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:28', null),
|
||||
(57, '新增', 'AddSysConfig', null, 0, null, 2, null, 'sys:config:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:52', null),
|
||||
(58, '修改', 'EditSysConfig', null, 0, null, 2, null, 'sys:config:edit', 1, 0, 1, '', null, 20, '2025-06-09 18:46:13', null),
|
||||
(59, '删除', 'DeleteSysConfig', null, 0, null, 2, null, 'sys:config:del', 1, 0, 1, '', null, 20, '2025-06-09 18:46:36', null),
|
||||
(60, '新增类型', 'AddSysDictType', null, 0, null, 2, null, 'sys:dict:type:add', 1, 0, 1, '', null, 21, '2025-06-09 18:48:17', null),
|
||||
(61, '修改类型', 'EditSysDictType', null, 0, null, 2, null, 'sys:dict:type:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:48:49', null),
|
||||
(62, '删除类型', 'DeleteSysDictType', null, 0, null, 2, null, 'sys:dict:type:del', 1, 0, 1, '', null, 21, '2025-06-09 18:49:23', null),
|
||||
(63, '新增', 'AddSysDictData', null, 0, null, 2, null, 'sys:dict:data:add', 1, 0, 1, '', null, 21, '2025-06-09 18:50:01', null),
|
||||
(64, '修改', 'EditSysDictData', null, 0, null, 2, null, 'sys:dict:data:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:50:26', null),
|
||||
(65, '删除', 'DeleteSysDictData', null, 0, null, 2, null, 'sys:dict:data:del', 1, 0, 1, '', null, 21, '2025-06-09 18:50:48', null),
|
||||
(66, '新增', 'AddSysNotice', null, 0, null, 2, null, 'sys:notice:add', 1, 0, 1, '', null, 22, '2025-06-09 18:51:22', null),
|
||||
(67, '修改', 'EditSysNotice', null, 0, null, 2, null, 'sys:notice:edit', 1, 0, 1, '', null, 22, '2025-06-09 18:51:45', null),
|
||||
(68, '删除', 'DeleteSysNotice', null, 0, null, 2, null, 'sys:notice:del', 1, 0, 1, '', null, 22, '2025-06-09 18:52:10', null),
|
||||
(69, '新增业务', 'AddSysGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:add', 1, 0, 1, '', null, 23, '2025-06-09 18:53:07', null),
|
||||
(70, '修改业务', 'EditGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:53:45', null),
|
||||
(71, '删除业务', 'DeleteGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:del', 1, 0, 1, '', null, 23, '2025-06-09 18:54:11', null),
|
||||
(72, '新增模型', 'AddGenCodeModel', null, 0, null, 2, null, 'gen:code:model:add', 1, 0, 1, '', null, 23, '2025-06-09 18:54:45', null),
|
||||
(73, '修改模型', 'EditGenCodeModel', null, 0, null, 2, null, 'gen:code:model:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:55:08', null),
|
||||
(74, '删除模型', 'DeleteGenCodeModel', null, 0, null, 2, null, 'gen:code:model:del', 1, 0, 1, '', null, 23, '2025-06-09 18:55:35', null),
|
||||
(75, '导入', 'ImportGenCode', null, 0, null, 2, null, 'gen:code:import', 1, 0, 1, '', null, 23, '2025-06-09 18:58:16', null),
|
||||
(76, '写入', 'WriteGenCode', null, 0, null, 2, null, 'gen:code:write', 1, 0, 1, '', null, 23, '2025-06-09 19:01:22', null),
|
||||
(77, '删除', 'DeleteSysLoginLog', null, 0, null, 2, null, 'log:login:del', 1, 0, 1, '', null, 25, '2025-06-09 19:02:21', 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);
|
||||
(50, '安装插件', 'InstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:install', 1, 0, 1, '', null, 19, '2025-06-09 18:38:14', null),
|
||||
(51, '卸载', 'UninstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:uninstall', 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, '新增', 'AddSysConfig', null, 0, null, 2, null, 'sys:config:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:52', null),
|
||||
(54, '修改', 'EditSysConfig', null, 0, null, 2, null, 'sys:config:edit', 1, 0, 1, '', null, 20, '2025-06-09 18:46: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, '新增类型', 'AddSysDictType', null, 0, null, 2, null, 'dict:type:add', 1, 0, 1, '', null, 21, '2025-06-09 18:48:17', null),
|
||||
(57, '修改类型', 'EditSysDictType', null, 0, null, 2, null, 'dict:type:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:48:49', null),
|
||||
(58, '删除类型', 'DeleteSysDictType', null, 0, null, 2, null, 'dict:type:del', 1, 0, 1, '', null, 21, '2025-06-09 18:49:23', null),
|
||||
(59, '新增', 'AddSysDictData', null, 0, null, 2, null, 'dict:data:add', 1, 0, 1, '', null, 21, '2025-06-09 18:50:01', null),
|
||||
(60, '修改', 'EditSysDictData', null, 0, null, 2, null, 'dict:data:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:50:26', null),
|
||||
(61, '删除', 'DeleteSysDictData', null, 0, null, 2, null, 'dict:data:del', 1, 0, 1, '', null, 21, '2025-06-09 18:50:48', null),
|
||||
(62, '新增', 'AddSysNotice', null, 0, null, 2, null, 'sys:notice:add', 1, 0, 1, '', null, 22, '2025-06-09 18:51:22', null),
|
||||
(63, '修改', 'EditSysNotice', null, 0, null, 2, null, 'sys:notice:edit', 1, 0, 1, '', null, 22, '2025-06-09 18:51:45', null),
|
||||
(64, '删除', 'DeleteSysNotice', null, 0, null, 2, null, 'sys:notice:del', 1, 0, 1, '', null, 22, '2025-06-09 18:52:10', null),
|
||||
(65, '新增业务', 'AddSysGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:add', 1, 0, 1, '', null, 23, '2025-06-09 18:53:07', null),
|
||||
(66, '修改业务', 'EditGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:53: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, '新增模型', 'AddGenCodeModel', null, 0, null, 2, null, 'codegen:model:add', 1, 0, 1, '', null, 23, '2025-06-09 18:54:45', null),
|
||||
(69, '修改模型', 'EditGenCodeModel', null, 0, null, 2, null, 'codegen:model:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:55:08', null),
|
||||
(70, '删除模型', 'DeleteGenCodeModel', null, 0, null, 2, null, 'codegen:model:del', 1, 0, 1, '', null, 23, '2025-06-09 18:55:35', null),
|
||||
(71, '导入', 'ImportGenCode', null, 0, null, 2, null, 'codegen:table:import', 1, 0, 1, '', null, 23, '2025-06-09 18:58:16', null),
|
||||
(72, '写入', 'WriteGenCode', null, 0, null, 2, null, 'codegen:local:write', 1, 0, 1, '', null, 23, '2025-06-09 19:01:22', null),
|
||||
(73, '删除', 'DeleteSysLoginLog', null, 0, null, 2, null, 'log:login:del', 1, 0, 1, '', null, 25, '2025-06-09 19:02:21', null),
|
||||
(74, '清空', 'EmptyLoginLog', null, 0, null, 2, null, 'log:login:clear', 1, 0, 1, '', null, 25, '2025-06-09 19:02:50', null),
|
||||
(75, '删除', 'DeleteOperaLog', null, 0, null, 2, null, 'log:opera:del', 1, 0, 1, '', null, 26, '2025-06-09 19:03:13', null),
|
||||
(76, '清空', 'EmptyOperaLog', null, 0, null, 2, null, 'log:opera:clear', 1, 0, 1, '', null, 26, '2025-06-09 19:03:40', null),
|
||||
(77, '下线', 'KickSysToken', null, 0, null, 2, null, 'sys:session:delete', 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)
|
||||
values (1, '测试', 1, 1, null, '2025-05-26 17:13:45', null);
|
||||
|
||||
@@ -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),
|
||||
(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),
|
||||
(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),
|
||||
(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),
|
||||
@@ -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),
|
||||
(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),
|
||||
(50, '安装zip插件', 'InstallZipSysPlugin', null, 0, null, 2, null, 'sys:plugin:zip', 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),
|
||||
(52, '卸载', 'UninstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:del', 1, 0, 1, '', null, 19, '2025-06-09 18:39:08', null),
|
||||
(53, '修改', 'EditSysPlugin', null, 0, null, 2, null, 'sys:plugin:status', 1, 0, 1, '', null, 19, '2025-06-09 18:39:47', null),
|
||||
(54, '新增网站参数', 'AddWebsiteSysConfig', null, 0, null, 2, null, 'sys:config:website:add', 1, 0, 1, '', null, 20, '2025-06-09 18:43:30', null),
|
||||
(55, '新增用户协议', 'AddProtocolSysConfig', null, 0, null, 2, null, 'sys:config:protocol:add', 1, 0, 1, '', null, 20, '2025-06-09 18:44:13', null),
|
||||
(56, '新增用户政策', 'AddPolicySysConfig', null, 0, null, 2, null, 'sys:config:policy:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:28', null),
|
||||
(57, '新增', 'AddSysConfig', null, 0, null, 2, null, 'sys:config:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:52', null),
|
||||
(58, '修改', 'EditSysConfig', null, 0, null, 2, null, 'sys:config:edit', 1, 0, 1, '', null, 20, '2025-06-09 18:46:13', null),
|
||||
(59, '删除', 'DeleteSysConfig', null, 0, null, 2, null, 'sys:config:del', 1, 0, 1, '', null, 20, '2025-06-09 18:46:36', null),
|
||||
(60, '新增类型', 'AddSysDictType', null, 0, null, 2, null, 'sys:dict:type:add', 1, 0, 1, '', null, 21, '2025-06-09 18:48:17', null),
|
||||
(61, '修改类型', 'EditSysDictType', null, 0, null, 2, null, 'sys:dict:type:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:48:49', null),
|
||||
(62, '删除类型', 'DeleteSysDictType', null, 0, null, 2, null, 'sys:dict:type:del', 1, 0, 1, '', null, 21, '2025-06-09 18:49:23', null),
|
||||
(63, '新增', 'AddSysDictData', null, 0, null, 2, null, 'sys:dict:data:add', 1, 0, 1, '', null, 21, '2025-06-09 18:50:01', null),
|
||||
(64, '修改', 'EditSysDictData', null, 0, null, 2, null, 'sys:dict:data:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:50:26', null),
|
||||
(65, '删除', 'DeleteSysDictData', null, 0, null, 2, null, 'sys:dict:data:del', 1, 0, 1, '', null, 21, '2025-06-09 18:50:48', null),
|
||||
(66, '新增', 'AddSysNotice', null, 0, null, 2, null, 'sys:notice:add', 1, 0, 1, '', null, 22, '2025-06-09 18:51:22', null),
|
||||
(67, '修改', 'EditSysNotice', null, 0, null, 2, null, 'sys:notice:edit', 1, 0, 1, '', null, 22, '2025-06-09 18:51:45', null),
|
||||
(68, '删除', 'DeleteSysNotice', null, 0, null, 2, null, 'sys:notice:del', 1, 0, 1, '', null, 22, '2025-06-09 18:52:10', null),
|
||||
(69, '新增业务', 'AddSysGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:add', 1, 0, 1, '', null, 23, '2025-06-09 18:53:07', null),
|
||||
(70, '修改业务', 'EditGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:53:45', null),
|
||||
(71, '删除业务', 'DeleteGenCodeBusiness', null, 0, null, 2, null, 'gen:code:business:del', 1, 0, 1, '', null, 23, '2025-06-09 18:54:11', null),
|
||||
(72, '新增模型', 'AddGenCodeModel', null, 0, null, 2, null, 'gen:code:model:add', 1, 0, 1, '', null, 23, '2025-06-09 18:54:45', null),
|
||||
(73, '修改模型', 'EditGenCodeModel', null, 0, null, 2, null, 'gen:code:model:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:55:08', null),
|
||||
(74, '删除模型', 'DeleteGenCodeModel', null, 0, null, 2, null, 'gen:code:model:del', 1, 0, 1, '', null, 23, '2025-06-09 18:55:35', null),
|
||||
(75, '导入', 'ImportGenCode', null, 0, null, 2, null, 'gen:code:import', 1, 0, 1, '', null, 23, '2025-06-09 18:58:16', null),
|
||||
(76, '写入', 'WriteGenCode', null, 0, null, 2, null, 'gen:code:write', 1, 0, 1, '', null, 23, '2025-06-09 19:01:22', null),
|
||||
(77, '删除', 'DeleteSysLoginLog', null, 0, null, 2, null, 'log:login:del', 1, 0, 1, '', null, 25, '2025-06-09 19:02:21', 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);
|
||||
(50, '安装插件', 'InstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:install', 1, 0, 1, '', null, 19, '2025-06-09 18:38:14', null),
|
||||
(51, '卸载', 'UninstallSysPlugin', null, 0, null, 2, null, 'sys:plugin:uninstall', 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, '新增', 'AddSysConfig', null, 0, null, 2, null, 'sys:config:add', 1, 0, 1, '', null, 20, '2025-06-09 18:45:52', null),
|
||||
(54, '修改', 'EditSysConfig', null, 0, null, 2, null, 'sys:config:edit', 1, 0, 1, '', null, 20, '2025-06-09 18:46: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, '新增类型', 'AddSysDictType', null, 0, null, 2, null, 'dict:type:add', 1, 0, 1, '', null, 21, '2025-06-09 18:48:17', null),
|
||||
(57, '修改类型', 'EditSysDictType', null, 0, null, 2, null, 'dict:type:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:48:49', null),
|
||||
(58, '删除类型', 'DeleteSysDictType', null, 0, null, 2, null, 'dict:type:del', 1, 0, 1, '', null, 21, '2025-06-09 18:49:23', null),
|
||||
(59, '新增', 'AddSysDictData', null, 0, null, 2, null, 'dict:data:add', 1, 0, 1, '', null, 21, '2025-06-09 18:50:01', null),
|
||||
(60, '修改', 'EditSysDictData', null, 0, null, 2, null, 'dict:data:edit', 1, 0, 1, '', null, 21, '2025-06-09 18:50:26', null),
|
||||
(61, '删除', 'DeleteSysDictData', null, 0, null, 2, null, 'dict:data:del', 1, 0, 1, '', null, 21, '2025-06-09 18:50:48', null),
|
||||
(62, '新增', 'AddSysNotice', null, 0, null, 2, null, 'sys:notice:add', 1, 0, 1, '', null, 22, '2025-06-09 18:51:22', null),
|
||||
(63, '修改', 'EditSysNotice', null, 0, null, 2, null, 'sys:notice:edit', 1, 0, 1, '', null, 22, '2025-06-09 18:51:45', null),
|
||||
(64, '删除', 'DeleteSysNotice', null, 0, null, 2, null, 'sys:notice:del', 1, 0, 1, '', null, 22, '2025-06-09 18:52:10', null),
|
||||
(65, '新增业务', 'AddSysGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:add', 1, 0, 1, '', null, 23, '2025-06-09 18:53:07', null),
|
||||
(66, '修改业务', 'EditGenCodeBusiness', null, 0, null, 2, null, 'codegen:business:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:53: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, '新增模型', 'AddGenCodeModel', null, 0, null, 2, null, 'codegen:model:add', 1, 0, 1, '', null, 23, '2025-06-09 18:54:45', null),
|
||||
(69, '修改模型', 'EditGenCodeModel', null, 0, null, 2, null, 'codegen:model:edit', 1, 0, 1, '', null, 23, '2025-06-09 18:55:08', null),
|
||||
(70, '删除模型', 'DeleteGenCodeModel', null, 0, null, 2, null, 'codegen:model:del', 1, 0, 1, '', null, 23, '2025-06-09 18:55:35', null),
|
||||
(71, '导入', 'ImportGenCode', null, 0, null, 2, null, 'codegen:table:import', 1, 0, 1, '', null, 23, '2025-06-09 18:58:16', null),
|
||||
(72, '写入', 'WriteGenCode', null, 0, null, 2, null, 'codegen:local:write', 1, 0, 1, '', null, 23, '2025-06-09 19:01:22', null),
|
||||
(73, '删除', 'DeleteSysLoginLog', null, 0, null, 2, null, 'log:login:del', 1, 0, 1, '', null, 25, '2025-06-09 19:02:21', null),
|
||||
(74, '清空', 'EmptyLoginLog', null, 0, null, 2, null, 'log:login:clear', 1, 0, 1, '', null, 25, '2025-06-09 19:02:50', null),
|
||||
(75, '删除', 'DeleteOperaLog', null, 0, null, 2, null, 'log:opera:del', 1, 0, 1, '', null, 26, '2025-06-09 19:03:13', null),
|
||||
(76, '清空', 'EmptyOperaLog', null, 0, null, 2, null, 'log:opera:clear', 1, 0, 1, '', null, 26, '2025-06-09 19:03:40', null),
|
||||
(77, '下线', 'KickSysToken', null, 0, null, 2, null, 'sys:session:delete', 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)
|
||||
values (1, '测试', 1, 1, null, '2025-05-26 17:13:45', null);
|
||||
|
||||
@@ -28,12 +28,11 @@ def build_filename(file: UploadFile) -> str:
|
||||
return new_filename
|
||||
|
||||
|
||||
def file_verify(file: UploadFile, file_type: FileType) -> None:
|
||||
def file_verify(file: UploadFile) -> None:
|
||||
"""
|
||||
文件验证
|
||||
|
||||
:param file: FastAPI 上传文件对象
|
||||
:param file_type: 文件类型枚举
|
||||
:return:
|
||||
"""
|
||||
filename = file.filename
|
||||
@@ -41,12 +40,12 @@ def file_verify(file: UploadFile, file_type: FileType) -> None:
|
||||
if not file_ext:
|
||||
raise errors.ForbiddenError(msg='未知的文件类型')
|
||||
|
||||
if file_type == FileType.image:
|
||||
if file_ext == FileType.image:
|
||||
if file_ext not in settings.UPLOAD_IMAGE_EXT_INCLUDE:
|
||||
raise errors.ForbiddenError(msg='此图片格式暂不支持')
|
||||
if file.size > settings.UPLOAD_IMAGE_SIZE_MAX:
|
||||
raise errors.ForbiddenError(msg='图片超出最大限制,请重新选择')
|
||||
elif file_type == FileType.video:
|
||||
elif file_ext == FileType.video:
|
||||
if file_ext not in settings.UPLOAD_VIDEO_EXT_INCLUDE:
|
||||
raise errors.ForbiddenError(msg='此视频格式暂不支持')
|
||||
if file.size > settings.UPLOAD_VIDEO_SIZE_MAX:
|
||||
|
||||
Reference in New Issue
Block a user