feat: role & menu api
This commit is contained in:
parent
f27d2e9f7c
commit
8e154d492a
3
backend/.gitignore
vendored
3
backend/.gitignore
vendored
@ -141,4 +141,5 @@ cython_debug/
|
||||
.pdm.toml
|
||||
__pypackages__/
|
||||
.pytest_cache
|
||||
.db-*
|
||||
.db*
|
||||
*/.pytest_cache
|
@ -1,17 +1 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
from core.security import check_token
|
||||
|
||||
|
||||
def register_routers(app: FastAPI):
|
||||
from controller.common import common
|
||||
from controller.menu import menu
|
||||
from controller.role import role
|
||||
from controller.user import user
|
||||
|
||||
app.include_router(router=common)
|
||||
app.include_router(
|
||||
router=user,
|
||||
)
|
||||
app.include_router(router=menu)
|
||||
app.include_router(router=role)
|
||||
|
@ -4,7 +4,7 @@ from schemas import LoginForm, LoginResult, Response
|
||||
|
||||
|
||||
async def login(auth_data: LoginForm) -> Response[LoginResult]:
|
||||
user_obj = await get_user({"username": auth_data.username})
|
||||
user_obj = await get_user({"username": auth_data.username, "status__not": 9})
|
||||
if user_obj:
|
||||
if verify_password(auth_data.password, user_obj.password):
|
||||
return Response(
|
||||
@ -12,4 +12,9 @@ async def login(auth_data: LoginForm) -> Response[LoginResult]:
|
||||
id=user_obj.id, token=generate_token(auth_data.username)
|
||||
)
|
||||
)
|
||||
return Response(msg="账号或密码错误")
|
||||
return Response(code=400, msg="账号或密码错误")
|
||||
|
||||
|
||||
async def about() -> Response:
|
||||
"""关于"""
|
||||
pass
|
||||
|
@ -1,6 +1,23 @@
|
||||
from dbhelper.menu import insert_menu
|
||||
from schemas import MenuIn, MenuRead, Response
|
||||
from fastapi import Query
|
||||
|
||||
from dbhelper.menu import del_menu, get_menus, insert_menu
|
||||
from schemas import ListAll, MenuIn, MenuRead, Response
|
||||
|
||||
|
||||
async def menu_add(data: MenuIn) -> Response[MenuRead]:
|
||||
return Response(data=await insert_menu(data))
|
||||
|
||||
|
||||
async def menu_arr(
|
||||
offset: int = Query(default=1, description="偏移量"),
|
||||
limit: int = Query(default=10, description="数量"),
|
||||
) -> Response[ListAll[list[MenuRead]]]:
|
||||
skip = (offset - 1) * limit
|
||||
menus, count = await get_menus(skip, limit)
|
||||
return Response(data=ListAll(total=count, items=menus))
|
||||
|
||||
|
||||
async def menu_del(pk: int) -> Response:
|
||||
if await del_menu(pk) == 0:
|
||||
return Response(code=400, msg="菜单不存在")
|
||||
return Response()
|
||||
|
@ -1,8 +1,18 @@
|
||||
import json
|
||||
|
||||
from fastapi import Query
|
||||
|
||||
from core.utils import list_to_tree
|
||||
from dbhelper.role import get_role_menus, new_role
|
||||
from schemas import Response, RoleIn, RoleInfo
|
||||
from dbhelper.relation import role_assigned_menu
|
||||
from dbhelper.role import (
|
||||
del_role,
|
||||
get_role,
|
||||
get_role_menus,
|
||||
get_roles,
|
||||
new_role,
|
||||
put_role,
|
||||
)
|
||||
from schemas import ListAll, Response, RoleIn, RoleInfo, RoleMenuIn, RoleQuery, RoleRead
|
||||
|
||||
|
||||
async def role_add(data: RoleIn) -> Response[RoleInfo]:
|
||||
@ -17,3 +27,43 @@ async def role_has_menu(rid: int):
|
||||
for obj in menus:
|
||||
obj["meta"] = json.loads(obj["meta"]) if obj["meta"] is not None else None
|
||||
return Response(data=list_to_tree(menus))
|
||||
|
||||
|
||||
async def role_arr(
|
||||
offset: int = Query(default=1, description="偏移量-页码"),
|
||||
limit: int = Query(default=10, description="数据量"),
|
||||
) -> Response[ListAll[list[RoleRead]]]:
|
||||
skip = (offset - 1) * limit
|
||||
roles, count = await get_roles(skip, limit)
|
||||
return Response(data=ListAll(total=count, items=roles))
|
||||
|
||||
|
||||
async def assigned_menu(data: RoleMenuIn) -> Response:
|
||||
"""分配菜单给角色"""
|
||||
if await get_role({"id": data.rid, "status__not": 9}) is None:
|
||||
return Response(code=400, msg="角色不存在")
|
||||
if isinstance(await role_assigned_menu(data), int):
|
||||
return Response(code=400, msg=f"菜单不存在")
|
||||
return Response()
|
||||
|
||||
|
||||
async def role_del(pk: int) -> Response:
|
||||
if await del_role(pk) == 0:
|
||||
return Response(code=400, msg="角色不存在")
|
||||
return Response()
|
||||
|
||||
|
||||
async def role_put(pk: int, data: RoleIn) -> Response:
|
||||
"""更新角色"""
|
||||
if await put_role(pk, data) == 0:
|
||||
return Response(code=400, msg="角色不存在")
|
||||
return Response()
|
||||
|
||||
|
||||
async def role_query(query: RoleQuery) -> Response[ListAll[list[RoleRead]]]:
|
||||
"""post条件查询角色表"""
|
||||
limit = query.size
|
||||
skip = (query.offset - 1) * limit
|
||||
del query.offset, query.size
|
||||
users, count = await get_roles(skip, limit, query.dict())
|
||||
return Response(data=ListAll(total=count, items=users))
|
||||
|
@ -1,48 +1,71 @@
|
||||
from fastapi import Query
|
||||
|
||||
from core.security import get_password_hash
|
||||
from dbhelper.user import get_user, get_user_info, get_users, insert_user, new_user
|
||||
from schemas import Response, UserAdd, UserIn, UserInfo, UserList, UserQuery, UserRead
|
||||
from dbhelper.user import (
|
||||
del_user,
|
||||
get_user,
|
||||
get_user_info,
|
||||
get_users,
|
||||
insert_user,
|
||||
put_user,
|
||||
)
|
||||
from schemas import Response, UserAdd, UserInfo, UserPut, UserQuery, UserRead
|
||||
from schemas.common import ListAll
|
||||
|
||||
|
||||
async def user_add(data: UserAdd) -> Response[UserInfo]:
|
||||
async def user_add(data: UserAdd) -> Response[UserRead]:
|
||||
"""新增用户并分配角色 一步到位"""
|
||||
if await get_user({"username": data.username}) is not None:
|
||||
return Response(code=400, msg="用户名已存在")
|
||||
roles = data.rids
|
||||
del data.rids
|
||||
data.password = get_password_hash(data.password)
|
||||
return await insert_user(data, roles)
|
||||
|
||||
|
||||
async def create_user(data: UserIn) -> Response[UserRead]:
|
||||
"""新增用户"""
|
||||
result = await get_user({"username": data.username})
|
||||
if result is None:
|
||||
data.password = get_password_hash(data.password)
|
||||
return Response(data=await new_user(data))
|
||||
return Response(msg="用户名已存在")
|
||||
result = await insert_user(data, roles)
|
||||
if isinstance(result, int):
|
||||
return Response(code=400, msg=f"角色{result}不存在")
|
||||
return Response(data=result)
|
||||
|
||||
|
||||
async def user_info(pk: int) -> Response[UserInfo]:
|
||||
try:
|
||||
return Response(data=await get_user_info(pk))
|
||||
except Exception as e:
|
||||
return Response(msg=f"用户不存在 {e}")
|
||||
"""获取用户信息"""
|
||||
obj = await get_user({"id": pk})
|
||||
if obj is None:
|
||||
return Response(code=400, msg="用户不存在")
|
||||
return Response(data=await get_user_info(obj))
|
||||
|
||||
|
||||
async def user_arr(
|
||||
offset: int = Query(default=1, description="偏移量-页码"),
|
||||
limit: int = Query(default=10, description="数据量"),
|
||||
) -> Response[ListAll[UserList]]:
|
||||
) -> Response[ListAll[list[UserRead]]]:
|
||||
"""分页列表数据"""
|
||||
skip = (offset - 1) * limit
|
||||
users, count = await get_users(skip, limit)
|
||||
return Response(data=ListAll(total=count, items=users))
|
||||
|
||||
|
||||
async def user_list(query: UserQuery) -> Response[ListAll[UserList]]:
|
||||
async def user_list(query: UserQuery) -> Response[ListAll[list[UserRead]]]:
|
||||
"""post查询用户列表"""
|
||||
limit = query.size
|
||||
skip = (query.offset - 1) * limit
|
||||
del query.offset, query.size
|
||||
users, count = await get_users(skip, limit, query.dict())
|
||||
return Response(data=ListAll(total=count, items=users))
|
||||
|
||||
|
||||
async def user_del(pk: int) -> Response:
|
||||
"""删除用户"""
|
||||
if await del_user(pk) == 0:
|
||||
return Response(code=400, msg="用户不存在")
|
||||
return Response()
|
||||
|
||||
|
||||
async def user_put(pk: int, data: UserPut) -> Response:
|
||||
"""更新用户"""
|
||||
if await get_user({"id": pk}) is None:
|
||||
return Response(code=400, msg="用户不存在")
|
||||
|
||||
result = await put_user(pk, data)
|
||||
if isinstance(result, int):
|
||||
return Response(code=400, msg=f"角色不存在{result}")
|
||||
return Response()
|
||||
|
@ -10,6 +10,6 @@ sh.setLevel(logging.DEBUG)
|
||||
sh.setFormatter(fmt)
|
||||
|
||||
# will print debug sql
|
||||
logger_db_client = logging.getLogger("mini-rbac")
|
||||
logger_db_client.setLevel(logging.DEBUG)
|
||||
logger_db_client.addHandler(sh)
|
||||
logger = logging.getLogger("mini-rbac")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.addHandler(sh)
|
||||
|
@ -5,3 +5,39 @@ from schemas.menu import MenuIn
|
||||
async def insert_menu(menu: MenuIn):
|
||||
"""新增菜单"""
|
||||
return await MenuModel.create(**menu.dict())
|
||||
|
||||
|
||||
async def get_menus(skip: int, limit: int, kwargs: dict = None):
|
||||
"""
|
||||
分页获取用户并且支持字段模糊查询
|
||||
Args:
|
||||
skip: 偏移量
|
||||
limit: 数量
|
||||
kwargs: 查询字典
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if kwargs is not None:
|
||||
kwargs = {f"{k}__contains": v for k, v in kwargs.items()}
|
||||
else:
|
||||
kwargs = {}
|
||||
result = MenuModel.filter(status__not=9, **kwargs).all().order_by("-created")
|
||||
return await result.offset(skip).limit(limit), await result.count()
|
||||
|
||||
|
||||
async def get_menu(kwargs):
|
||||
"""
|
||||
根据条件查询到第一条符合结果的数据
|
||||
Args:
|
||||
kwargs:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return await MenuModel.filter(**kwargs).first()
|
||||
|
||||
|
||||
async def del_menu(mid: int):
|
||||
"""删除用户"""
|
||||
return await MenuModel.filter(id=mid).update(status=9)
|
||||
|
31
backend/dbhelper/relation.py
Normal file
31
backend/dbhelper/relation.py
Normal file
@ -0,0 +1,31 @@
|
||||
from tortoise import connections
|
||||
|
||||
from dbhelper.menu import get_menu
|
||||
from models import RoleMenuModel, UserRoleModel
|
||||
from schemas import UserRole
|
||||
|
||||
|
||||
async def user_assigned_role(data: UserRole):
|
||||
"""给用户分配角色"""
|
||||
return await UserRoleModel.create(**data.dict())
|
||||
|
||||
|
||||
async def role_assigned_menu(data):
|
||||
"""给角色分配菜单"""
|
||||
for mid in data.menus:
|
||||
if await get_menu({"id": mid}) is None:
|
||||
return mid
|
||||
|
||||
# todo 性能优化
|
||||
db = connections.get("default")
|
||||
# 1. 先把所有数据做删除
|
||||
await db.execute_query_dict(
|
||||
"""
|
||||
update sys_role_menu set status = 9 where rid = (?)
|
||||
""",
|
||||
[data.rid],
|
||||
)
|
||||
# 2. 新增数据
|
||||
await RoleMenuModel.bulk_create(
|
||||
[RoleMenuModel(rid=data.rid, mid=mid) for mid in data.menus]
|
||||
)
|
@ -11,9 +11,9 @@ async def get_role_menus(rid: int):
|
||||
db = connections.get("default")
|
||||
return await db.execute_query_dict(
|
||||
"""
|
||||
select m.id, m.name, m.meta, m.path, m.type, m.component, m.pid, m.identifier, m.api_regx,m.api, m.method, m.sort
|
||||
select m.id, m.name, m.meta, m.path, m.type, m.component, m.pid, m.identifier, m.regx,m.api, m.method
|
||||
FROM sys_menu as m, sys_role_menu WHERE m.id = sys_role_menu.mid
|
||||
AND sys_role_menu.rid = (%s) AND m.`status` = 1 ORDER BY m.sort""",
|
||||
AND sys_role_menu.rid = (?) AND m.`status` = 1""",
|
||||
[rid],
|
||||
)
|
||||
|
||||
@ -21,3 +21,44 @@ async def get_role_menus(rid: int):
|
||||
async def new_role(role: RoleIn):
|
||||
"""新增角色"""
|
||||
return await RoleModel.create(**role.dict())
|
||||
|
||||
|
||||
async def get_roles(skip: int, limit: int, kwargs: dict = None):
|
||||
"""
|
||||
分页获取用户并且支持字段模糊查询
|
||||
Args:
|
||||
skip: 偏移量
|
||||
limit: 数量
|
||||
kwargs: 查询字典
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if kwargs is not None:
|
||||
kwargs = {f"{k}__contains": v for k, v in kwargs.items()}
|
||||
else:
|
||||
kwargs = {}
|
||||
result = RoleModel.filter(status__not=9, **kwargs).all().order_by("-created")
|
||||
return await result.offset(skip).limit(limit), await result.count()
|
||||
|
||||
|
||||
async def get_role(kwargs):
|
||||
"""
|
||||
根据条件查询到第一条符合结果的数据
|
||||
Args:
|
||||
kwargs:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return await RoleModel.filter(**kwargs).first()
|
||||
|
||||
|
||||
async def del_role(rid: int):
|
||||
"""删除用户"""
|
||||
return await RoleModel.filter(id=rid).update(status=9)
|
||||
|
||||
|
||||
async def put_role(pk: int, data):
|
||||
"""更新角色"""
|
||||
return await RoleModel.filter(id=pk).update(**data.dict())
|
||||
|
@ -1,7 +1,9 @@
|
||||
from tortoise.transactions import atomic
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from tortoise import connections
|
||||
|
||||
from models import RoleModel, UserModel, UserRoleModel
|
||||
from schemas.user import UserIn, UserRole
|
||||
from dbhelper.role import get_role
|
||||
from models import UserModel, UserRoleModel
|
||||
from schemas import UserPut
|
||||
|
||||
|
||||
async def get_user(kwargs):
|
||||
@ -16,25 +18,21 @@ async def get_user(kwargs):
|
||||
return await UserModel.filter(**kwargs).first()
|
||||
|
||||
|
||||
async def get_user_info(pk: int):
|
||||
async def get_user_info(user: UserModel):
|
||||
"""
|
||||
根据id查用户角色列表,当前激活角色
|
||||
根据id查用户角色列表 按激活角色倒序显示
|
||||
"""
|
||||
user = await UserModel.get(pk=pk).values(
|
||||
"id", "username", "nickname", "identity", "created", "modified"
|
||||
db = connections.get("default")
|
||||
sql_result = await db.execute_query_dict(
|
||||
"""
|
||||
select r.id, r.name, ur.status from sys_role as r
|
||||
left join sys_user_role as ur on r.id = ur.rid where
|
||||
ur.uid = (?) and ur.status != 9 and r.status != 9 order by ur.status desc
|
||||
""",
|
||||
[user.id],
|
||||
)
|
||||
role = (
|
||||
await UserRoleModel.filter(uid=pk, status__not_in=[9, 5])
|
||||
.all()
|
||||
.values("rid", "status")
|
||||
)
|
||||
active_rid = role[0].get("rid")
|
||||
rids = []
|
||||
for obj in role:
|
||||
if obj.get("status") == 5:
|
||||
active_rid = obj.get("rid")
|
||||
rids.append(obj.get("rid"))
|
||||
return {**user, "active_rid": active_rid, "rids": rids}
|
||||
|
||||
return {**jsonable_encoder(user), "roles": sql_result}
|
||||
|
||||
|
||||
async def get_users(skip: int, limit: int, kwargs: dict = None):
|
||||
@ -58,22 +56,51 @@ async def get_users(skip: int, limit: int, kwargs: dict = None):
|
||||
return await result.offset(skip).limit(limit), await result.count()
|
||||
|
||||
|
||||
@atomic()
|
||||
async def insert_user(user, roles):
|
||||
for index, rid in enumerate(roles):
|
||||
# 1. 查角色表是否有该角色
|
||||
await RoleModel.get(pk=rid)
|
||||
"""新增用户,选择角色"""
|
||||
for role in roles:
|
||||
if await get_role({"id": role.rid, "status__not": 9}) is None:
|
||||
return role.rid
|
||||
|
||||
# 创建用户
|
||||
obj = await UserModel.create(**user.dict())
|
||||
|
||||
user_role = UserRole(rid=rid, uid=obj.id)
|
||||
if index == 0:
|
||||
user_role.status = 5
|
||||
# 第一个角色默认, 添加到关系表
|
||||
await UserRoleModel.create(**user_role.dict())
|
||||
return user
|
||||
await UserRoleModel.bulk_create(
|
||||
[UserRoleModel(rid=role.rid, uid=obj.id, status=role.status) for role in roles]
|
||||
)
|
||||
return obj
|
||||
|
||||
|
||||
async def new_user(user: UserIn):
|
||||
"""新增用户"""
|
||||
return await UserModel.create(**user.dict())
|
||||
async def del_user(uid: int):
|
||||
"""删除用户"""
|
||||
return await UserModel.filter(id=uid).update(status=9)
|
||||
|
||||
|
||||
async def put_user(uid: int, data: UserPut):
|
||||
"""更新用户"""
|
||||
from core.security import get_password_hash
|
||||
|
||||
roles = data.rids
|
||||
del data.rids
|
||||
for role in roles:
|
||||
if await get_role({"id": role.rid, "status__not": 9}) is None:
|
||||
return role.rid
|
||||
|
||||
# 更新用户
|
||||
data.password = get_password_hash(data.password)
|
||||
await UserModel.filter(id=uid).update(**data.dict())
|
||||
|
||||
# todo 1. 先前有的角色,这次更新成没有 2. 先前没有的角色 这次更新成有, 3. 只更新了状态
|
||||
|
||||
db = connections.get("default")
|
||||
# 1. 先把所有数据做删除
|
||||
await db.execute_query_dict(
|
||||
"""
|
||||
update sys_user_role set status = 9 where uid = (?)
|
||||
""",
|
||||
[uid],
|
||||
)
|
||||
# 2. 新增次此更新的数据
|
||||
await UserRoleModel.bulk_create(
|
||||
[UserRoleModel(uid=uid, **role.dict()) for role in roles]
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
from core.events import close_orm, init_orm
|
||||
from core.log import logger_db_client as logger
|
||||
from core.log import logger
|
||||
from core.middleware import middlewares
|
||||
from router.url import routes
|
||||
|
||||
|
BIN
backend/mini.db
BIN
backend/mini.db
Binary file not shown.
@ -1,11 +1,20 @@
|
||||
from typing import Any, Callable, get_type_hints
|
||||
|
||||
from fastapi import routing
|
||||
from fastapi import Depends, routing
|
||||
|
||||
from controller.common import login
|
||||
from controller.menu import menu_add
|
||||
from controller.role import role_add, role_has_menu
|
||||
from controller.user import create_user, user_arr, user_info, user_list
|
||||
from controller.common import about, login
|
||||
from controller.menu import menu_add, menu_arr, menu_del
|
||||
from controller.role import (
|
||||
assigned_menu,
|
||||
role_add,
|
||||
role_arr,
|
||||
role_del,
|
||||
role_has_menu,
|
||||
role_put,
|
||||
role_query,
|
||||
)
|
||||
from controller.user import user_add, user_arr, user_del, user_info, user_list, user_put
|
||||
from core.security import check_token
|
||||
|
||||
|
||||
class Route(routing.APIRoute):
|
||||
@ -106,18 +115,47 @@ class Route(routing.APIRoute):
|
||||
|
||||
routes = [
|
||||
Route.post("/login", endpoint=login, tags=["公共"], summary="登录"),
|
||||
Route.get("/about", endpoint=about, tags=["公共"], summary="关于"),
|
||||
# 用户管理
|
||||
Route.post("/user", endpoint=create_user, tags=["用户管理"], summary="用户新增"),
|
||||
Route.get("/user/{pk}", endpoint=user_info, tags=["用户管理"], summary="用户信息"),
|
||||
Route.get("/user", endpoint=user_arr, tags=["用户管理"], summary="用户列表"),
|
||||
Route.post("/user", endpoint=user_add, tags=["用户管理"], summary="用户新增"),
|
||||
Route.delete(
|
||||
"/user/{pk}",
|
||||
endpoint=user_del,
|
||||
tags=["用户管理"],
|
||||
summary="用户删除",
|
||||
),
|
||||
Route.put("/user/{pk}", endpoint=user_put, tags=["用户管理"], summary="用户更新"),
|
||||
Route.get("/user/{pk}", endpoint=user_info, tags=["用户管理"], summary="用户信息"),
|
||||
Route.post("/user/query", endpoint=user_list, tags=["用户管理"], summary="用户列表查询"),
|
||||
# 角色管理
|
||||
# 角色管理,
|
||||
Route.get("/role", endpoint=role_arr, tags=["角色管理"], summary="角色列表"),
|
||||
Route.post("/role", endpoint=role_add, tags=["角色管理"], summary="角色新增"),
|
||||
Route.delete(
|
||||
"/role/{pk}",
|
||||
endpoint=role_del,
|
||||
tags=["角色管理"],
|
||||
summary="角色删除",
|
||||
dependencies=[Depends(check_token)],
|
||||
),
|
||||
Route.get(
|
||||
"role/{rid}/menu", endpoint=role_has_menu, tags=["角色管理"], summary="查询角色拥有权限"
|
||||
"/role/{rid}/menu", endpoint=role_has_menu, tags=["角色管理"], summary="查询角色拥有权限"
|
||||
),
|
||||
Route.put("/role", endpoint=role_put, tags=["角色管理"], summary="角色更新"),
|
||||
Route.post("/role/query", endpoint=role_query, tags=["角色管理"], summary="角色条件查询"),
|
||||
Route.post(
|
||||
"/role/assigned/menu", endpoint=assigned_menu, tags=["角色管理"], summary="角色分配菜单"
|
||||
),
|
||||
# 菜单新增
|
||||
Route.get("/menu", endpoint=menu_arr, tags=["菜单管理"], summary="菜单列表"),
|
||||
Route.post("/menu", endpoint=menu_add, tags=["菜单管理"], summary="菜单新增"),
|
||||
Route.delete(
|
||||
"/menu/{pk}",
|
||||
endpoint=menu_del,
|
||||
tags=["菜单管理"],
|
||||
summary="菜单删除",
|
||||
dependencies=[Depends(check_token)],
|
||||
),
|
||||
]
|
||||
|
||||
__all__ = [routes]
|
||||
|
@ -1,32 +1,26 @@
|
||||
from tortoise.contrib.pydantic import pydantic_model_creator
|
||||
from typing import Optional
|
||||
|
||||
from models import MenuModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
MenuRead = pydantic_model_creator(MenuModel, name="MenuOut")
|
||||
MenuIn = pydantic_model_creator(MenuModel, name="MenuIn", exclude_readonly=True)
|
||||
from schemas.common import ReadBase
|
||||
|
||||
|
||||
# from pydantic import BaseModel, Field
|
||||
# from typing import Optional
|
||||
# from core import ReadBase
|
||||
#
|
||||
#
|
||||
# class MenuBasic(BaseModel):
|
||||
# name: str
|
||||
# meta: Optional[str] = Field(default=None, description="元信息")
|
||||
# path: Optional[str] = Field(default=None, description="前端路由地址")
|
||||
# type: int = Field(description="0 目录 1 组件 2 按钮")
|
||||
# component: Optional[str] = Field(default=None, description="前端组件地址")
|
||||
# pid: int = Field(default=0, description="0 表示没有根节点")
|
||||
# identifier: Optional[str] = Field(default=None, description="权限标识符 -> 按钮显示")
|
||||
# api: Optional[str] = Field(default=None, description="后端接口地址")
|
||||
# method: Optional[str] = Field(default=None, description="接口请求方法")
|
||||
# regx: Optional[str] = Field(default=None, description="正则匹配")
|
||||
#
|
||||
#
|
||||
# class MenuIn(MenuBasic):
|
||||
# pass
|
||||
#
|
||||
#
|
||||
# class MenuRead(MenuBasic, ReadBase):
|
||||
# pass
|
||||
class MenuBasic(BaseModel):
|
||||
name: str
|
||||
meta: dict = Field(default=None, description="元信息")
|
||||
path: Optional[str] = Field(default=None, description="前端路由地址")
|
||||
type: int = Field(description="0 目录 1 组件 2 按钮")
|
||||
component: Optional[str] = Field(default=None, description="前端组件地址")
|
||||
pid: int = Field(default=0, description="0 表示没有根节点")
|
||||
identifier: Optional[str] = Field(default=None, description="权限标识符 -> 按钮显示")
|
||||
api: Optional[str] = Field(default=None, description="后端接口地址")
|
||||
method: Optional[str] = Field(default=None, description="接口请求方法")
|
||||
regx: Optional[str] = Field(default=None, description="正则匹配")
|
||||
|
||||
|
||||
class MenuIn(MenuBasic):
|
||||
pass
|
||||
|
||||
|
||||
class MenuRead(MenuBasic, ReadBase):
|
||||
pass
|
||||
|
@ -1,10 +1,17 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from tortoise.contrib.pydantic import pydantic_model_creator
|
||||
|
||||
from models import RoleModel
|
||||
from schemas.common import ReadBase
|
||||
from schemas.common import QueryData, ReadBase
|
||||
|
||||
RoleRed = pydantic_model_creator(RoleModel, name="RoleOut")
|
||||
|
||||
class RoleMenuIn(BaseModel):
|
||||
"""角色 -分配菜单id"""
|
||||
|
||||
rid: int = Field(description="角色ID")
|
||||
menus: list[int] = Field(description="菜单ID 列表")
|
||||
|
||||
|
||||
class RoleMenuRead(RoleMenuIn, ReadBase):
|
||||
pass
|
||||
|
||||
|
||||
class RoleBasic(BaseModel):
|
||||
@ -16,9 +23,15 @@ class RoleIn(RoleBasic):
|
||||
pass
|
||||
|
||||
|
||||
class RoleRed(RoleBasic, ReadBase):
|
||||
class RoleRead(RoleBasic, ReadBase):
|
||||
pass
|
||||
|
||||
|
||||
class RoleInfo(RoleRed):
|
||||
class RoleInfo(RoleRead):
|
||||
pass
|
||||
|
||||
|
||||
class RoleQuery(QueryData):
|
||||
"""查询模型"""
|
||||
|
||||
name: str = Field("", description="角色名")
|
||||
|
@ -1,14 +1,19 @@
|
||||
from typing import List, Optional
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from tortoise.contrib.pydantic import pydantic_model_creator
|
||||
|
||||
from models import UserRoleModel
|
||||
from schemas.common import QueryData
|
||||
from schemas.common import QueryData, ReadBase
|
||||
|
||||
UserRole = pydantic_model_creator(UserRoleModel, name="UserRole", exclude_readonly=True)
|
||||
|
||||
from schemas.common import ReadBase
|
||||
class UserRole(BaseModel):
|
||||
uid: int = Field(description="用户id")
|
||||
rid: int = Field(description="角色id")
|
||||
|
||||
|
||||
class UserRoleRead(UserRole, ReadBase):
|
||||
"""用户 角色 读取模型"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UserBasic(BaseModel):
|
||||
@ -24,18 +29,41 @@ class UserRead(UserBasic, ReadBase):
|
||||
pass
|
||||
|
||||
|
||||
class UserHasRole(BaseModel):
|
||||
"""用户拥有角色"""
|
||||
|
||||
id: int
|
||||
name: str
|
||||
status: int = Field(default=1, description="激活角色 5 正常 1 删除 9")
|
||||
|
||||
|
||||
class UserInfo(UserRead):
|
||||
active_rid: int = Field(..., description="用户当前激活角色")
|
||||
rids: List[int] = Field(..., description="用户拥有角色")
|
||||
"""用户信息模型"""
|
||||
|
||||
roles: list[UserHasRole] = Field(..., description="用户拥有角色")
|
||||
|
||||
|
||||
class RoleActive(BaseModel):
|
||||
rid: int = Field(description="角色id")
|
||||
status: int = Field(default=1, description="激活角色 5 正常 1 删除 9")
|
||||
|
||||
|
||||
class UserAdd(UserIn):
|
||||
rids: List[int] = Field(..., description="用户角色列表")
|
||||
"""新增用户模型"""
|
||||
|
||||
rids: list[RoleActive] = Field(..., description="选择角色列表")
|
||||
|
||||
|
||||
class UserQuery(QueryData):
|
||||
"""查询模型"""
|
||||
|
||||
username: Optional[str] = Field("", description="用户名")
|
||||
nickname: Optional[str] = Field("", description="姓名")
|
||||
|
||||
|
||||
UserList = List[UserRead]
|
||||
class UserPut(BaseModel):
|
||||
"""用户更新模型"""
|
||||
|
||||
nickname: str = Field(..., description="用户昵称")
|
||||
password: str = Field(..., description="密码")
|
||||
rids: list[RoleActive] = Field(..., description="选择角色列表")
|
||||
|
@ -1,51 +1,49 @@
|
||||
import json
|
||||
|
||||
import pytest
|
||||
import requests as client
|
||||
|
||||
from core.log import logger
|
||||
from schemas.menu import MenuIn
|
||||
from schemas.role import RoleIn
|
||||
from schemas.user import UserIn
|
||||
from schemas.role import RoleIn, RoleMenuIn
|
||||
from schemas.user import RoleActive, UserAdd
|
||||
|
||||
base = "http://localhost:8000"
|
||||
|
||||
|
||||
def test_user_add():
|
||||
url = base + "/user"
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=UserIn(username="admin", nickname="超级管理员", password="123456").dict(),
|
||||
)
|
||||
assert res.status_code == 200
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=UserIn(username="tester", nickname="测试员", password="123456").dict(),
|
||||
)
|
||||
assert res.status_code == 200
|
||||
|
||||
|
||||
def test_role_add():
|
||||
url = base + "/role"
|
||||
res = client.request(
|
||||
method="post", url=url, json=RoleIn(name="super", remark="全部权限").dict()
|
||||
)
|
||||
assert res.status_code == 200
|
||||
res = client.request(
|
||||
method="post", url=url, json=RoleIn(name="user", remark="用户权限").dict()
|
||||
)
|
||||
assert res.status_code == 200
|
||||
|
||||
|
||||
def test_menu_add():
|
||||
url = base + "/menu"
|
||||
# id 1
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
params = [
|
||||
# 创建角色
|
||||
("/role", RoleIn(name="super", remark="全部权限").dict()),
|
||||
("/role", RoleIn(name="user", remark="用户权限").dict()),
|
||||
# 创建用户
|
||||
(
|
||||
"/user",
|
||||
UserAdd(
|
||||
username="admin",
|
||||
nickname="管理员",
|
||||
password="123456",
|
||||
rids=[
|
||||
RoleActive(rid=1, status=5),
|
||||
RoleActive(rid=2),
|
||||
],
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/user",
|
||||
UserAdd(
|
||||
username="tester",
|
||||
nickname="测试员",
|
||||
password="123456",
|
||||
rids=[
|
||||
RoleActive(rid=2, status=5),
|
||||
],
|
||||
).dict(),
|
||||
),
|
||||
# 创建菜单
|
||||
# 目录
|
||||
(
|
||||
"/menu",
|
||||
MenuIn( # id 1
|
||||
name="系统管理",
|
||||
meta=json.dumps({"icon": "Group"}),
|
||||
meta={"icon": "Group"},
|
||||
path="/system",
|
||||
type=0,
|
||||
component=None,
|
||||
@ -55,16 +53,28 @@ def test_menu_add():
|
||||
method=None,
|
||||
regx=None,
|
||||
).dict(),
|
||||
)
|
||||
|
||||
assert res.status_code == 200
|
||||
# id 2
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn( # id 2
|
||||
name="系统设置",
|
||||
meta={"icon": "setting"},
|
||||
path="/system",
|
||||
type=0,
|
||||
component=None,
|
||||
pid=0,
|
||||
identifier=None,
|
||||
api=None,
|
||||
method=None,
|
||||
regx=None,
|
||||
).dict(),
|
||||
),
|
||||
# 组件
|
||||
(
|
||||
"/menu",
|
||||
MenuIn( # id 3
|
||||
name="用户管理",
|
||||
meta=json.dumps({"icon": "User"}),
|
||||
meta={"icon": "User"},
|
||||
path="/system/user",
|
||||
type=1,
|
||||
component="/system/user.vue",
|
||||
@ -74,15 +84,12 @@ def test_menu_add():
|
||||
method="{'GET'}",
|
||||
regx="^/user$",
|
||||
).dict(),
|
||||
)
|
||||
assert res.status_code == 200
|
||||
# id 3
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn( # id 4
|
||||
name="角色管理",
|
||||
meta=json.dumps({"icon": "User"}),
|
||||
meta={"icon": "Role"},
|
||||
path="/system/role",
|
||||
type=1,
|
||||
component="/system/role.vue",
|
||||
@ -92,15 +99,12 @@ def test_menu_add():
|
||||
method="{'GET'}",
|
||||
regx="^/role$",
|
||||
).dict(),
|
||||
)
|
||||
|
||||
# id 4
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn( # id 5
|
||||
name="菜单管理",
|
||||
meta=json.dumps({"icon": "Menu"}),
|
||||
meta={"icon": "Menu"},
|
||||
path="/system/menu",
|
||||
type=1,
|
||||
component="/system/menu.vue",
|
||||
@ -110,92 +114,231 @@ def test_menu_add():
|
||||
method="{'GET'}",
|
||||
regx="^/menu$",
|
||||
).dict(),
|
||||
)
|
||||
|
||||
# id 5
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
name="系统设置",
|
||||
meta=json.dumps({"icon": "Setting"}),
|
||||
path="/setting",
|
||||
type=0,
|
||||
component=None,
|
||||
pid=0,
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn( # id 6
|
||||
name="关于",
|
||||
meta={"icon": "Menu"},
|
||||
path="/setting/about",
|
||||
type=1,
|
||||
component="/setting/about.vue",
|
||||
pid=2,
|
||||
identifier=None,
|
||||
api=None,
|
||||
method=None,
|
||||
regx=None,
|
||||
api="/about",
|
||||
method="{'GET'}",
|
||||
regx="^/about",
|
||||
).dict(),
|
||||
)
|
||||
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
name="系统监控",
|
||||
meta=json.dumps({"icon": "minitor"}),
|
||||
path="/setting/minitor",
|
||||
type=0,
|
||||
component="/setting/minitor.vue",
|
||||
pid=5,
|
||||
identifier=None,
|
||||
api=None,
|
||||
method=None,
|
||||
regx=None,
|
||||
).dict(),
|
||||
)
|
||||
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
name="新增用户",
|
||||
meta=json.dumps({"icon": "Add"}),
|
||||
),
|
||||
# 按钮
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="用户新增",
|
||||
meta={"icon": "Add"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=2,
|
||||
identifier="user:add",
|
||||
pid=3,
|
||||
identifier="user:create",
|
||||
api="/user",
|
||||
method="{'POST'}",
|
||||
regx="^/user$",
|
||||
).dict(),
|
||||
)
|
||||
assert res.status_code == 200
|
||||
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
name="查询用户",
|
||||
meta=json.dumps({"icon": "Select"}),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="用户删除",
|
||||
meta={"icon": "Delete"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=2,
|
||||
pid=3,
|
||||
identifier="user:delete",
|
||||
api="/user/{pk}",
|
||||
method="{'DELETE'}",
|
||||
regx="^/user/(?P<pk>[^/]+)$",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="用户更新",
|
||||
meta={"icon": "Update"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=3,
|
||||
identifier="user:update",
|
||||
api="/user/{pk}",
|
||||
method="{'PUT'}",
|
||||
regx="^/user/(?P<pk>[^/]+)$",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="用户详情",
|
||||
meta={"icon": "Info"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=3,
|
||||
identifier="user:get",
|
||||
api="/user/{pk}",
|
||||
method="{'GET'}",
|
||||
regx="^/user/(?P<pk>[^/]+)$",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="用户查询",
|
||||
meta={"icon": "Search"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=3,
|
||||
identifier="user:query",
|
||||
api="/user/query",
|
||||
method="{'POST'}",
|
||||
regx="^/user/query$",
|
||||
).dict(),
|
||||
)
|
||||
|
||||
res = client.request(
|
||||
method="post",
|
||||
url=url,
|
||||
json=MenuIn(
|
||||
name="角色管理",
|
||||
meta=json.dumps({"icon": "User"}),
|
||||
path="/system/role",
|
||||
type=1,
|
||||
component="/system/role.vue",
|
||||
pid=1,
|
||||
identifier=None,
|
||||
),
|
||||
# 角色管理
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="角色新增",
|
||||
meta={"icon": "Add"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=4,
|
||||
identifier="role:create",
|
||||
api="/role",
|
||||
method="{'GET'}",
|
||||
regx="^/role",
|
||||
method="{'POST'}",
|
||||
regx="^/role$",
|
||||
).dict(),
|
||||
)
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="角色删除",
|
||||
meta={"icon": "Delete"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=4,
|
||||
identifier="role:delete",
|
||||
api="/role/{pk}",
|
||||
method="{'DELETE'}",
|
||||
regx="^/role/(?P<pk>[^/]+)$",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="查询角色拥有权限",
|
||||
meta={"icon": "Delete"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=4,
|
||||
identifier=None,
|
||||
api="/role/{rid}/menu",
|
||||
method="{'GET'}",
|
||||
regx="^/role/(?P<rid>[^/]+)/menu$",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="查询角色",
|
||||
meta={"icon": "Search"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=4,
|
||||
identifier="",
|
||||
api="/role/query",
|
||||
method="{'POST'}",
|
||||
regx="^/role/query$",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="分配权限",
|
||||
meta={"icon": "Delete"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=4,
|
||||
identifier="role:assign",
|
||||
api="/role/assigned/menu",
|
||||
method="{'POST'}",
|
||||
regx="^/role/assigned/menu$",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="更新角色",
|
||||
meta={"icon": "Update"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=4,
|
||||
identifier="role:update",
|
||||
api="/role",
|
||||
method="{'PUT'}",
|
||||
regx="^/role$",
|
||||
).dict(),
|
||||
),
|
||||
# 菜单管理的权限
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="新增菜单",
|
||||
meta={"icon": "Update"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=5,
|
||||
identifier="menu:create",
|
||||
api="/menu",
|
||||
method="{'POST'}",
|
||||
regx="^/menu$",
|
||||
).dict(),
|
||||
),
|
||||
(
|
||||
"/menu",
|
||||
MenuIn(
|
||||
name="删除菜单",
|
||||
meta={"icon": "Delete"},
|
||||
path=None,
|
||||
type=2,
|
||||
component=None,
|
||||
pid=5,
|
||||
identifier="menu:delete",
|
||||
api="/menu/{pk}",
|
||||
method="{'DELETE'}",
|
||||
regx="/menu/(?P<pk>[^/]+)$",
|
||||
).dict(),
|
||||
),
|
||||
# 分配权限
|
||||
(
|
||||
"/role/assigned/menu",
|
||||
RoleMenuIn(rid=1, menus=[num for num in range(1, 20)]).dict(),
|
||||
),
|
||||
("/role/assigned/menu", RoleMenuIn(rid=2, menus=[3, 7, 8, 9, 10, 11]).dict()),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path, data", params)
|
||||
def test_add_data(path, data):
|
||||
res = client.post(url=base + path, json=data)
|
||||
logger.info(res.json())
|
||||
assert res.status_code == 200
|
||||
|
Loading…
Reference in New Issue
Block a user