优化crud 增加类型提示

This commit is contained in:
ktianc 2023-12-16 21:17:44 +08:00
parent 518f9d4a47
commit 29037d7184
8 changed files with 154 additions and 79 deletions

View File

@ -13,7 +13,7 @@ from sqlalchemy.orm import joinedload
from sqlalchemy.orm.strategy_options import _AbstractLoad
from core.exception import CustomException
from fastapi.encoders import jsonable_encoder
from sqlalchemy import select
from sqlalchemy import select, false
from core.crud import DalBase
from sqlalchemy.ext.asyncio import AsyncSession
from core.validator import vali_telephone
@ -45,7 +45,10 @@ class UserDal(DalBase):
]
def __init__(self, db: AsyncSession):
super(UserDal, self).__init__(db, models.VadminUser, schemas.UserSimpleOut)
super(UserDal, self).__init__()
self.db = db
self.model = models.VadminUser
self.schema = schemas.UserSimpleOut
async def update_login_info(self, user: models.VadminUser, last_ip: str) -> None:
"""
@ -67,6 +70,11 @@ class UserDal(DalBase):
) -> Any:
"""
创建用户
:param data:
:param v_options:
:param v_return_obj:
:param v_schema:
:return:
"""
unique = await self.get_data(telephone=data.telephone, v_return_none=True)
if unique:
@ -92,6 +100,12 @@ class UserDal(DalBase):
) -> Any:
"""
更新用户信息
:param data_id:
:param data:
:param v_options:
:param v_return_obj:
:param v_schema:
:return:
"""
obj = await self.get_data(data_id, v_options=[joinedload(self.model.roles)])
data_dict = jsonable_encoder(data)
@ -111,6 +125,9 @@ class UserDal(DalBase):
async def reset_current_password(self, user: models.VadminUser, data: schemas.ResetPwd) -> None:
"""
重置密码
:param user:
:param data:
:return:
"""
if data.password != data.password_two:
raise CustomException(msg="两次密码不一致", code=400)
@ -124,6 +141,9 @@ class UserDal(DalBase):
async def update_current_info(self, user: models.VadminUser, data: schemas.UserUpdateBaseInfo) -> Any:
"""
更新当前用户基本信息
:param user:
:param data:
:return:
"""
if data.telephone != user.telephone:
unique = await self.get_data(telephone=data.telephone, v_return_none=True)
@ -140,6 +160,9 @@ class UserDal(DalBase):
async def export_query_list(self, header: list, params: UserParams) -> dict:
"""
导出用户查询列表为 excel
:param header:
:param params:
:return:
"""
datas = await self.get_datas(**params.dict(), v_return_objs=True)
# 获取表头
@ -171,6 +194,7 @@ class UserDal(DalBase):
async def get_import_headers_options(self) -> None:
"""
补全表头数据选项
:return:
"""
# 角色选择项
roles = await RoleDal(self.db).get_datas(limit=0, v_return_objs=True, disabled=False, is_admin=False)
@ -188,16 +212,20 @@ class UserDal(DalBase):
async def download_import_template(self) -> dict:
"""
下载用户最新版导入模板
:return:
"""
await self.get_import_headers_options()
em = WriteXlsx(sheet_name="用户导入模板")
em = WriteXlsx()
em.create_excel(sheet_name="用户导入模板", save_static=True)
em.generate_template(copy.deepcopy(self.import_headers))
em.close()
return {"url": em.file_url, "filename": "用户导入模板.xlsx"}
return {"url": em.get_file_url(), "filename": "用户导入模板.xlsx"}
async def import_users(self, file: UploadFile) -> dict:
"""
批量导入用户数据
:param file:
:return:
"""
await self.get_import_headers_options()
im = ImportManage(file, copy.deepcopy(self.import_headers))
@ -224,6 +252,8 @@ class UserDal(DalBase):
"""
初始化所选用户密码
将用户密码改为系统默认密码并将初始化密码状态改为false
:param ids:
:return:
"""
users = await self.get_datas(limit=0, id=("in", ids), v_return_objs=True)
result = []
@ -244,6 +274,9 @@ class UserDal(DalBase):
"""
初始化所选用户密码并发送通知短信
将用户密码改为系统默认密码并将初始化密码状态改为false
:param ids:
:param rd:
:return:
"""
result = await self.init_password(ids)
for user in result:
@ -266,6 +299,9 @@ class UserDal(DalBase):
"""
初始化所选用户密码并发送通知邮件
将用户密码改为系统默认密码并将初始化密码状态改为false
:param ids:
:param rd:
:return:
"""
result = await self.init_password(ids)
for user in result:
@ -294,6 +330,9 @@ class UserDal(DalBase):
async def update_current_avatar(self, user: models.VadminUser, file: UploadFile) -> str:
"""
更新当前用户头像
:param user:
:param file:
:return:
"""
result = await AliyunOSS(BucketConf(**settings.ALIYUN_OSS)).upload_image("avatar", file)
user.avatar = result
@ -303,6 +342,10 @@ class UserDal(DalBase):
async def update_wx_server_openid(self, code: str, user: models.VadminUser, redis: Redis) -> bool:
"""
更新用户服务端微信平台openid
:param code:
:param user:
:param redis:
:return:
"""
wx = WXOAuth(redis, 0)
openid = await wx.parsing_openid(code)
@ -332,7 +375,10 @@ class UserDal(DalBase):
class RoleDal(DalBase):
def __init__(self, db: AsyncSession):
super(RoleDal, self).__init__(db, models.VadminRole, schemas.RoleSimpleOut)
super(RoleDal, self).__init__()
self.db = db
self.model = models.VadminRole
self.schema = schemas.RoleSimpleOut
async def create_data(
self,
@ -341,7 +387,14 @@ class RoleDal(DalBase):
v_return_obj: bool = False,
v_schema: Any = None
) -> Any:
"""创建数据"""
"""
创建数据
:param data:
:param v_options:
:param v_return_obj:
:param v_schema:
:return:
"""
obj = self.model(**data.model_dump(exclude={'menu_ids'}))
if data.menu_ids:
menus = await MenuDal(db=self.db).get_datas(limit=0, id=("in", data.menu_ids), v_return_objs=True)
@ -358,7 +411,15 @@ class RoleDal(DalBase):
v_return_obj: bool = False,
v_schema: Any = None
) -> Any:
"""更新单个数据"""
"""
更新单个数据
:param data_id:
:param data:
:param v_options:
:param v_return_obj:
:param v_schema:
:return:
"""
obj = await self.get_data(data_id, v_options=[joinedload(self.model.menus)])
obj_dict = jsonable_encoder(data)
for key, value in obj_dict.items():
@ -379,7 +440,10 @@ class RoleDal(DalBase):
return [i.id for i in role.menus]
async def get_select_datas(self) -> list:
"""获取选择数据,全部数据"""
"""
获取选择数据全部数据
:return:
"""
sql = select(self.model)
queryset = await self.db.scalars(sql)
return [schemas.RoleOptionsOut.model_validate(i).model_dump() for i in queryset.all()]
@ -401,18 +465,23 @@ class RoleDal(DalBase):
class MenuDal(DalBase):
def __init__(self, db: AsyncSession):
super(MenuDal, self).__init__(db, models.VadminMenu, schemas.MenuSimpleOut)
super(MenuDal, self).__init__()
self.db = db
self.model = models.VadminMenu
self.schema = schemas.MenuSimpleOut
async def get_tree_list(self, mode: int) -> list:
"""
1获取菜单树列表
2获取菜单树选择项添加/修改菜单时使用
3获取菜单树列表角色添加菜单权限时使用
:param mode:
:return:
"""
if mode == 3:
sql = select(self.model).where(self.model.disabled == 0, self.model.is_delete == False)
sql = select(self.model).where(self.model.disabled == 0, self.model.is_delete == false())
else:
sql = select(self.model).where(self.model.is_delete == False)
sql = select(self.model).where(self.model.is_delete == false())
queryset = await self.db.scalars(sql)
datas = list(queryset.all())
roots = filter(lambda i: not i.parent_id, datas)
@ -435,10 +504,12 @@ class MenuDal(DalBase):
redirect: string
children?: AppCustomRouteRecordRaw[]
}
:param user:
:return:
"""
if any([i.is_admin for i in user.roles]):
sql = select(self.model) \
.where(self.model.disabled == 0, self.model.menu_type != "2", self.model.is_delete == False)
.where(self.model.disabled == 0, self.model.menu_type != "2", self.model.is_delete == false())
queryset = await self.db.scalars(sql)
datas = list(queryset.all())
else:
@ -457,10 +528,10 @@ class MenuDal(DalBase):
def generate_router_tree(self, menus: list[models.VadminMenu], nodes: filter, name: str = "") -> list:
"""
生成路由树
menus: 菜单列表
nodes节点菜单列表
namename拼接切记Name不能重复
:param menus: 总菜单列表
:param nodes: 节点菜单列表
:param name: name拼接切记Name不能重复
:return:
"""
data = []
for root in nodes:
@ -482,9 +553,9 @@ class MenuDal(DalBase):
def generate_tree_list(self, menus: list[models.VadminMenu], nodes: filter) -> list:
"""
生成菜单树列表
menus: 菜单列表
nodes每层节点菜单列表
:param menus: 总菜单列表
:param nodes: 每层节点菜单列表
:return:
"""
data = []
for root in nodes:
@ -498,9 +569,9 @@ class MenuDal(DalBase):
def generate_tree_options(self, menus: list[models.VadminMenu], nodes: filter) -> list:
"""
生成菜单树选择项
menus: 菜单列表
nodes每层节点菜单列表
:param menus:总菜单列表
:param nodes:每层节点菜单列表
:return:
"""
data = []
for root in nodes:
@ -515,6 +586,10 @@ class MenuDal(DalBase):
def menus_order(cls, datas: list, order: str = "order", children: str = "children") -> list:
"""
菜单排序
:param datas:
:param order:
:param children:
:return:
"""
result = sorted(datas, key=lambda menu: menu[order])
for item in result:
@ -529,32 +604,10 @@ class MenuDal(DalBase):
:param ids: 数据集
:param v_soft: 是否执行软删除
:param kwargs: 其他更新字段
:return:
"""
count = await RoleDal(self.db).get_count(v_join=[["menus"]], v_where=[self.model.id.in_(ids)])
if count > 0:
raise CustomException("无法删除存在角色关联的菜单", code=400)
await super(MenuDal, self).delete_datas(ids, v_soft, **kwargs)
class TestDal(DalBase):
def __init__(self, db: AsyncSession):
super(TestDal, self).__init__(db, models.VadminUser, schemas.UserSimpleOut)
async def test(self):
# print("-----------------------开始------------------------")
options = [joinedload(self.model.roles)]
v_join = [[self.model.roles]]
v_where = [self.model.id == 1, models.VadminRole.id == 1]
v_start_sql = select(self.model)
result, count = await self.get_datas(
v_start_sql=v_start_sql,
v_join=v_join,
v_options=options,
v_where=v_where,
v_return_count=True
)
if result:
print(result)
print(count)
# print("-----------------------结束------------------------")

View File

@ -9,7 +9,7 @@
"""
类依赖项-官方文档https://fastapi.tiangolo.com/zh/tutorial/dependencies/classes-as-dependencies/
"""
from fastapi import Depends
from fastapi import Depends, Query
from core.dependencies import Paging, QueryParams
@ -20,9 +20,9 @@ class RoleParams(QueryParams):
def __init__(
self,
name: str | None = None,
role_key: str | None = None,
disabled: bool | None = None,
name: str | None = Query(None, title="角色名称"),
role_key: str | None = Query(None, title="权限字符"),
disabled: bool | None = Query(None, title="是否禁用"),
params: Paging = Depends()
):
super().__init__(params)

View File

@ -9,7 +9,7 @@
"""
类依赖项-官方文档https://fastapi.tiangolo.com/zh/tutorial/dependencies/classes-as-dependencies/
"""
from fastapi import Depends
from fastapi import Depends, Query
from core.dependencies import Paging, QueryParams
@ -20,11 +20,11 @@ class UserParams(QueryParams):
def __init__(
self,
name: str | None = None,
telephone: str | None = None,
email: str | None = None,
is_active: bool | None = None,
is_staff: bool | None = None,
name: str | None = Query(None, title="用户名称"),
telephone: str | None = Query(None, title="手机号"),
email: str | None = Query(None, title="邮箱"),
is_active: bool | None = Query(None, title="是否可用"),
is_staff: bool | None = Query(None, title="是否为工作人员"),
params: Paging = Depends()
):
super().__init__(params)

View File

@ -13,22 +13,13 @@ from core.database import redis_getter
from utils.response import SuccessResponse, ErrorResponse
from . import schemas, crud, models
from core.dependencies import IdList
from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth, OpenAuth
from apps.vadmin.auth.utils.current import AllUserAuth, FullAdminAuth
from apps.vadmin.auth.utils.validation.auth import Auth
from .params import UserParams, RoleParams
app = APIRouter()
###########################################################
# 接口测试
###########################################################
@app.get("/test", summary="接口测试")
async def test(auth: Auth = Depends(OpenAuth())):
await crud.TestDal(auth.db).test()
return SuccessResponse()
###########################################################
# 用户管理
###########################################################
@ -260,6 +251,6 @@ async def get_role_menu_tree(
role_id: int,
auth: Auth = Depends(FullAdminAuth(permissions=["auth.role.create", "auth.role.update"]))
):
treeselect = await crud.MenuDal(auth.db).get_tree_list(mode=3)
tree_data = await crud.MenuDal(auth.db).get_tree_list(mode=3)
role_menu_tree = await crud.RoleDal(auth.db).get_role_menu_tree(role_id)
return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": treeselect})
return SuccessResponse({"role_menu_tree": role_menu_tree, "menus": tree_data})

View File

@ -14,7 +14,10 @@ from . import models, schemas
class IssueDal(DalBase):
def __init__(self, db: AsyncSession):
super(IssueDal, self).__init__(db, models.VadminIssue, schemas.IssueSimpleOut)
super(IssueDal, self).__init__()
self.db = db
self.model = models.VadminIssue
self.schema = schemas.IssueSimpleOut
async def add_view_number(self, data_id: int) -> None:
"""
@ -28,4 +31,7 @@ class IssueDal(DalBase):
class IssueCategoryDal(DalBase):
def __init__(self, db: AsyncSession):
super(IssueCategoryDal, self).__init__(db, models.VadminIssueCategory, schemas.IssueCategorySimpleOut)
super(IssueCategoryDal, self).__init__()
self.db = db
self.model = models.VadminIssueCategory
self.schema = schemas.IssueCategorySimpleOut

