diff --git a/backend/app/admin/api/v1/auth/auth.py b/backend/app/admin/api/v1/auth/auth.py index 1c22b10d..0321c1b1 100644 --- a/backend/app/admin/api/v1/auth/auth.py +++ b/backend/app/admin/api/v1/auth/auth.py @@ -34,8 +34,8 @@ async def user_login( return response_base.success(data=data) -@router.post('/token/new', summary='创建新 token') -async def create_new_token(request: Request) -> ResponseSchemaModel[GetNewToken]: +@router.post('/tokens/refresh', summary='刷新 token') +async def refresh_token(request: Request) -> ResponseSchemaModel[GetNewToken]: data = await auth_service.new_token(request=request) return response_base.success(data=data) diff --git a/backend/app/admin/api/v1/sys/__init__.py b/backend/app/admin/api/v1/sys/__init__.py index e796d376..3411dcf8 100644 --- a/backend/app/admin/api/v1/sys/__init__.py +++ b/backend/app/admin/api/v1/sys/__init__.py @@ -20,4 +20,4 @@ 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(plugin_router, prefix='/plugin', tags=['系统插件']) +router.include_router(plugin_router, prefix='/plugins', tags=['系统插件']) diff --git a/backend/app/admin/api/v1/sys/data_rule.py b/backend/app/admin/api/v1/sys/data_rule.py index 91c42166..ecd9b119 100644 --- a/backend/app/admin/api/v1/sys/data_rule.py +++ b/backend/app/admin/api/v1/sys/data_rule.py @@ -27,7 +27,7 @@ async def get_data_rule_models() -> ResponseSchemaModel[list[str]]: return response_base.success(data=models) -@router.get('/model/{model}/columns', summary='获取数据规则可用模型列', dependencies=[DependsJwtAuth]) +@router.get('/models/{model}/columns', summary='获取数据规则可用模型列', dependencies=[DependsJwtAuth]) async def get_data_rule_model_columns( model: Annotated[str, Path(description='模型名称')], ) -> ResponseSchemaModel[list[GetDataRuleColumnDetail]]: diff --git a/backend/app/admin/api/v1/sys/plugin.py b/backend/app/admin/api/v1/sys/plugin.py index 0d12e5c2..eebf6c1a 100644 --- a/backend/app/admin/api/v1/sys/plugin.py +++ b/backend/app/admin/api/v1/sys/plugin.py @@ -22,18 +22,18 @@ async def get_all_plugins() -> ResponseSchemaModel[list[dict[str, Any]]]: return response_base.success(data=plugins) -@router.get('/changed', summary='插件状态是否变更', dependencies=[DependsJwtAuth]) +@router.get('/changes', summary='插件状态是否变更', dependencies=[DependsJwtAuth]) async def plugin_changed() -> ResponseSchemaModel[bool]: plugins = await plugin_service.changed() return response_base.success(data=bool(plugins)) @router.post( - '/install/zip', + '/zip', summary='安装 zip 插件', description='使用插件 zip 压缩包进行安装', dependencies=[ - Depends(RequestPermission('sys:plugin:install')), + Depends(RequestPermission('sys:plugin:zip')), DependsRBAC, ], ) @@ -43,11 +43,11 @@ async def install_zip_plugin(file: Annotated[UploadFile, File()]) -> ResponseMod @router.post( - '/install/git', + '/git', summary='安装 git 插件', description='使用插件 git 仓库地址进行安装,不限制平台;如果需要凭证,需在 git 仓库地址中添加凭证信息', dependencies=[ - Depends(RequestPermission('sys:plugin:install')), + Depends(RequestPermission('sys:plugin:git')), DependsRBAC, ], ) @@ -57,35 +57,35 @@ async def install_git_plugin(repo_url: Annotated[str, Query(description='插件 @router.delete( - '/uninstall', + '/{plugin}', summary='卸载插件', description='此操作会直接删除插件依赖,但不会直接删除插件,而是将插件移动到备份目录', dependencies=[ - Depends(RequestPermission('sys:plugin:uninstall')), + Depends(RequestPermission('sys:plugin:del')), DependsRBAC, ], ) -async def uninstall_plugin(plugin: Annotated[str, Query(description='插件名称')]) -> ResponseModel: +async def uninstall_plugin(plugin: Annotated[str, Path(description='插件名称')]) -> ResponseModel: await plugin_service.uninstall(plugin=plugin) return response_base.success(res=CustomResponseCode.PLUGIN_UNINSTALL_SUCCESS) @router.post( - '/status', + '/{plugin}/status', summary='更新插件状态', dependencies=[ Depends(RequestPermission('sys:plugin:status')), DependsRBAC, ], ) -async def update_plugin_status(plugin: Annotated[str, Query(description='插件名称')]) -> ResponseModel: +async def update_plugin_status(plugin: Annotated[str, Path(description='插件名称')]) -> ResponseModel: await plugin_service.update_status(plugin=plugin) return response_base.success() @router.get( - '/zip/{plugin}', - summary='打包插件', + '/{plugin}', + summary='打包并下载插件', dependencies=[ Depends(RequestPermission('sys:plugin:zip')), DependsRBAC, diff --git a/backend/app/admin/api/v1/sys/role.py b/backend/app/admin/api/v1/sys/role.py index 027a8133..5a4c5b2d 100644 --- a/backend/app/admin/api/v1/sys/role.py +++ b/backend/app/admin/api/v1/sys/role.py @@ -29,14 +29,6 @@ async def get_all_roles() -> ResponseSchemaModel[list[GetRoleDetail]]: return response_base.success(data=data) -@router.get('/{pk}/all', summary='获取用户所有角色', dependencies=[DependsJwtAuth]) -async def get_user_all_roles( - pk: Annotated[int, Path(description='用户 ID')], -) -> ResponseSchemaModel[list[GetRoleDetail]]: - data = await role_service.get_users(pk=pk) - return response_base.success(data=data) - - @router.get('/{pk}/menus', summary='获取角色所有菜单', dependencies=[DependsJwtAuth]) async def get_role_all_menus( pk: Annotated[int, Path(description='角色 ID')], @@ -106,7 +98,7 @@ async def update_role(pk: Annotated[int, Path(description='角色 ID')], obj: Up @router.put( - '/{pk}/menu', + '/{pk}/menus', summary='更新角色菜单', dependencies=[ Depends(RequestPermission('sys:role:menu:edit')), @@ -123,7 +115,7 @@ async def update_role_menus( @router.put( - '/{pk}/scope', + '/{pk}/scopes', summary='更新角色数据范围', dependencies=[ Depends(RequestPermission('sys:role:scope:edit')), diff --git a/backend/app/admin/api/v1/sys/user.py b/backend/app/admin/api/v1/sys/user.py index 972e2544..a27ea8dd 100644 --- a/backend/app/admin/api/v1/sys/user.py +++ b/backend/app/admin/api/v1/sys/user.py @@ -4,6 +4,7 @@ from fastapi import APIRouter, Depends, Path, Query, Request +from backend.app.admin.schema.role import GetRoleDetail from backend.app.admin.schema.user import ( AddUserParam, GetCurrentUserInfoWithRelationDetail, @@ -36,9 +37,11 @@ async def add_user(request: Request, obj: AddUserParam) -> ResponseSchemaModel[G return response_base.success(data=data) -@router.post('/password/reset', summary='密码重置', dependencies=[DependsJwtAuth]) -async def password_reset(request: Request, obj: ResetPasswordParam) -> ResponseModel: - count = await user_service.pwd_reset(request=request, obj=obj) +@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() @@ -58,14 +61,12 @@ async def get_user( return response_base.success(data=data) -@router.put('/{username}', summary='更新用户信息', dependencies=[DependsJwtAuth]) -async def update_user( - request: Request, username: Annotated[str, Path(description='用户名')], obj: UpdateUserParam -) -> ResponseModel: - count = await user_service.update(request=request, username=username, obj=obj) - if count > 0: - return response_base.success() - return response_base.fail() +@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) + return response_base.success(data=data) @router.get( @@ -88,6 +89,16 @@ async def get_pagination_users( return response_base.success(data=page_data) +@router.put('/{username}', summary='更新用户信息', dependencies=[DependsJwtAuth]) +async def update_user( + request: Request, username: Annotated[str, Path(description='用户名')], obj: UpdateUserParam +) -> ResponseModel: + count = await user_service.update(request=request, username=username, 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) diff --git a/backend/app/admin/crud/crud_role.py b/backend/app/admin/crud/crud_role.py index 1b1df841..69e41dd3 100644 --- a/backend/app/admin/crud/crud_role.py +++ b/backend/app/admin/crud/crud_role.py @@ -7,7 +7,7 @@ from sqlalchemy.orm import noload, selectinload from sqlalchemy_crud_plus import CRUDPlus -from backend.app.admin.model import DataScope, Menu, Role, User +from backend.app.admin.model import DataScope, Menu, Role from backend.app.admin.schema.role import ( CreateRoleParam, UpdateRoleMenuParam, @@ -54,18 +54,6 @@ async def get_all(self, db: AsyncSession) -> Sequence[Role]: """ return await self.select_models(db) - async def get_users(self, db: AsyncSession, user_id: int) -> Sequence[Role]: - """ - 获取用户角色列表 - - :param db: 数据库会话 - :param user_id: 用户 ID - :return: - """ - stmt = select(self.model).join(self.model.users).where(User.id == user_id) - roles = await db.execute(stmt) - return roles.scalars().all() - async def get_list(self, name: str | None, status: int | None) -> Select: """ 获取角色列表 diff --git a/backend/app/admin/service/role_service.py b/backend/app/admin/service/role_service.py index 19e6d50c..9708348f 100644 --- a/backend/app/admin/service/role_service.py +++ b/backend/app/admin/service/role_service.py @@ -45,18 +45,6 @@ async def get_all() -> Sequence[Role]: roles = await role_dao.get_all(db) return roles - @staticmethod - async def get_users(*, pk: int) -> Sequence[Role]: - """ - 获取用户的角色列表 - - :param pk: 用户 ID - :return: - """ - async with async_db_session() as db: - roles = await role_dao.get_users(db, user_id=pk) - return roles - @staticmethod async def get_select(*, name: str | None, status: int | None) -> Select: """ diff --git a/backend/app/admin/service/user_service.py b/backend/app/admin/service/user_service.py index 32e36282..7730f68e 100644 --- a/backend/app/admin/service/user_service.py +++ b/backend/app/admin/service/user_service.py @@ -2,13 +2,15 @@ # -*- coding: utf-8 -*- import random +from typing import Sequence + from fastapi import Request from sqlalchemy import Select from backend.app.admin.crud.crud_dept import dept_dao from backend.app.admin.crud.crud_role import role_dao from backend.app.admin.crud.crud_user import user_dao -from backend.app.admin.model import User +from backend.app.admin.model import Role, User from backend.app.admin.schema.user import ( AddUserParam, RegisterUserParam, @@ -81,16 +83,16 @@ async def add(*, request: Request, obj: AddUserParam) -> None: await user_dao.add(db, obj) @staticmethod - async def pwd_reset(*, request: Request, obj: ResetPasswordParam) -> int: + async def pwd_reset(*, username: str, obj: ResetPasswordParam) -> int: """ 重置用户密码 - :param request: FastAPI 请求对象 + :param username: 用户名 :param obj: 密码重置参数 :return: """ async with async_db_session.begin() as db: - user = await user_dao.get(db, request.user.id) + 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): @@ -98,11 +100,11 @@ async def pwd_reset(*, request: Request, obj: ResetPasswordParam) -> int: 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, request.user.id, new_pwd) + count = await user_dao.reset_password(db, user.id, new_pwd) key_prefix = [ - f'{settings.TOKEN_REDIS_PREFIX}:{request.user.id}', - f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{request.user.id}', - f'{settings.JWT_USER_REDIS_PREFIX}:{request.user.id}', + 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) @@ -122,6 +124,33 @@ async def get_userinfo(*, username: str) -> User: raise errors.NotFoundError(msg='用户不存在') return user + @staticmethod + async def get_roles(*, username: str) -> Sequence[Role]: + """ + 获取用户所有角色 + + :param username: 用户名 + :return: + """ + async with async_db_session() as db: + user = await user_dao.get_with_relation(db, username=username) + if not user: + raise errors.NotFoundError(msg='用户不存在') + return user.roles + + @staticmethod + async def get_select(*, dept: int, username: str, phone: str, status: int) -> Select: + """ + 获取用户列表查询条件 + + :param dept: 部门 ID + :param username: 用户名 + :param phone: 手机号 + :param status: 状态 + :return: + """ + 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: """ @@ -158,19 +187,6 @@ async def update(*, request: Request, username: str, obj: UpdateUserParam) -> in await redis_client.delete(f'{settings.JWT_USER_REDIS_PREFIX}:{user.id}') return count - @staticmethod - async def get_select(*, dept: int, username: str, phone: str, status: int) -> Select: - """ - 获取用户列表查询条件 - - :param dept: 部门 ID - :param username: 用户名 - :param phone: 手机号 - :param status: 状态 - :return: - """ - return await user_dao.get_list(dept=dept, username=username, phone=phone, status=status) - @staticmethod async def update_permission(*, request: Request, pk: int) -> int: """ diff --git a/backend/plugin/code_generator/api/router.py b/backend/plugin/code_generator/api/router.py index 6036b831..61068dd9 100644 --- a/backend/plugin/code_generator/api/router.py +++ b/backend/plugin/code_generator/api/router.py @@ -9,6 +9,6 @@ v1 = APIRouter(prefix=f'{settings.FASTAPI_API_V1_PATH}/gen', tags=['代码生成']) -v1.include_router(gen_router) +v1.include_router(gen_router, prefix='/tables') v1.include_router(business_router, prefix='/businesses') v1.include_router(model_router, prefix='/models') diff --git a/backend/plugin/code_generator/api/v1/gen.py b/backend/plugin/code_generator/api/v1/gen.py index 0d30920d..4a166cc5 100644 --- a/backend/plugin/code_generator/api/v1/gen.py +++ b/backend/plugin/code_generator/api/v1/gen.py @@ -16,7 +16,7 @@ router = APIRouter() -@router.get('/tables', summary='获取数据库表') +@router.get('', summary='获取数据库表') async def get_all_tables( table_schema: Annotated[str, Query(description='数据库名')] = 'fba', ) -> ResponseSchemaModel[list[str]]: @@ -37,20 +37,20 @@ async def import_table(obj: ImportParam) -> ResponseModel: return response_base.success() -@router.get('/preview/{pk}', summary='生成代码预览', dependencies=[DependsJwtAuth]) +@router.get('/{pk}/preview', 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('/generate/{pk}/path', summary='获取代码生成路径', dependencies=[DependsJwtAuth]) +@router.get('/{pk}/code/path', summary='获取代码生成路径', dependencies=[DependsJwtAuth]) async def generate_path(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( - '/generate/{pk}', + '/{pk}/code', summary='代码生成', description='文件磁盘写入,请谨慎操作', dependencies=[ @@ -63,7 +63,7 @@ async def generate_code(pk: Annotated[int, Path(description='业务 ID')]) -> Re return response_base.success() -@router.get('/download/{pk}', summary='下载代码', dependencies=[DependsJwtAuth]) +@router.get('/{pk}', summary='下载代码', dependencies=[DependsJwtAuth]) async def download_code(pk: Annotated[int, Path(description='业务 ID')]): bio = await gen_service.download(pk=pk) return StreamingResponse(