feat: user api

This commit is contained in:
zy7y 2022-09-12 15:11:12 +08:00
parent 28013b0e8e
commit 7db1277dd9
30 changed files with 513 additions and 304 deletions

2
backend/.gitignore vendored
View File

@ -140,3 +140,5 @@ cython_debug/
.idea
.pdm.toml
__pypackages__/
.pytest_cache
.db-*

View File

@ -1,16 +1,16 @@
from core.resp import Response
from core.router import Router
from core import Response
from core.security import generate_token, verify_password
from dbhelper.user import get_user
from schemas.common import LoginForm, LoginResult
common = Router(tags=["公共接口"])
@common.post("/login", summary="登录")
async def login(auth_data: LoginForm) -> Response[LoginResult]:
user_obj = await get_user({"username": auth_data.username})
if user_obj:
if verify_password(auth_data.password, user_obj.password):
return Response(data=LoginResult(id=user_obj.id, token=generate_token(auth_data.username)))
return Response(
data=LoginResult(
id=user_obj.id, token=generate_token(auth_data.username)
)
)
return Response(msg="账号或密码错误")

View File

@ -1,31 +1,7 @@
from core.resp import Response
from core.router import Router
from schemas.common import QueryData
from core import Response
from dbhelper.menu import insert_menu
from schemas.menu import MenuIn, MenuRead
menu = Router(prefix="/menu", tags=["菜单管理"])
@menu.post("", summary="菜单添加")
async def menu_add(data: MenuIn) -> Response[MenuRead]:
pass
@menu.get("/{pk}", summary="菜单详情")
async def menu_info(pk: int) -> Response[MenuRead]:
pass
@menu.delete("/{pk}", summary="删除菜单")
async def menu_del(pk: int) -> Response:
pass
@menu.put("/{pk}", summary="编辑菜单")
async def menu_put(pk: int, data: MenuIn) -> Response[MenuRead]:
pass
@menu.post("/list", summary="查询菜单列表")
async def menu_list(data: QueryData) -> Response[list[MenuRead]]:
pass
return Response(data=await insert_menu(data))

View File

@ -1,53 +1,20 @@
import json
from core.resp import Response
from core.router import Router
from core import Response
from core.utils import list_to_tree
from dbhelper.role import get_role_menus
from schemas.common import QueryData
from schemas.role import RoleAdd, RoleInfo
role = Router(prefix="/role", tags=["角色管理"])
from dbhelper.role import get_role_menus, new_role
from schemas.role import RoleIn, RoleInfo
@role.post("", summary="角色添加")
async def role_add(data: RoleAdd) -> Response[RoleInfo]:
pass
async def role_add(data: RoleIn) -> Response[RoleInfo]:
return Response(data=await new_role(data))
@role.get("/{pk}", summary="角色详情")
async def role_info(pk: int) -> Response[RoleInfo]:
pass
@role.delete("/{pk}", summary="删除角色")
async def role_del(pk: int) -> Response:
pass
@role.put("/{pk}", summary="编辑角色")
async def role_put(pk: int, data: RoleAdd) -> Response[RoleInfo]:
pass
@role.post("/list", summary="查询角色列表")
async def role_list(data: QueryData) -> Response[list[RoleInfo]]:
pass
@role.get("/{pk}/menu", summary="查询角色菜单权限")
async def role_menu(pk: int):
menus = await get_role_menus(pk)
async def role_has_menu(rid: int):
"""
rid: 角色ID
"""
menus = await get_role_menus(rid)
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))
@role.get("/{pk}/menuIds", summary="查询角色菜单ids")
async def role_menus_id():
pass
@role.get("/assign", summary="分配权限")
async def role_assign():
pass

View File

