Refactor backend MVC (#2)
* docs(requirements.txt):升级fastapi、uvicorn版本 * refactor(user):重构用户router、service * ref: role list api * doc: 1 * refactor(backend): mvc ref
This commit is contained in:
parent
60d07a477a
commit
547a4eeae6
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,36 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
|
|
||||||
from fastapi import APIRouter
|
|
||||||
from starlette.websockets import WebSocket
|
|
||||||
from websockets.exceptions import WebSocketException
|
|
||||||
|
|
||||||
from core.security import generate_token, verify_password
|
|
||||||
from core.utils import get_system_info
|
|
||||||
from dbhelper.user import get_user
|
|
||||||
from schemas import LoginForm, LoginResult, Response
|
|
||||||
|
|
||||||
router = APIRouter(tags=["公共"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/login", summary="登录", response_model=Response[LoginResult])
|
|
||||||
async def login(auth_data: LoginForm):
|
|
||||||
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(
|
|
||||||
data=LoginResult(
|
|
||||||
id=user_obj.id, token=generate_token(auth_data.username)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return Response(code=400, msg="账号或密码错误")
|
|
||||||
|
|
||||||
|
|
||||||
@router.websocket("/ws", name="系统信息")
|
|
||||||
async def websocket(ws: WebSocket):
|
|
||||||
await ws.accept()
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
await asyncio.sleep(1)
|
|
||||||
await ws.send_json(get_system_info())
|
|
||||||
except WebSocketException:
|
|
||||||
await ws.close()
|
|
@ -1,40 +0,0 @@
|
|||||||
# router service db router+service db
|
|
||||||
from fastapi import APIRouter
|
|
||||||
|
|
||||||
from core.utils import list_to_tree
|
|
||||||
from dbhelper.menu import del_menu, get_menu, get_tree_menu, insert_menu, put_menu
|
|
||||||
from schemas import MenuIn, MenuRead, Response
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/menu", tags=["菜单管理"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("", summary="菜单新增", response_model=Response[MenuRead])
|
|
||||||
async def menu_add(data: MenuIn):
|
|
||||||
return Response(data=await insert_menu(data))
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", summary="菜单列表", response_model=Response)
|
|
||||||
async def menu_arr():
|
|
||||||
menus = await get_tree_menu()
|
|
||||||
try:
|
|
||||||
data = list_to_tree(menus)
|
|
||||||
except KeyError:
|
|
||||||
return Response(code=400, msg="菜单根节点丢失")
|
|
||||||
return Response(data=data)
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{pk}", summary="菜单删除", response_model=Response)
|
|
||||||
async def menu_del(pk: int):
|
|
||||||
if await get_menu({"pid": pk}) is not None:
|
|
||||||
return Response(code=400, msg="请先删除子节点")
|
|
||||||
if await del_menu(pk) == 0:
|
|
||||||
return Response(code=400, msg="菜单不存在")
|
|
||||||
return Response()
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{pk}", summary="菜单更新", response_model=Response)
|
|
||||||
async def menu_put(pk: int, data: MenuIn):
|
|
||||||
"""更新菜单"""
|
|
||||||
if await put_menu(pk, data) == 0:
|
|
||||||
return Response(code=400, msg="菜单不存在")
|
|
||||||
return Response()
|
|
@ -1,78 +0,0 @@
|
|||||||
from fastapi import APIRouter, Query
|
|
||||||
|
|
||||||
from core.utils import list_to_tree
|
|
||||||
from dbhelper.menu import get_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, RoleQuery, RoleRead
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/role", tags=["角色管理"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("", summary="角色新增", response_model=Response[RoleInfo])
|
|
||||||
async def role_add(data: RoleIn):
|
|
||||||
if result := await new_role(data):
|
|
||||||
return Response(data=result)
|
|
||||||
return Response(code=400, msg="菜单不存在")
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{rid}/menu", summary="查询角色拥有权限", response_model=Response)
|
|
||||||
async def role_has_menu(rid: int):
|
|
||||||
"""
|
|
||||||
rid: 角色ID
|
|
||||||
"""
|
|
||||||
menus = await get_role_menus(rid)
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = list_to_tree(menus)
|
|
||||||
except KeyError:
|
|
||||||
return Response(code=400, msg="菜单缺少根节点.")
|
|
||||||
return Response(data=result)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", summary="角色列表", response_model=Response[ListAll[list[RoleRead]]])
|
|
||||||
async def role_arr(
|
|
||||||
offset: int = Query(default=1, description="偏移量-页码"),
|
|
||||||
limit: int = Query(default=10, description="数据量"),
|
|
||||||
):
|
|
||||||
skip = (offset - 1) * limit
|
|
||||||
roles, count = await get_roles(skip, limit)
|
|
||||||
return Response(data=ListAll(total=count, items=roles))
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{pk}", summary="角色删除", response_model=Response)
|
|
||||||
async def role_del(pk: int):
|
|
||||||
if await del_role(pk) == 0:
|
|
||||||
return Response(code=400, msg="角色不存在")
|
|
||||||
return Response()
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{pk}", summary="角色更新", response_model=Response)
|
|
||||||
async def role_put(pk: int, data: RoleIn):
|
|
||||||
"""更新角色"""
|
|
||||||
if await get_role({"id": pk}) is None:
|
|
||||||
|
|
||||||
return Response(code=400, msg="角色不存在")
|
|
||||||
# 如果不为ture -> 有菜单id不存在
|
|
||||||
if not all([await get_menu({"id": mid}) for mid in data.menus]):
|
|
||||||
return Response(code=400, msg="菜单不存在")
|
|
||||||
|
|
||||||
if await put_role(pk, data) == 0:
|
|
||||||
return Response(code=400, msg="角色不存在")
|
|
||||||
return Response()
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/query", summary="角色查询", response_model=Response[ListAll[list[RoleRead]]])
|
|
||||||
async def role_query(query: RoleQuery):
|
|
||||||
"""post条件查询角色表"""
|
|
||||||
size = query.limit
|
|
||||||
skip = (query.offset - 1) * size
|
|
||||||
del query.offset, query.limit
|
|
||||||
users, count = await get_roles(skip, size, query.dict())
|
|
||||||
return Response(data=ListAll(total=count, items=users))
|
|
@ -1,89 +0,0 @@
|
|||||||
from fastapi import APIRouter, Depends, Query
|
|
||||||
|
|
||||||
from core.security import check_permissions, get_password_hash
|
|
||||||
from dbhelper.user import (
|
|
||||||
del_user,
|
|
||||||
get_user,
|
|
||||||
get_user_info,
|
|
||||||
get_users,
|
|
||||||
insert_user,
|
|
||||||
put_user,
|
|
||||||
select_role,
|
|
||||||
)
|
|
||||||
from schemas import Response, UserAdd, UserInfo, UserPut, UserQuery, UserRead
|
|
||||||
from schemas.common import ListAll
|
|
||||||
|
|
||||||
router = APIRouter(prefix="/user", tags=["用户管理"])
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("", summary="用户新增", response_model=Response[UserRead])
|
|
||||||
async def user_add(data: UserAdd):
|
|
||||||
"""新增用户并分配角色 一步到位"""
|
|
||||||
if await get_user({"username": data.username}) is not None:
|
|
||||||
return Response(code=400, msg="用户名已存在")
|
|
||||||
rids = data.roles
|
|
||||||
del data.roles
|
|
||||||
data.password = get_password_hash(data.password)
|
|
||||||
result = await insert_user(data, rids)
|
|
||||||
if isinstance(result, int):
|
|
||||||
return Response(code=400, msg=f"角色{result}不存在")
|
|
||||||
return Response(data=result)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{pk}", summary="用户信息", response_model=Response[UserInfo])
|
|
||||||
async def user_info(pk: int):
|
|
||||||
"""获取用户信息"""
|
|
||||||
obj = await get_user({"id": pk})
|
|
||||||
if obj is None:
|
|
||||||
return Response(code=400, msg="用户不存在")
|
|
||||||
return Response(data=await get_user_info(obj))
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("", summary="用户列表", response_model=Response[ListAll[list[UserRead]]])
|
|
||||||
async def user_arr(
|
|
||||||
offset: int = Query(default=1, description="偏移量-页码"),
|
|
||||||
limit: int = Query(default=10, description="数据量"),
|
|
||||||
):
|
|
||||||
"""分页列表数据"""
|
|
||||||
skip = (offset - 1) * limit
|
|
||||||
users, count = await get_users(skip, limit)
|
|
||||||
return Response(data=ListAll(total=count, items=users))
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/query", summary="用户查询", response_model=Response[ListAll[list[UserRead]]])
|
|
||||||
async def user_list(query: UserQuery):
|
|
||||||
"""post查询用户列表"""
|
|
||||||
size = query.limit
|
|
||||||
skip = (query.offset - 1) * size
|
|
||||||
del query.offset, query.limit
|
|
||||||
users, count = await get_users(skip, size, query.dict())
|
|
||||||
return Response(data=ListAll(total=count, items=users))
|
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/{pk}", summary="用户删除", response_model=Response)
|
|
||||||
async def user_del(pk: int):
|
|
||||||
"""删除用户"""
|
|
||||||
if await del_user(pk) == 0:
|
|
||||||
return Response(code=400, msg="用户不存在")
|
|
||||||
return Response()
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/{pk}", summary="用户更新", response_model=Response)
|
|
||||||
async def user_put(pk: int, data: UserPut):
|
|
||||||
"""更新用户"""
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
@router.put("/role/{rid}", summary="用户切换角色", response_model=Response)
|
|
||||||
async def user_select_role(rid: int, user=Depends(check_permissions)):
|
|
||||||
"""用户切换角色"""
|
|
||||||
res = await select_role(user.id, rid)
|
|
||||||
if res == 0:
|
|
||||||
return Response(code=400, msg=f"角色不存在{res}")
|
|
||||||
return Response()
|
|
@ -1,14 +1,14 @@
|
|||||||
"""数据库通用查询方法"""
|
"""数据库通用查询方法"""
|
||||||
from typing import Optional
|
from tortoise import connections
|
||||||
|
|
||||||
from tortoise import connections, models
|
from models import MenuModel, RoleMenuModel, RoleModel, UserModel, UserRoleModel
|
||||||
|
|
||||||
|
|
||||||
class DbHelper:
|
class DbHelper:
|
||||||
def __init__(self, model: models.Model):
|
def __init__(self, model):
|
||||||
"""
|
"""
|
||||||
初始化
|
初始化
|
||||||
:param model: 模型类
|
:param model: 模型类 orm model
|
||||||
"""
|
"""
|
||||||
self.model = model
|
self.model = model
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class DbHelper:
|
|||||||
"""
|
"""
|
||||||
return self.model.filter(**kwargs)
|
return self.model.filter(**kwargs)
|
||||||
|
|
||||||
async def select(self, kwargs: dict = None) -> Optional[models.Model]:
|
async def select(self, kwargs: dict = None):
|
||||||
"""
|
"""
|
||||||
查询符合条件的第一个对象, 查无结果时返回None
|
查询符合条件的第一个对象, 查无结果时返回None
|
||||||
:param kwargs: kwargs: {"name:"7y", "id": 1}
|
:param kwargs: kwargs: {"name:"7y", "id": 1}
|
||||||
@ -58,7 +58,7 @@ class DbHelper:
|
|||||||
return await self.model.create(**data)
|
return await self.model.create(**data)
|
||||||
|
|
||||||
async def selects(
|
async def selects(
|
||||||
self, offset: int, limit: int, kwargs: dict = None, order_by: str = None
|
self, offset: int, limit: int, kwargs: dict = None, order_by: str = "-created"
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
条件分页查询数据列表, 支持排序
|
条件分页查询数据列表, 支持排序
|
||||||
@ -81,13 +81,13 @@ class DbHelper:
|
|||||||
items=await objs.offset(offset).limit(limit), total=await objs.count()
|
items=await objs.offset(offset).limit(limit), total=await objs.count()
|
||||||
)
|
)
|
||||||
|
|
||||||
async def inserts(self, objs: list[models.Model]):
|
async def inserts(self, objs: list):
|
||||||
"""
|
"""
|
||||||
批量新增数据
|
批量新增数据
|
||||||
:param objs: 模型列表
|
:param objs: 模型列表
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
await self.model.bulk_create(objs)
|
await self.model.bulk_create([self.model(**obj) for obj in objs])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def raw_sql(cls, sql: str, args: list = None):
|
async def raw_sql(cls, sql: str, args: list = None):
|
||||||
@ -101,3 +101,48 @@ class DbHelper:
|
|||||||
if args is None:
|
if args is None:
|
||||||
args = []
|
args = []
|
||||||
return await db.execute_query_dict(sql, args)
|
return await db.execute_query_dict(sql, args)
|
||||||
|
|
||||||
|
|
||||||
|
UserDao = DbHelper(UserModel)
|
||||||
|
RoleDao = DbHelper(RoleModel)
|
||||||
|
UserRoleDao = DbHelper(UserRoleModel)
|
||||||
|
MenuDao = DbHelper(MenuModel)
|
||||||
|
RoleMenuDao = DbHelper(RoleMenuModel)
|
||||||
|
|
||||||
|
|
||||||
|
async def has_roles(uid):
|
||||||
|
"""
|
||||||
|
获取用户角色信息,激活的角色升序
|
||||||
|
:param uid: 用户id
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
sql = """select r.id, r.name, ur.status from sys_role as r , sys_user_role as ur where r.id = ur.rid and
|
||||||
|
ur.uid = (?) and r.status = 1 and ur.status !=9 order by ur.status desc
|
||||||
|
"""
|
||||||
|
return await UserRoleDao.raw_sql(sql, [uid])
|
||||||
|
|
||||||
|
|
||||||
|
async def has_user(username):
|
||||||
|
"""
|
||||||
|
通过用户名检索数据是否存在
|
||||||
|
:param username:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return await UserDao.select({"username": username, "status__not": 9})
|
||||||
|
|
||||||
|
|
||||||
|
async def has_permissions(rid, is_menu=False):
|
||||||
|
"""
|
||||||
|
根据角色ID查到当前拥有的接口权限
|
||||||
|
:param rid: 角色ID
|
||||||
|
:param is_menu: 是否是菜单,默认不是 -》接口
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
filters = "m.api, m.method"
|
||||||
|
if is_menu:
|
||||||
|
filters = "m.id, m.name, m.icon, m.path, m.type, m.component, m.pid, m.identifier, m.api, m.method"
|
||||||
|
sql = f"""
|
||||||
|
select {filters}
|
||||||
|
FROM sys_menu as m, sys_role_menu as srm WHERE m.id = srm.mid
|
||||||
|
AND srm.rid = (?) and m.status != 9 order by m.id asc"""
|
||||||
|
return await RoleMenuDao.raw_sql(sql, [rid])
|
||||||
|
@ -1,15 +1 @@
|
|||||||
import logging
|
from loguru import logger
|
||||||
import sys
|
|
||||||
|
|
||||||
fmt = logging.Formatter(
|
|
||||||
fmt="%(asctime)s - %(name)s:%(lineno)d - %(levelname)s - %(message)s",
|
|
||||||
datefmt="%Y-%m-%d %H:%M:%S",
|
|
||||||
)
|
|
||||||
sh = logging.StreamHandler(sys.stdout)
|
|
||||||
sh.setLevel(logging.DEBUG)
|
|
||||||
sh.setFormatter(fmt)
|
|
||||||
|
|
||||||
# will print debug sql
|
|
||||||
logger = logging.getLogger("mini-rbac")
|
|
||||||
logger.setLevel(logging.DEBUG)
|
|
||||||
logger.addHandler(sh)
|
|
||||||
|
@ -1,12 +1,28 @@
|
|||||||
from fastapi.middleware import Middleware
|
from fastapi.middleware import Middleware
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from starlette.middleware.base import BaseHTTPMiddleware
|
||||||
|
|
||||||
|
from core.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
class CustomRequestLogMiddleware(BaseHTTPMiddleware):
|
||||||
|
async def dispatch(self, request, call_next):
|
||||||
|
logger.info(
|
||||||
|
f"Client: {request.client} Method: {request.method} "
|
||||||
|
f"Path: {request.url} Headers: {request.headers}"
|
||||||
|
)
|
||||||
|
# python-multipart == await request.form()
|
||||||
|
response = await call_next(request)
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
middlewares = [
|
middlewares = [
|
||||||
|
Middleware(CustomRequestLogMiddleware),
|
||||||
Middleware(
|
Middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"],
|
allow_origins=["*"],
|
||||||
allow_credentials=True,
|
allow_credentials=True,
|
||||||
allow_methods=["*"],
|
allow_methods=["*"],
|
||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
|
@ -6,10 +6,8 @@ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|||||||
from jose import JWTError, jwt
|
from jose import JWTError, jwt
|
||||||
from passlib.context import CryptContext
|
from passlib.context import CryptContext
|
||||||
|
|
||||||
|
from core.dbhelper import has_permissions, has_roles, has_user
|
||||||
from core.exceptions import PermissionsError, TokenAuthFailure
|
from core.exceptions import PermissionsError, TokenAuthFailure
|
||||||
from dbhelper.menu import get_apis
|
|
||||||
from dbhelper.user import get_user, get_user_info
|
|
||||||
from models import UserModel
|
|
||||||
|
|
||||||
# JWT
|
# JWT
|
||||||
SECRET_KEY = "lLNiBWPGiEmCLLR9kRGidgLY7Ac1rpSWwfGzTJpTmCU"
|
SECRET_KEY = "lLNiBWPGiEmCLLR9kRGidgLY7Ac1rpSWwfGzTJpTmCU"
|
||||||
@ -59,20 +57,20 @@ async def check_token(security: HTTPAuthorizationCredentials = Depends(bearer)):
|
|||||||
try:
|
try:
|
||||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||||
username: str = payload.get("sub")
|
username: str = payload.get("sub")
|
||||||
return await get_user({"username": username})
|
return await has_user(username)
|
||||||
except JWTError:
|
except JWTError:
|
||||||
raise TokenAuthFailure(403, "认证失败")
|
raise TokenAuthFailure(403, "认证失败")
|
||||||
|
|
||||||
|
|
||||||
async def check_permissions(request: Request, user: UserModel = Depends(check_token)):
|
async def check_permissions(request: Request, user=Depends(check_token)):
|
||||||
"""检查接口权限"""
|
"""检查接口权限"""
|
||||||
# 查询当前激活角色
|
# 查询当前激活角色
|
||||||
result = await get_user_info(user)
|
roles = await has_roles(user.id)
|
||||||
active_rid = result["roles"][0]["id"]
|
active_rid = roles[0]["id"]
|
||||||
|
|
||||||
# 白名单 登录用户信息, 登录用户菜单信息
|
# 白名单 登录用户信息, 登录用户菜单信息
|
||||||
whitelist = [(f"/user/{user.id}", "GET"), (f"/role/{active_rid}/menu", "GET")] + [
|
whitelist = [(f"/user/{user.id}", "GET"), (f"/role/{active_rid}/menu", "GET")] + [
|
||||||
(f"/user/role/{rid['id']}", "PUT") for rid in result["roles"]
|
(f"/user/role/{rid['id']}", "PUT") for rid in roles
|
||||||
]
|
]
|
||||||
|
|
||||||
if (request.url.path, request.method) in whitelist:
|
if (request.url.path, request.method) in whitelist:
|
||||||
@ -82,11 +80,11 @@ async def check_permissions(request: Request, user: UserModel = Depends(check_to
|
|||||||
for k, v in request.path_params.items():
|
for k, v in request.path_params.items():
|
||||||
api = api.replace(v, "{%s}" % k)
|
api = api.replace(v, "{%s}" % k)
|
||||||
|
|
||||||
# 2. 登录之后查一次 后面去结果查 todo 更新权限时需要更新 , 最好结果放redis
|
# todo 结果放redis
|
||||||
cache_key = f"{user.username}_{active_rid}"
|
cache_key = f"{user.username}_{active_rid}"
|
||||||
# 缓存到fastapi 应用实例中
|
# 缓存到fastapi 应用实例中
|
||||||
if not hasattr(request.app.state, cache_key):
|
if not hasattr(request.app.state, cache_key):
|
||||||
setattr(request.app.state, cache_key, await get_apis(active_rid))
|
setattr(request.app.state, cache_key, await has_permissions(active_rid))
|
||||||
if {"api": api, "method": request.method} not in getattr(
|
if {"api": api, "method": request.method} not in getattr(
|
||||||
request.app.state, cache_key
|
request.app.state, cache_key
|
||||||
):
|
):
|
||||||
|
63
backend/core/service.py
Normal file
63
backend/core/service.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
from core.dbhelper import DbHelper
|
||||||
|
|
||||||
|
|
||||||
|
class Service:
|
||||||
|
|
||||||
|
filter_del = {"status__not": 9}
|
||||||
|
|
||||||
|
def __init__(self, dao: DbHelper):
|
||||||
|
self.dao = dao
|
||||||
|
|
||||||
|
async def get_items(self, offset, limit):
|
||||||
|
"""
|
||||||
|
分页获取数据, 过滤掉删除
|
||||||
|
:param offset: 起始值
|
||||||
|
:param limit: 偏移量
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
skip = (offset - 1) * limit
|
||||||
|
return dict(data=await self.dao.selects(skip, limit, Service.filter_del))
|
||||||
|
|
||||||
|
async def query_items(self, query):
|
||||||
|
"""
|
||||||
|
根据条件查询结果
|
||||||
|
:param query:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
size = query.limit
|
||||||
|
skip = (query.offset - 1) * size
|
||||||
|
del query.offset, query.limit
|
||||||
|
filters = {f"{k}__contains": v for k, v in query.dict().items()}
|
||||||
|
filters.update(Service.filter_del)
|
||||||
|
return dict(data=await self.dao.selects(skip, size, filters))
|
||||||
|
|
||||||
|
async def delete_item(self, pk):
|
||||||
|
"""
|
||||||
|
逻辑删除数据
|
||||||
|
:param pk:主键
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
filters = {"id": pk}
|
||||||
|
filters.update(Service.filter_del)
|
||||||
|
if await self.dao.update(filters, {"status": 9}) == 0:
|
||||||
|
return dict(code=400, msg="数据不存在")
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
async def update_item(self, pk, data):
|
||||||
|
"""
|
||||||
|
更新数据,不通用,可重写
|
||||||
|
:param pk: 主键
|
||||||
|
:param data: pydantic model
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if await self.dao.update({"id": pk}, data.dict()) == 0:
|
||||||
|
return dict(code=400, msg="数据不存在")
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
async def create_item(self, data):
|
||||||
|
"""
|
||||||
|
创建数据,不通用可重写
|
||||||
|
:param data: pydantic model
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return await self.dao.insert(data.dict())
|
@ -1,66 +0,0 @@
|
|||||||
from tortoise import connections
|
|
||||||
|
|
||||||
from models import MenuModel
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_tree_menu():
|
|
||||||
return await MenuModel.filter(status__not=9).all().values()
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
async def get_apis(pk: int):
|
|
||||||
"""返回当前角色拥有的接口权限列表"""
|
|
||||||
db = connections.get("default")
|
|
||||||
return await db.execute_query_dict(
|
|
||||||
"""
|
|
||||||
select m.api, m.method
|
|
||||||
FROM sys_menu as m, sys_role_menu as srm WHERE m.id = srm.mid
|
|
||||||
AND srm.rid = (?) and m.status != 9""",
|
|
||||||
[pk],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def put_menu(pk: int, data):
|
|
||||||
"""更新菜单"""
|
|
||||||
return await MenuModel.filter(id=pk).update(**data.dict())
|
|
@ -1,79 +0,0 @@
|
|||||||
from tortoise import connections
|
|
||||||
|
|
||||||
from models import MenuModel, RoleMenuModel, RoleModel
|
|
||||||
from schemas.role import RoleIn
|
|
||||||
|
|
||||||
|
|
||||||
async def get_role_menus(rid: int):
|
|
||||||
"""
|
|
||||||
根据角色id 获取菜单
|
|
||||||
"""
|
|
||||||
db = connections.get("default")
|
|
||||||
# asc 降序
|
|
||||||
return await db.execute_query_dict(
|
|
||||||
"""
|
|
||||||
select m.id, m.name, m.icon, m.path, m.type, m.component, m.pid, m.identifier, m.api, m.method
|
|
||||||
FROM sys_menu as m, sys_role_menu WHERE m.id = sys_role_menu.mid
|
|
||||||
AND sys_role_menu.rid = (?) AND sys_role_menu.`status` = 1 order by m.id asc""",
|
|
||||||
[rid],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def new_role(role: RoleIn):
|
|
||||||
"""新增角色"""
|
|
||||||
# 校验菜单是否存在
|
|
||||||
if not all([await MenuModel.filter(id=mid).first() for mid in role.menus]):
|
|
||||||
return False
|
|
||||||
|
|
||||||
obj = await RoleModel.create(name=role.name, remark=role.remark)
|
|
||||||
# 写入菜单
|
|
||||||
await RoleMenuModel.bulk_create(
|
|
||||||
[RoleMenuModel(rid=obj.id, mid=mid) for mid in role.menus]
|
|
||||||
)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
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(**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, data):
|
|
||||||
"""更新角色 菜单"""
|
|
||||||
await RoleModel.filter(id=pk).update(name=data.name, remark=data.remark)
|
|
||||||
await RoleMenuModel.filter(rid=pk).update(status=9)
|
|
||||||
|
|
||||||
await RoleMenuModel.bulk_create(
|
|
||||||
[RoleMenuModel(rid=pk, mid=mid) for mid in data.menus]
|
|
||||||
)
|
|
@ -1,121 +0,0 @@
|
|||||||
from fastapi.encoders import jsonable_encoder
|
|
||||||
from tortoise import connections
|
|
||||||
|
|
||||||
from dbhelper.role import get_role
|
|
||||||
from models import UserModel, UserRoleModel
|
|
||||||
from schemas import UserPut
|
|
||||||
|
|
||||||
|
|
||||||
async def get_user(kwargs):
|
|
||||||
"""
|
|
||||||
根据条件查询到第一条符合结果的数据
|
|
||||||
Args:
|
|
||||||
kwargs:
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
|
|
||||||
"""
|
|
||||||
return await UserModel.filter(**kwargs).first()
|
|
||||||
|
|
||||||
|
|
||||||
async def get_user_info(user: UserModel):
|
|
||||||
"""
|
|
||||||
根据id查用户角色列表 按激活角色倒序显示
|
|
||||||
"""
|
|
||||||
db = connections.get("default")
|
|
||||||
# 查角色表 用户角色表中 角色状态 = 1, 关联表中 状态 != 9 为有效角色
|
|
||||||
sql_result = await db.execute_query_dict(
|
|
||||||
"""
|
|
||||||
select r.id, r.name, ur.status from sys_role as r , sys_user_role as ur where r.id = ur.rid and
|
|
||||||
ur.uid = (?) and r.status = 1 and ur.status !=9 order by ur.status desc
|
|
||||||
""",
|
|
||||||
[user.id],
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
**jsonable_encoder(user),
|
|
||||||
"roles": sql_result,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def get_users(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 = UserModel.filter(**kwargs).all().order_by("-created")
|
|
||||||
return await result.offset(skip).limit(limit), await result.count()
|
|
||||||
|
|
||||||
|
|
||||||
async def insert_user(user, roles):
|
|
||||||
"""新增用户,选择角色"""
|
|
||||||
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())
|
|
||||||
# 已有角色 关联 角色id 和是否选中状态
|
|
||||||
await UserRoleModel.bulk_create(
|
|
||||||
[UserRoleModel(rid=role.rid, uid=obj.id, status=role.status) for role in roles]
|
|
||||||
)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
rids = data.roles
|
|
||||||
del data.roles
|
|
||||||
for role in rids:
|
|
||||||
if await get_role({"id": role.rid, "status__not": 9}) is None:
|
|
||||||
return role.rid
|
|
||||||
# 更新用户
|
|
||||||
if data.password != "加密之后的密码":
|
|
||||||
data.password = get_password_hash(data.password)
|
|
||||||
else:
|
|
||||||
del data.password
|
|
||||||
await UserModel.filter(id=uid).update(**data.dict())
|
|
||||||
|
|
||||||
# todo 1. 先前有的角色,这次更新成没有 2. 先前没有的角色 这次更新成有, 3. 只更新了状态
|
|
||||||
|
|
||||||
db = connections.get("default")
|
|
||||||
# 1. 先把用户有的角色做删除
|
|
||||||
has_roles = await db.execute_query_dict(
|
|
||||||
"""
|
|
||||||
select r.id from sys_role as r , sys_user_role as ur where r.id = ur.rid and
|
|
||||||
ur.uid = (?) and r.status = 1 and ur.status !=9
|
|
||||||
""",
|
|
||||||
[uid],
|
|
||||||
)
|
|
||||||
|
|
||||||
# 2. 将先有的数据标记 删除
|
|
||||||
[await UserRoleModel.filter(rid=role["id"]).update(status=9) for role in has_roles]
|
|
||||||
|
|
||||||
# 2. 新增次此更新的数据
|
|
||||||
await UserRoleModel.bulk_create(
|
|
||||||
[UserRoleModel(uid=uid, **role.dict()) for role in rids]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def select_role(uid: int, rid: int):
|
|
||||||
"""用户切换角色"""
|
|
||||||
# 1.将用户id 未删除角色状态置为正常 1 ( 除切换角色id )
|
|
||||||
await UserRoleModel.filter(uid=uid, rid__not=rid, status__not=9).update(status=1)
|
|
||||||
# 2.将用户id 角色id 和当前角色匹配的数据置为选中
|
|
||||||
return await UserRoleModel.filter(uid=uid, rid=rid, status__not=9).update(status=5)
|
|
@ -13,9 +13,7 @@ app = FastAPI(
|
|||||||
exception_handlers=exception_handlers,
|
exception_handlers=exception_handlers,
|
||||||
)
|
)
|
||||||
|
|
||||||
load_routers(
|
load_routers(app, "router", no_depends="auth", depends=[Depends(check_permissions)])
|
||||||
app, "controller", no_depends="common", depends=[Depends(check_permissions)]
|
|
||||||
)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
BIN
backend/mini.db
BIN
backend/mini.db
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -7,3 +7,4 @@ requests==2.28.1
|
|||||||
tortoise-orm==0.19.2
|
tortoise-orm==0.19.2
|
||||||
uvicorn==0.18.3
|
uvicorn==0.18.3
|
||||||
websockets==10.3
|
websockets==10.3
|
||||||
|
loguru==0.6.0
|
||||||
|
19
backend/router/auth.py
Normal file
19
backend/router/auth.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from fastapi import APIRouter, WebSocket
|
||||||
|
|
||||||
|
from schemas import common as BaseSchema
|
||||||
|
from service import auth as AuthService
|
||||||
|
|
||||||
|
router = APIRouter(tags=["公共"])
|
||||||
|
|
||||||
|
|
||||||
|
LoginResult = BaseSchema.Response[BaseSchema.LoginResult]
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/login", summary="登录", response_model=LoginResult)
|
||||||
|
async def login(data: BaseSchema.LoginForm):
|
||||||
|
return await AuthService.user_login(data)
|
||||||
|
|
||||||
|
|
||||||
|
@router.websocket("/ws", name="系统信息")
|
||||||
|
async def get_system_info(ws: WebSocket):
|
||||||
|
await AuthService.system_info(ws)
|
30
backend/router/menu.py
Normal file
30
backend/router/menu.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
from schemas import common as BaseSchema
|
||||||
|
from schemas import menu as MenuSchema
|
||||||
|
from service.menu import service as MenuService
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/menu", tags=["菜单管理"])
|
||||||
|
|
||||||
|
Response = BaseSchema.Response
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("", summary="菜单新增", response_model=Response[MenuSchema.MenuRead])
|
||||||
|
async def menu_add(data: MenuSchema.MenuIn):
|
||||||
|
return await MenuService.create_item(data)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", summary="菜单列表", response_model=Response)
|
||||||
|
async def menu_arr():
|
||||||
|
return await MenuService.get_items()
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{pk}", summary="菜单删除", response_model=Response)
|
||||||
|
async def menu_del(pk: int):
|
||||||
|
return await MenuService.delete_item(pk)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{pk}", summary="菜单更新", response_model=Response)
|
||||||
|
async def menu_put(pk: int, data: MenuSchema.MenuIn):
|
||||||
|
"""更新菜单"""
|
||||||
|
return await MenuService.update_item(pk, data)
|
46
backend/router/role.py
Normal file
46
backend/router/role.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from fastapi import APIRouter, Query
|
||||||
|
|
||||||
|
from schemas import common as BaseSchema
|
||||||
|
from schemas import role as RoleSchema
|
||||||
|
from service.role import service as RoleService
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/role", tags=["角色管理"])
|
||||||
|
|
||||||
|
Response = BaseSchema.Response
|
||||||
|
ListAll = BaseSchema.ListAll
|
||||||
|
|
||||||
|
role_list_schema = ListAll[list[RoleSchema.RoleRead]]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", summary="角色列表", response_model=Response[role_list_schema])
|
||||||
|
async def role_list(
|
||||||
|
offset: int = Query(default=1, description="偏移量-页码"),
|
||||||
|
limit: int = Query(default=10, description="数据量"),
|
||||||
|
):
|
||||||
|
return await RoleService.get_items(offset, limit)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/query", summary="角色查询", response_model=Response[role_list_schema])
|
||||||
|
async def role_query(query: RoleSchema.RoleQuery):
|
||||||
|
return await RoleService.query_items(query)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("", summary="角色新增", response_model=Response[RoleSchema.RoleInfo])
|
||||||
|
async def role_create(data: RoleSchema.RoleIn):
|
||||||
|
return await RoleService.create_item(data)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{rid}/menu", summary="查询角色拥有权限", response_model=Response)
|
||||||
|
async def role_has_menu(rid: int):
|
||||||
|
return await RoleService.has_tree_menus(rid)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{pk}", summary="角色删除", response_model=Response)
|
||||||
|
async def role_del(pk: int):
|
||||||
|
return await RoleService.delete_item(pk)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{pk}", summary="角色更新", response_model=Response)
|
||||||
|
async def role_put(pk: int, data: RoleSchema.RoleIn):
|
||||||
|
"""更新角色"""
|
||||||
|
return await RoleService.update_item(pk, data)
|
53
backend/router/user.py
Normal file
53
backend/router/user.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
from fastapi import APIRouter, Depends, Query
|
||||||
|
|
||||||
|
from core.security import check_permissions
|
||||||
|
from schemas import common as BaseSchema
|
||||||
|
from schemas import user as UserSchema
|
||||||
|
from service.user import service as UserService
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/user", tags=["用户管理"])
|
||||||
|
|
||||||
|
Response = BaseSchema.Response
|
||||||
|
ListAll = BaseSchema.ListAll
|
||||||
|
|
||||||
|
user_list_schema = ListAll[list[UserSchema.UserRead]]
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("", summary="用户列表", response_model=Response[user_list_schema])
|
||||||
|
async def user_list(
|
||||||
|
offset: int = Query(default=1, description="偏移量-页码"),
|
||||||
|
limit: int = Query(default=10, description="数据量"),
|
||||||
|
):
|
||||||
|
return await UserService.get_items(offset, limit)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/query", summary="用户查询", response_model=Response[user_list_schema])
|
||||||
|
async def user_query(query: UserSchema.UserQuery):
|
||||||
|
return await UserService.query_items(query)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("", summary="用户新增", response_model=Response[UserSchema.UserRead])
|
||||||
|
async def user_create(data: UserSchema.UserAdd):
|
||||||
|
return await UserService.create_item(data)
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/{pk}", summary="用户删除", response_model=Response)
|
||||||
|
async def user_delete(pk: int):
|
||||||
|
return await UserService.delete_item(pk)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{pk}", summary="用户信息", response_model=Response[UserSchema.UserInfo])
|
||||||
|
async def user_info(pk: int):
|
||||||
|
return await UserService.get_item(pk)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/{pk}", summary="用户更新", response_model=Response)
|
||||||
|
async def user_update(pk: int, data: UserSchema.UserPut):
|
||||||
|
return await UserService.update_item(pk, data)
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/role/{rid}", summary="用户切换角色", response_model=Response)
|
||||||
|
async def user_change_role(
|
||||||
|
rid: int, user: UserSchema.UserRead = Depends(check_permissions)
|
||||||
|
):
|
||||||
|
return await UserService.change_current_role(user.id, rid)
|
@ -1,4 +1 @@
|
|||||||
from schemas.common import *
|
|
||||||
from schemas.menu import *
|
|
||||||
from schemas.role import *
|
|
||||||
from schemas.user import *
|
|
||||||
|
0
backend/service/__init__.py
Normal file
0
backend/service/__init__.py
Normal file
26
backend/service/auth.py
Normal file
26
backend/service/auth.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
from websockets.exceptions import WebSocketException
|
||||||
|
|
||||||
|
from core.dbhelper import has_user
|
||||||
|
from core.security import generate_token, verify_password
|
||||||
|
from core.utils import get_system_info
|
||||||
|
|
||||||
|
|
||||||
|
async def user_login(data):
|
||||||
|
"""用户登录"""
|
||||||
|
user_obj = await has_user(data.username)
|
||||||
|
if user_obj:
|
||||||
|
if verify_password(data.password, user_obj.password):
|
||||||
|
return dict(data=dict(id=user_obj.id, token=generate_token(data.username)))
|
||||||
|
return dict(code=400, msg="账号或密码错误")
|
||||||
|
|
||||||
|
|
||||||
|
async def system_info(ws):
|
||||||
|
await ws.accept()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
await ws.send_json(get_system_info())
|
||||||
|
except WebSocketException:
|
||||||
|
await ws.close()
|
26
backend/service/menu.py
Normal file
26
backend/service/menu.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from core.dbhelper import MenuDao
|
||||||
|
from core.service import Service
|
||||||
|
from core.utils import list_to_tree
|
||||||
|
|
||||||
|
|
||||||
|
class MenuService(Service):
|
||||||
|
def __init__(self):
|
||||||
|
super(MenuService, self).__init__(MenuDao)
|
||||||
|
|
||||||
|
async def get_items(self):
|
||||||
|
sql = "select * from sys_menu where status != 9 ;"
|
||||||
|
menus = await self.dao.raw_sql(sql)
|
||||||
|
try:
|
||||||
|
return dict(data=list_to_tree(menus))
|
||||||
|
except KeyError:
|
||||||
|
return dict(code=400, msg="菜单根节点丢失")
|
||||||
|
|
||||||
|
async def delete_item(self, pk):
|
||||||
|
if await MenuDao.select({"pid": pk, "status__not": 9}) is not None:
|
||||||
|
return dict(code=400, msg="请先删除子节点")
|
||||||
|
if await MenuDao.delete(pk) == 0:
|
||||||
|
return dict(code=400, msg="菜单不存在")
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
|
||||||
|
service = MenuService()
|
61
backend/service/role.py
Normal file
61
backend/service/role.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
from core.dbhelper import MenuDao, RoleDao, RoleMenuDao, has_permissions
|
||||||
|
from core.service import Service
|
||||||
|
from core.utils import list_to_tree
|
||||||
|
|
||||||
|
|
||||||
|
class RoleService(Service):
|
||||||
|
def __init__(self):
|
||||||
|
super(RoleService, self).__init__(RoleDao)
|
||||||
|
|
||||||
|
async def create_item(self, role):
|
||||||
|
"""
|
||||||
|
创建角色
|
||||||
|
:param role: pydantic model
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if not all(
|
||||||
|
[await MenuDao.select({"id": mid, "status__not": 9}) for mid in role.menus]
|
||||||
|
):
|
||||||
|
return dict(code=400, msg="菜单不存在")
|
||||||
|
|
||||||
|
obj = await RoleDao.insert(dict(name=role.name, remark=role.remark))
|
||||||
|
# 写入菜单
|
||||||
|
await RoleMenuDao.inserts([dict(rid=obj.id, mid=mid) for mid in role.menus])
|
||||||
|
return dict(data=obj)
|
||||||
|
|
||||||
|
async def update_item(self, pk, data):
|
||||||
|
"""
|
||||||
|
更新角色
|
||||||
|
:param pk:
|
||||||
|
:param data:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if await RoleDao.select({"id": pk}) is None:
|
||||||
|
return dict(code=400, msg="角色不存在")
|
||||||
|
# 如果不为ture -> 有菜单id不存在
|
||||||
|
if not all([await MenuDao.select({"id": mid}) for mid in data.menus]):
|
||||||
|
return dict(code=400, msg="菜单不存在")
|
||||||
|
|
||||||
|
await RoleDao.update(dict(id=pk), dict(name=data.name, remark=data.remark))
|
||||||
|
await RoleMenuDao.update(dict(rid=pk), dict(status=9))
|
||||||
|
|
||||||
|
await RoleMenuDao.inserts([dict(rid=pk, mid=mid) for mid in data.menus])
|
||||||
|
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def has_tree_menus(pk):
|
||||||
|
"""
|
||||||
|
查询角色拥有菜单
|
||||||
|
:param pk:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
menus = await has_permissions(pk, is_menu=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return dict(data=list_to_tree(menus))
|
||||||
|
except KeyError:
|
||||||
|
return dict(code=400, msg="菜单缺少根节点.")
|
||||||
|
|
||||||
|
|
||||||
|
service = RoleService()
|
90
backend/service/user.py
Normal file
90
backend/service/user.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
from fastapi.encoders import jsonable_encoder
|
||||||
|
|
||||||
|
from core.dbhelper import RoleDao, UserDao, UserRoleDao, has_roles
|
||||||
|
from core.security import get_password_hash
|
||||||
|
from core.service import Service
|
||||||
|
|
||||||
|
|
||||||
|
class UserService(Service):
|
||||||
|
def __init__(self):
|
||||||
|
super(UserService, self).__init__(UserDao)
|
||||||
|
|
||||||
|
async def create_item(self, data):
|
||||||
|
"""创建用户"""
|
||||||
|
# 检查用户是否存在
|
||||||
|
if await self.dao.select({"username": data.username}) is not None:
|
||||||
|
return dict(code=400, msg="用户名已存在")
|
||||||
|
rids = data.roles
|
||||||
|
del data.roles
|
||||||
|
data.password = get_password_hash(data.password)
|
||||||
|
# 检查选中的角色是否存在
|
||||||
|
for role in rids:
|
||||||
|
if await RoleDao.select(dict(id=role.rid, status__not=9)) is None:
|
||||||
|
return dict(code=400, msg=f"角色{role.rid}不存在")
|
||||||
|
|
||||||
|
# 创建用户- 用户表写入数据
|
||||||
|
user_obj = await UserDao.insert(data.dict())
|
||||||
|
# 关联表写入数据
|
||||||
|
await UserRoleDao.inserts(
|
||||||
|
[dict(rid=role.rid, uid=user_obj.id, status=role.status) for role in rids]
|
||||||
|
)
|
||||||
|
return dict(data=user_obj)
|
||||||
|
|
||||||
|
async def get_item(self, pk):
|
||||||
|
"""获取用户信息"""
|
||||||
|
user_obj = await self.dao.select({"id": pk})
|
||||||
|
if user_obj is None:
|
||||||
|
return dict(code=400, msg="用户不存在")
|
||||||
|
roles = await has_roles(user_obj.id)
|
||||||
|
return dict(data=dict(**jsonable_encoder(user_obj), roles=roles))
|
||||||
|
|
||||||
|
async def update_item(self, pk, data):
|
||||||
|
"""用户编辑修改"""
|
||||||
|
if await self.dao.select({"id": pk}) is None:
|
||||||
|
return dict(code=400, msg="用户不存在")
|
||||||
|
|
||||||
|
rids = data.roles
|
||||||
|
del data.roles
|
||||||
|
for role in rids:
|
||||||
|
if await RoleDao.select({"id": role.rid, "status__not": 9}) is None:
|
||||||
|
return role.rid
|
||||||
|
# 更新用户
|
||||||
|
if data.password != "加密之后的密码":
|
||||||
|
data.password = get_password_hash(data.password)
|
||||||
|
else:
|
||||||
|
del data.password
|
||||||
|
await UserDao.update(dict(id=pk), data.dict())
|
||||||
|
|
||||||
|
# todo 1. 先前有的角色,这次更新成没有 2. 先前没有的角色 这次更新成有, 3. 只更新了状态
|
||||||
|
|
||||||
|
roles = await has_roles(pk)
|
||||||
|
|
||||||
|
# 2. 将先有的数据标记 删除
|
||||||
|
[
|
||||||
|
await UserRoleDao.update(dict(rid=role["id"], uid=pk), dict(status=9))
|
||||||
|
for role in roles
|
||||||
|
]
|
||||||
|
|
||||||
|
# 2. 新增次此更新的数据
|
||||||
|
await UserRoleDao.inserts(
|
||||||
|
[dict(role.dict(), uid=pk, status=role.status) for role in rids]
|
||||||
|
)
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
async def change_current_role(uid, rid):
|
||||||
|
"""用户切换角色"""
|
||||||
|
# 1.将用户id 未删除角色状态置为正常 1 ( 除切换角色id )
|
||||||
|
await UserRoleDao.update(
|
||||||
|
dict(uid=uid, rid__not=rid, status__not=9), dict(status=1)
|
||||||
|
)
|
||||||
|
# 2.将用户id 角色id 和当前角色匹配的数据置为选中
|
||||||
|
res = await UserRoleDao.update(
|
||||||
|
dict(uid=uid, rid=rid, status__not=9), dict(status=5)
|
||||||
|
)
|
||||||
|
if res == 0:
|
||||||
|
return dict(code=400, msg=f"角色不存在{res}")
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
|
||||||
|
service = UserService()
|
Loading…
Reference in New Issue
Block a user