mirror of
https://github.com/fastapi-practices/fastapi-best-architecture.git
synced 2026-09-21 21:15:13 +00:00
Refactor routes to better align with RESTful (#645)
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -20,4 +20,4 @@ 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(plugin_router, prefix='/plugin', tags=['系统插件'])
|
||||
router.include_router(plugin_router, prefix='/plugins', tags=['系统插件'])
|
||||
|
||||
@@ -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]]:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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')),
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Annotated
|
||||
|
||||
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)
|
||||
|
||||
@@ -7,7 +7,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
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 @@ class CRUDRole(CRUDPlus[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:
|
||||
"""
|
||||
获取角色列表
|
||||
|
||||
@@ -45,18 +45,6 @@ class RoleService:
|
||||
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:
|
||||
"""
|
||||
|
||||
@@ -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 @@ class UserService:
|
||||
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 @@ class UserService:
|
||||
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 @@ class UserService:
|
||||
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 @@ class UserService:
|
||||
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:
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
v1.include_router(gen_router, prefix='/tables')
|
||||
v1.include_router(business_router, prefix='/businesses')
|
||||
v1.include_router(model_router, prefix='/models')
|
||||
|
||||
@@ -16,7 +16,7 @@ from backend.plugin.code_generator.service.gen_service import gen_service
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user