@ -1,22 +1,29 @@
from core.resp import Response
from core.router import Router
from fastapi import Query
from core import Response
from core.security import get_password_hash
from dbhelper.user import get_user_info, get_users, insert_user
from dbhelper.user import get_user, get_user_info, get_users, insert_user, new_user
from schemas.common import ListAll
from schemas.user import UserAdd, UserInfo, UserList, UserQuery
user = Router(prefix="/user", tags=["用户管理"])
from schemas.user import UserAdd, UserIn, UserInfo, UserList, UserQuery, UserRead
@user.post("", summary="用户添加")
async def user_add(data: UserAdd) -> Response[UserInfo]:
"""新增用户并分配角色 一步到位"""
roles = data.rids
del data.rids
user.password = get_password_hash(user.password)
data.password = get_password_hash(data.password)
return await insert_user(data, roles)
@user.get("/{pk}", summary="用户详情")
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="用户名已存在")
async def user_info(pk: int) -> Response[UserInfo]:
try:
return Response(data=await get_user_info(pk))
@ -24,18 +31,17 @@ async def user_info(pk: int) -> Response[UserInfo]:
return Response(msg=f"用户不存在 {e}")
@user.delete("/{pk}", summary="删除用户")
async def user_del(pk: int) -> Response:
pass
async def user_arr(
offset: int = Query(default=1, description="偏移量-页码"),
limit: int = Query(default=10, description="数据量"),
) -> Response[ListAll[UserList]]:
skip = (offset - 1) * limit
users, count = await get_users(skip, limit)
return Response(data=ListAll(total=count, items=users))
@user.put("/{pk}", summary="编辑用户")
async def user_put(pk: int, data: UserAdd) -> Response[UserInfo]:
pass
@user.post("/list", summary="查询用户列表")
async def user_list(query: UserQuery) -> Response[ListAll[UserList]]:
"""post查询用户列表"""
limit = query.size
skip = (query.offset - 1) * limit
del query.offset, query.size

View File

@ -0,0 +1,125 @@
from typing import Generic, Optional, TypeVar
from pydantic import BaseModel, Field
from pydantic.generics import GenericModel
T = TypeVar("T")
class Response(GenericModel, Generic[T]):
code: int = 200
data: Optional[T]
msg: str = "请求成功"
from datetime import datetime
class ReadBase(BaseModel):
"""数据读取的基类"""
id: int
status: int = Field(default=1, description="数据状态 1正常默认值 9 删除 5使用中 ")
created: datetime
modified: datetime
from typing import Any, Callable, get_type_hints
from fastapi import routing
class Route(routing.APIRoute):
"""
https://github.com/tiangolo/fastapi/issues/620
Django挂载视图方法
def index() -> User:
pass
Route("/", endpoint=index)
"""
def __init__(
self,
path: str,
endpoint: Callable[..., Any],
tags: list[str],
summary: str,
**kwargs: Any
):
if kwargs.get("response_model") is None:
kwargs["response_model"] = get_type_hints(endpoint).get("return")
super(Route, self).__init__(
path=path, endpoint=endpoint, tags=tags, summary=summary, **kwargs
)
@classmethod
def post(
cls,
path: str,
endpoint: Callable[..., Any],
tags: list[str],
summary: str,
**kwargs: Any
):
return Route(
path=path,
endpoint=endpoint,
methods=["POST"],
tags=tags,
summary=summary,
**kwargs
)
@classmethod
def get(
cls,
path: str,
endpoint: Callable[..., Any],
tags: list[str],
summary: str,
**kwargs: Any
):
return Route(
path=path,
endpoint=endpoint,
methods=["GET"],
tags=tags,
summary=summary,
**kwargs
)
@classmethod
def delete(
cls,
path: str,
endpoint: Callable[..., Any],
tags: list[str],
summary: str,
**kwargs: Any
):
return Route(
path=path,
endpoint=endpoint,
methods=["DELETE"],
tags=tags,
summary=summary,
**kwargs
)
@classmethod
def put(
cls,
path: str,
endpoint: Callable[..., Any],
tags: list[str],
summary: str,
**kwargs: Any
):
return Route(
path=path,
endpoint=endpoint,
methods=["PUT"],
tags=tags,
summary=summary,
**kwargs
)

View File

@ -1,35 +0,0 @@
import enum
class Status(enum.IntEnum):
"""
数据库状态枚举值
9 删除 5 无效 1 有效 3 使用
"""
DELETED: int = 9
INACTIVE: int = 5
ACTIVE: int = 1
SELECTED: int = 3
class UserType(enum.IntEnum):
"""
数据库超级管理员枚举
0 超级管理员 1用户
"""
ADMIN: int = 0
USER: int = 1
class MenuType(enum.IntEnum):
"""
菜单类型枚举
目录 0
组件 1 按钮 2
"""
DIR = 0
CPN = 1
BTN = 2

View File

@ -3,9 +3,7 @@ from tortoise import Tortoise
async def init_orm():
"""初始化orm"""
await Tortoise.init(
db_url="sqlite://mini.db", modules={"models": ["models"]},
)
await Tortoise.init(db_url="sqlite://mini.db", modules={"models": ["models"]})
await Tortoise.generate_schemas()

View File