View File

@ -17,7 +17,10 @@ from core.mongo_manage import MongoManage
class LoginRecordDal(DalBase):
def __init__(self, db: AsyncSession):
super(LoginRecordDal, self).__init__(db, models.VadminLoginRecord, schemas.LoginRecordSimpleOut)
super(LoginRecordDal, self).__init__()
self.db = db
self.model = models.VadminLoginRecord
self.schema = schemas.LoginRecordSimpleOut
async def get_user_distribute(self) -> list[dict]:
"""
@ -69,10 +72,17 @@ class LoginRecordDal(DalBase):
class SMSSendRecordDal(DalBase):
def __init__(self, db: AsyncSession):
super(SMSSendRecordDal, self).__init__(db, models.VadminSMSSendRecord, schemas.SMSSendRecordSimpleOut)
super(SMSSendRecordDal, self).__init__()
self.db = db
self.model = models.VadminSMSSendRecord
self.schema = schemas.SMSSendRecordSimpleOut
class OperationRecordDal(MongoManage):
def __init__(self, db: AsyncIOMotorDatabase):
super(OperationRecordDal, self).__init__(db, "operation_record", schemas.OperationRecordSimpleOut)
super(OperationRecordDal, self).__init__()
self.db = db
self.collection = db["operation_record"]
self.schema = schemas.OperationRecordSimpleOut
self.is_object_id = True

View File

@ -14,4 +14,7 @@ from . import models, schemas
class ImagesDal(DalBase):
def __init__(self, db: AsyncSession):
super(ImagesDal, self).__init__(db, models.VadminImages, schemas.ImagesSimpleOut)
super(ImagesDal, self).__init__()
self.db = db
self.model = models.VadminImages
self.schema = schemas.ImagesSimpleOut

View File

@ -30,7 +30,10 @@ from fastapi import Request
class DictTypeDal(DalBase):
def __init__(self, db: AsyncSession):
super(DictTypeDal, self).__init__(db, models.VadminDictType, schemas.DictTypeSimpleOut)
super(DictTypeDal, self).__init__()
self.db = db
self.model = models.VadminDictType
self.schema = schemas.DictTypeSimpleOut
async def get_dicts_details(self, dict_types: list[str]) -> dict:
"""
@ -62,13 +65,19 @@ class DictTypeDal(DalBase):
class DictDetailsDal(DalBase):
def __init__(self, db: AsyncSession):
super(DictDetailsDal, self).__init__(db, models.VadminDictDetails, schemas.DictDetailsSimpleOut)
super(DictDetailsDal, self).__init__()
self.db = db
self.model = models.VadminDictDetails
self.schema = schemas.DictDetailsSimpleOut
class SettingsDal(DalBase):
def __init__(self, db: AsyncSession):
super(SettingsDal, self).__init__(db, models.VadminSystemSettings, schemas.SettingsSimpleOut)
super(SettingsDal, self).__init__()
self.db = db
self.model = models.VadminSystemSettings
self.schema = schemas.SettingsSimpleOut
async def get_tab_values(self, tab_id: int) -> dict:
"""
@ -108,6 +117,9 @@ class SettingsDal(DalBase):
if "wx_server_app_id" in datas and REDIS_DB_ENABLE:
rd = redis_getter(request)
await rd.client().set("wx_server", json.dumps(datas))
elif "sms_access_key" in datas and REDIS_DB_ENABLE:
rd = redis_getter(request)
await rd.client().set('aliyun_sms', json.dumps(datas))
async def get_base_config(self) -> dict:
"""
@ -371,8 +383,8 @@ class TaskDal(MongoManage):
使用消息无保留策略无保留是指当发送者向某个频道发送消息时如果没有订阅该频道的调用方就直接将该消息丢弃
:params rd: redis 对象
:params data: 行数据字典
:param rd: redis 对象
:param data: 行数据字典
:return: 接收到消息的订阅者数量
"""
exec_strategy = data.get("exec_strategy")