@ -1,32 +0,0 @@
import enum
from typing import Generic, Optional, TypeVar, Union
from pydantic.generics import GenericModel
T = TypeVar("T")
class Status(enum.IntEnum):
OK = 200
CREATED = 201
ACCEPTED = 202
NO_CONTENT = 204
BAD_REQUEST = 400
UNAUTHORIZED = 401
FORBIDDEN = 403
NOT_FOUND = 404
INTERNAL_SERVER_ERROR = 500
NOT_IMPLEMENTED = 501
BAD_GATEWAY = 502
SERVICE_UNAVAILABLE = 503
class Msg(enum.Enum):
OK = "OK"
FAIL = "FAIL"
class Response(GenericModel, Generic[T]):
code: Status = Status.OK
data: Optional[T]
msg: Union[Msg, str] = Msg.OK

View File

@ -1,48 +0,0 @@
from typing import TYPE_CHECKING, Any, Callable, get_type_hints
from fastapi import APIRouter
from fastapi.routing import APIRoute
"""
根据类型标注自动返回模型
https://github.com/tiangolo/fastapi/issues/620
"""
class Router(APIRouter):
"""
装饰器用法
@app.get("/")
def index() -> User:
"""
if not TYPE_CHECKING:
def add_api_route(
self, path: str, endpoint: Callable[..., Any], **kwargs: Any
) -> None:
if kwargs.get("response_model") is None:
kwargs["response_model"] = get_type_hints(endpoint).get("return")
return super().add_api_route(path, endpoint, **kwargs)
else: # pragma: no cover
pass
class Route(APIRoute):
"""
Django挂载视图方法
def index() -> User:
pass
Route("/", endpoint=index)
"""
if not TYPE_CHECKING:
def __init__(self, path: str, endpoint: Callable[..., Any], **kwargs: Any):
if kwargs.get("response_model") is None:
kwargs["response_model"] = get_type_hints(endpoint).get("return")
super(Route, self).__init__(path=path, endpoint=endpoint, **kwargs)
else: # pragma: no cover
pass

View File

@ -14,7 +14,7 @@ SECRET_KEY = "lLNiBWPGiEmCLLR9kRGidgLY7Ac1rpSWwfGzTJpTmCU"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60 *24 * 7
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 7
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
bearer = HTTPBearer()
@ -45,13 +45,9 @@ def generate_token(username: str, expires_delta: Optional[timedelta] = None):
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(
minutes=ACCESS_TOKEN_EXPIRE_MINUTES
)
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update(dict(exp=expire))
encoded_jwt = jwt.encode(
to_encode, SECRET_KEY, algorithm=ALGORITHM
)
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
@ -59,9 +55,7 @@ async def check_token(security: HTTPAuthorizationCredentials = Depends(bearer)):
"""检查用户token"""
token = security.credentials
try:
payload = jwt.decode(
token, SECRET_KEY, algorithms=[ALGORITHM]
)
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
return await get_user({"username": username})
except JWTError:

View File

@ -1,7 +1,5 @@
from tortoise import fields, models
from core.enums import Status
class Table(models.Model):
"""
@ -9,7 +7,7 @@ class Table(models.Model):
"""
id = fields.IntField(pk=True, description="主键")
status = fields.IntEnumField(Status, description="状态", default=Status.ACTIVE)
status = fields.SmallIntField(default=1, description="状态 1有效 9 删除 5选中")
created = fields.DatetimeField(auto_now_add=True, description="创建时间", null=True)
modified = fields.DatetimeField(auto_now=True, description="更新时间", null=True)

View File

@ -24,28 +24,3 @@ def list_to_tree(
else:
arr.append(menu)
return arr
def menu_table():
"""生成菜单表数据"""
from models import MenuModel
MenuModel.bulk_create([
MenuModel(name="系统管理",
meta={"icon": "Grid"},
path="/system",
type=0),
MenuModel(name="系统设置",
meta={"icon": "Setting"},
path="/setting",
type=0),
MenuModel(name="菜单管理",
meta={"icon": "Menu"},
path="/system/menu",
type=1,
component="/system/menu",
pid=1,
api="/menu",
method="{'GET}",
regx="^/menu$"
)
])

7
backend/dbhelper/menu.py Normal file
View File

@ -0,0 +1,7 @@
from models import MenuModel
from schemas.menu import MenuIn
async def insert_menu(menu: MenuIn):
"""新增菜单"""
return await MenuModel.create(**menu.dict())

View File

@ -1,5 +1,8 @@
from tortoise import connections
from models import RoleModel
from schemas.role import RoleIn
async def get_role_menus(rid: int):
"""
@ -13,3 +16,8 @@ async def get_role_menus(rid: int):
AND sys_role_menu.rid = (%s) AND m.`status` = 1 ORDER BY m.sort""",
[rid],
)
async def new_role(role: RoleIn):
"""新增角色"""
return await RoleModel.create(**role.dict())

View File

@ -1,8 +1,7 @@
from tortoise.transactions import atomic
from core.enums import Status
from models import RoleModel, UserModel, UserRoleModel
from schemas.user import UserRole
from schemas.user import UserIn, UserRole
async def get_user(kwargs):
@ -32,13 +31,13 @@ async def get_user_info(pk: int):
active_rid = role[0].get("rid")
rids = []
for obj in role:
if obj.get("status") == Status.SELECTED:
if obj.get("status") == 5:
active_rid = obj.get("rid")
rids.append(obj.get("rid"))
return {**user, "active_rid": active_rid, "rids": rids}
async def get_users(skip: int, limit: int, kwargs: dict):
async def get_users(skip: int, limit: int, kwargs: dict = None):
"""
分页获取用户并且支持字段模糊查询
Args:
@ -49,7 +48,10 @@ async def get_users(skip: int, limit: int, kwargs: dict):
Returns:
"""
kwargs = {f"{k}__contains": v for k, v in kwargs.items()}
if kwargs is not None:
kwargs = {f"{k}__contains": v for k, v in kwargs.items()}
else:
kwargs = {}
result = (
UserModel.filter(status__not_in=[9, 5], **kwargs).all().order_by("-created")
)
@ -66,7 +68,12 @@ async def insert_user(user, roles):
user_role = UserRole(rid=rid, uid=obj.id)
if index == 0:
user_role.status = Status.SELECTED
user_role.status = 5
# 第一个角色默认, 添加到关系表
await UserRoleModel.create(**user_role.dict())
return user
async def new_user(user: UserIn):
"""新增用户"""
return await UserModel.create(**user.dict())

View File

@ -1,41 +1,23 @@
from fastapi import FastAPI
from fastapi.openapi.docs import get_swagger_ui_html
from controller import register_routers
from core.events import close_orm, init_orm
from core.log import logger_db_client
from core.utils import menu_table
from core.log import logger_db_client as logger
from core.middleware import middlewares
from router.url import routes
app = FastAPI(
on_startup=[init_orm, menu_table],
on_startup=[init_orm],
on_shutdown=[close_orm],
docs_url=None,
redoc_url=None,
routes=routes,
middleware=middlewares,
)
register_routers(app)
@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
return get_swagger_ui_html(
openapi_url=app.openapi_url,
title=app.title + " - Swagger UI",
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
swagger_js_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/4.10.3/swagger-ui-bundle.js",
swagger_css_url="https://cdn.bootcdn.net/ajax/libs/swagger-ui/4.10.3/swagger-ui.css",
)
for i in app.routes:
logger_db_client.debug(i.__dict__)
logger_db_client.info(f"{i.path}, {i.methods}, {i.path_regex}")
"""
'path_regex': re.compile('^/role/(?P<
pk>[^/]+)/menu$'), 'path_format': '/role/{pk}/menu',
"""
if __name__ == '__main__':
if __name__ == "__main__":
import uvicorn
for i in app.routes:
logger.info(
f"{i.path}, {i.methods}, {i.path_regex}, {i.__dict__.get('summary')}, {i.endpoint}"
)
uvicorn.run("main:app", reload=True)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,3 @@
from core.enums import MenuType
from core.table import Table, fields
@ -10,7 +9,7 @@ class MenuModel(Table):
name = fields.CharField(max_length=20, description="名称", null=True)
meta = fields.JSONField(description="元数据信息", null=True)
path = fields.CharField(max_length=128, description="菜单url", null=True)
type = fields.IntEnumField(MenuType, description="菜单类型")
type = fields.SmallIntField(description="菜单类型 0目录 1组件 2按钮")
component = fields.CharField(max_length=128, description="组件地址", null=True)
pid = fields.IntField(description="父id", null=True)
identifier = fields.CharField(max_length=30, description="权限标识 user:add", null=True)

View File

@ -1,4 +1,3 @@
from core.enums import UserType
from core.table import Table, fields
@ -16,4 +15,3 @@ class UserModel(Table):
table_description = "用户表"
# 索引
unique_together = ("username",)

View File

@ -1,23 +1,35 @@
aiosqlite==0.17.0
anyio==3.6.1
attrs==22.1.0
bcrypt==4.0.0
certifi==2022.6.15.1
charset-normalizer==2.1.1
click==8.1.3
colorama==0.4.5
ecdsa==0.18.0
fastapi==0.82.0
h11==0.13.0
idna==3.3
iniconfig==1.1.1
iso8601==1.0.2
packaging==21.3
passlib==1.7.4
pluggy==1.0.0
py==1.11.0
pyasn1==0.4.8
pydantic==1.10.2
pyparsing==3.0.9
pypika-tortoise==0.1.6
pytest==7.1.3
python-jose==3.3.0
pytz==2022.2.1
requests==2.28.1
rsa==4.9
six==1.16.0
sniffio==1.3.0
starlette==0.19.1
tomli==2.0.1
tortoise-orm==0.19.2
typing-extensions==4.3.0
urllib3==1.26.12
uvicorn==0.18.3

View File

23
backend/router/url.py Normal file
View File

@ -0,0 +1,23 @@
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 core import Route
routes = [
Route.post("/login", endpoint=login, 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/query", endpoint=user_list, tags=["用户管理"], summary="用户列表查询"),
# 角色管理
Route.post("/role", endpoint=role_add, tags=["角色管理"], summary="角色新增"),
Route.get(
"role/{rid}/menu", endpoint=role_has_menu, tags=["角色管理"], summary="查询角色拥有权限"
),
# 菜单新增
Route.post("/menu", endpoint=menu_add, tags=["菜单管理"], summary="菜单新增"),
]
__all__ = [routes]

View File

@ -4,3 +4,29 @@ from models import MenuModel
MenuRead = pydantic_model_creator(MenuModel, name="MenuOut")
MenuIn = pydantic_model_creator(MenuModel, name="MenuIn", exclude_readonly=True)
# 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

View File

@ -1,15 +1,23 @@
from pydantic import Field
from pydantic import BaseModel, Field
from tortoise.contrib.pydantic import pydantic_model_creator
from core import ReadBase
from models import RoleModel
RoleRed = pydantic_model_creator(RoleModel, name="RoleOut")
RoleIn = pydantic_model_creator(RoleModel, name="RoleIn", exclude_readonly=True)
class RoleAdd(RoleIn):
menus: list[int] = Field(..., description="菜单列表")
class RoleBasic(BaseModel):
name: str = Field(None, description="角色名称")
remark: str = Field(None, description="备注信息")
class RoleIn(RoleBasic):
pass
class RoleRed(RoleBasic, ReadBase):
pass
class RoleInfo(RoleRed):

View File

@ -1,16 +1,28 @@
from typing import List, Optional
from pydantic import Field
from pydantic import BaseModel, Field
from tortoise.contrib.pydantic import pydantic_model_creator
from models import UserModel, UserRoleModel
from models import UserRoleModel
from schemas.common import QueryData
UserRead = pydantic_model_creator(UserModel, name="UserOut", exclude=("password",))
UserIn = pydantic_model_creator(UserModel, name="UserIn", exclude_readonly=True, exclude=("status",))
UserRole = pydantic_model_creator(UserRoleModel, name="UserRole", exclude_readonly=True)
from core import ReadBase
class UserBasic(BaseModel):
username: str
nickname: str
class UserIn(UserBasic):
password: str
class UserRead(UserBasic, ReadBase):
pass
class UserInfo(UserRead):
active_rid: int = Field(..., description="用户当前激活角色")

View File

201
backend/tests/test_case.py Normal file
View File

@ -0,0 +1,201 @@
import json
import requests as client
from schemas.menu import MenuIn
from schemas.role import RoleIn
from schemas.user import UserIn
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(
name="系统管理",
meta=json.dumps({"icon": "Group"}),
path="/system",
type=0,
component=None,
pid=0,
identifier=None,
api=None,
method=None,
regx=None,
).dict(),
)
assert res.status_code == 200
# id 2
res = client.request(
method="post",
url=url,
json=MenuIn(
name="用户管理",
meta=json.dumps({"icon": "User"}),
path="/system/user",
type=1,
component="/system/user.vue",
pid=1,
identifier=None,
api="/user",
method="{'GET'}",
regx="^/user$",
).dict(),
)
assert res.status_code == 200
# id 3
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,
api="/role",
method="{'GET'}",
regx="^/role$",
).dict(),
)
# id 4
res = client.request(
method="post",
url=url,
json=MenuIn(
name="菜单管理",
meta=json.dumps({"icon": "Menu"}),
path="/system/menu",
type=1,
component="/system/menu.vue",
pid=1,
identifier=None,
api="/menu",
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,
identifier=None,
api=None,
method=None,
regx=None,
).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"}),
path=None,
type=2,
component=None,
pid=2,
identifier="user:add",
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"}),
path=None,
type=2,
component=None,
pid=2,
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,
api="/role",
method="{'GET'}",
regx="^/role",
).dict(),
)
assert res.status_code == 200