diff --git a/kinit-admin/src/components/Form/src/Form.vue b/kinit-admin/src/components/Form/src/Form.vue index 5077924..a30a69b 100644 --- a/kinit-admin/src/components/Form/src/Form.vue +++ b/kinit-admin/src/components/Form/src/Form.vue @@ -162,15 +162,7 @@ export default defineComponent({ if (v.hidden !== undefined) { return !v.hidden } else if (v.ifshow !== undefined) { - const show = v.ifshow(unref(formModel)) - if (!show) { - if (v.value !== undefined) { - formModel.value[v.field] = v.value - } else { - formModel.value[v.field] = undefined - } - } - return show + return v.ifshow(unref(formModel)) } return true }) diff --git a/kinit-admin/src/components/RightToolbar/src/RightToolbar.vue b/kinit-admin/src/components/RightToolbar/src/RightToolbar.vue index 40e3bfd..9a0dde8 100644 --- a/kinit-admin/src/components/RightToolbar/src/RightToolbar.vue +++ b/kinit-admin/src/components/RightToolbar/src/RightToolbar.vue @@ -15,7 +15,9 @@ import { ref, PropType } from 'vue' import { propTypes } from '@/utils/propTypes' import draggable from 'vuedraggable' import { useAppStore } from '@/store/modules/app' +import { useCache } from '@/hooks/web/useCache' +const { wsCache } = useCache() const appStore = useAppStore() const emit = defineEmits(['getList', 'update:tableSize']) @@ -25,7 +27,8 @@ const props = defineProps({ columns: { type: Array as PropType, default: () => [] - } + }, + cacheTableHeadersKey: propTypes.string.def('') }) const handleClickRefresh = () => { @@ -43,6 +46,15 @@ const handleCommand = (command: string) => { const checkAll = ref(false) const columns = ref(props.columns) const isIndeterminate = ref(true) // 如果为True,则表示为半选状态 + +// 获取表头字段默认列表 +if (props.cacheTableHeadersKey) { + const cacheTableHeaders = wsCache.get(props.cacheTableHeadersKey) + if (cacheTableHeaders) { + Object.assign(columns.value, JSON.parse(cacheTableHeaders)) + } +} + const handleCheckAllChange = (val: boolean) => { columns.value.forEach((item) => { if (item.disabled !== true) { @@ -51,6 +63,7 @@ const handleCheckAllChange = (val: boolean) => { }) isIndeterminate.value = false } + const handleCheckChange = () => { checkAll.value = columns.value.every((item) => item.show) if (checkAll.value) { diff --git a/kinit-admin/src/views/vadmin/auth/menu/index.vue b/kinit-admin/src/views/vadmin/auth/menu/index.vue index ac4ea25..f1ac3be 100644 --- a/kinit-admin/src/views/vadmin/auth/menu/index.vue +++ b/kinit-admin/src/views/vadmin/auth/menu/index.vue @@ -17,7 +17,10 @@ import Write from './components/Write.vue' import { columns } from './components/menu.data' import { useDictStore } from '@/store/modules/dict' import { selectDictLabel, DictDetail } from '@/utils/dict' +import { useCache } from '@/hooks/web/useCache' +import { useRouter } from 'vue-router' +const { wsCache } = useCache() const { t } = useI18n() let menuTypeOptions = ref([]) @@ -117,9 +120,13 @@ watch(tableSize, (val) => { tableSize.value = val }) +const route = useRouter() +const cacheTableHeadersKey = route.currentRoute.value.fullPath + watch( columns, - async () => { + async (val) => { + wsCache.set(cacheTableHeadersKey, JSON.stringify(val)) await nextTick() elTableRef.value?.doLayout() }, @@ -139,7 +146,12 @@ watch( > - + { +const addAction = () => { dialogTitle.value = t('exampleDemo.add') tableObject.currentRow = null dialogVisible.value = true @@ -50,7 +53,6 @@ const updateAction = async (row: any) => { dialogTitle.value = '编辑' tableObject.currentRow = res.data defaultCheckedKeys.value = res.data.menus.map((item: any) => item.id) - console.log(defaultCheckedKeys.value) dialogVisible.value = true actionType.value = 'edit' } @@ -103,9 +105,13 @@ watch(tableSize, (val) => { tableSize.value = val }) +const route = useRouter() +const cacheTableHeadersKey = route.currentRoute.value.fullPath + watch( columns, - async () => { + async (val) => { + wsCache.set(cacheTableHeadersKey, JSON.stringify(val)) await nextTick() elTableRef.value?.doLayout() }, @@ -122,10 +128,15 @@ watch(
- 新增角色 + 新增角色 - +
{ +const addAction = () => { dialogTitle.value = t('exampleDemo.add') tableObject.currentRow = null dialogVisible.value = true @@ -147,9 +150,13 @@ watch(tableSize, (val) => { tableSize.value = val }) +const route = useRouter() +const cacheTableHeadersKey = route.currentRoute.value.fullPath + watch( columns, - async () => { + async (val) => { + wsCache.set(cacheTableHeadersKey, JSON.stringify(val)) await nextTick() elTableRef.value?.doLayout() }, @@ -209,7 +216,7 @@ const handleCommand = (command: string) => {
- 新增用户 + 新增用户 批量导入用户 @@ -250,7 +257,12 @@ const handleCommand = (command: string) => { - +
{ +const addAction = () => { dialogTitle.value = t('exampleDemo.add') tableObject.currentRow = null dialogVisible.value = true @@ -126,9 +127,13 @@ const tableSize = ref('default') watch(tableSize, (val) => { tableSize.value = val }) + +const cacheTableHeadersKey = currentRoute.value.fullPath + watch( columns, - async () => { + async (val) => { + wsCache.set(cacheTableHeadersKey, JSON.stringify(val)) await nextTick() elTableRef.value?.doLayout() }, @@ -150,10 +155,15 @@ watch(
- {{ t('exampleDemo.add') }} + {{ t('exampleDemo.add') }} - +
{ +const addAction = () => { dialogTitle.value = t('exampleDemo.add') tableObject.currentRow = null dialogVisible.value = true @@ -104,9 +105,14 @@ const tableSize = ref('default') watch(tableSize, (val) => { tableSize.value = val }) + +const route = useRouter() +const cacheTableHeadersKey = route.currentRoute.value.fullPath + watch( columns, - async () => { + async (val) => { + wsCache.set(cacheTableHeadersKey, JSON.stringify(val)) await nextTick() elTableRef.value?.doLayout() }, @@ -123,10 +129,15 @@ watch(
- {{ t('exampleDemo.add') }} + {{ t('exampleDemo.add') }} - +
{ tableSize.value = val }) +const route = useRouter() +const cacheTableHeadersKey = route.currentRoute.value.fullPath + watch( columns, - async () => { + async (val) => { + wsCache.set(cacheTableHeadersKey, JSON.stringify(val)) await nextTick() elTableRef.value?.doLayout() }, @@ -85,7 +93,12 @@ getList()
- +
{ tableSize.value = val }) +const route = useRouter() +const cacheTableHeadersKey = route.currentRoute.value.fullPath + watch( columns, - async () => { + async (val) => { + wsCache.set(cacheTableHeadersKey, JSON.stringify(val)) await nextTick() elTableRef.value?.doLayout() }, @@ -56,7 +64,12 @@ watch(
- +
{ const appStore = useAppStore() const { wsCache } = useCache() const loading = ref(false) -const { token } = config -const _token = wsCache.get(token) +const token = wsCache.get(appStore.getToken) const save = async () => { const formRef = unref(elFormRef) @@ -122,7 +120,7 @@ getData() :on-success="handleLogoUploadSuccess" accept="image/jpeg,image/gif,image/png" name="file" - :headers="{ Authorization: _token }" + :headers="{ Authorization: token }" > LoginResult: self.result = LoginResult() options = [models.VadminUser.roles, "roles.menus"] - user = await crud.UserDal(db).get_data(telephone=data.telephone, v_return_none=True, options=options) + user = await crud.UserDal(db).get_data(telephone=data.telephone, v_return_none=True, v_options=options) if not user: self.result.msg = "该手机号不存在!" return self.result diff --git a/kinit-api/apps/vadmin/auth/views.py b/kinit-api/apps/vadmin/auth/views.py index ac24dac..38ba690 100644 --- a/kinit-api/apps/vadmin/auth/views.py +++ b/kinit-api/apps/vadmin/auth/views.py @@ -51,7 +51,7 @@ async def get_user(data_id: int, auth: Auth = Depends(login_auth)): model = models.VadminUser options = [model.roles] schema = schemas.UserOut - return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, options, schema)) + return SuccessResponse(await crud.UserDal(auth.db).get_data(data_id, options, v_schema=schema)) @app.post("/user/current/reset/password/", summary="重置当前用户密码") @@ -77,8 +77,11 @@ async def get_user_current_info(auth: Auth = Depends(full_admin)): @app.post("/user/export/query/list/to/excel/", summary="导出用户查询列表为excel") -async def post_user_export_query_list(header: list = Body(..., title="表头与对应字段"), params: UserParams = Depends(), - auth: Auth = Depends(login_auth)): +async def post_user_export_query_list( + header: list = Body(..., title="表头与对应字段"), + params: UserParams = Depends(), + auth: Auth = Depends(login_auth) +): return SuccessResponse(await crud.UserDal(auth.db).export_query_list(header, params)) @@ -137,7 +140,7 @@ async def get_role(data_id: int, auth: Auth = Depends(login_auth)): model = models.VadminRole options = [model.menus] schema = schemas.RoleOut - return SuccessResponse(await crud.RoleDal(auth.db).get_data(data_id, options, schema)) + return SuccessResponse(await crud.RoleDal(auth.db).get_data(data_id, options, v_schema=schema)) ########################################################### @@ -181,7 +184,7 @@ async def put_menus(data_id: int, data: schemas.Menu, auth: Auth = Depends(login @app.get("/menus/{data_id}/", summary="获取菜单信息") async def put_menus(data_id: int, auth: Auth = Depends(login_auth)): schema = schemas.MenuSimpleOut - return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, schema)) + return SuccessResponse(await crud.MenuDal(auth.db).get_data(data_id, None, v_schema=schema)) @app.get("/role/menus/tree/{role_id}/", summary="获取菜单列表树信息以及角色菜单权限ID,角色权限使用") diff --git a/kinit-api/apps/vadmin/record/crud.py b/kinit-api/apps/vadmin/record/crud.py index a9b43eb..bf31539 100644 --- a/kinit-api/apps/vadmin/record/crud.py +++ b/kinit-api/apps/vadmin/record/crud.py @@ -69,5 +69,6 @@ class LoginRecordDal(DalBase): class SMSSendRecordDal(DalBase): + def __init__(self, db: AsyncSession): super(SMSSendRecordDal, self).__init__(db, models.VadminSMSSendRecord, schemas.SMSSendRecordSimpleOut) diff --git a/kinit-api/apps/vadmin/record/models/login.py b/kinit-api/apps/vadmin/record/models/login.py index 9893d20..be239eb 100644 --- a/kinit-api/apps/vadmin/record/models/login.py +++ b/kinit-api/apps/vadmin/record/models/login.py @@ -57,8 +57,16 @@ class VadminLoginRecord(BaseModel): ip = IPManage(req.client.host) location = await ip.parse() params = json.dumps({"body": body, "headers": header}) - obj = VadminLoginRecord(**location.dict(), telephone=data.telephone, status=status, browser=browser, - system=system, response=json.dumps(resp), request=params, platform=data.platform, - login_method=data.method) + obj = VadminLoginRecord( + **location.dict(), + telephone=data.telephone, + status=status, + browser=browser, + system=system, + response=json.dumps(resp), + request=params, + platform=data.platform, + login_method=data.method + ) db.add(obj) await db.flush() diff --git a/kinit-api/apps/vadmin/record/views.py b/kinit-api/apps/vadmin/record/views.py index 128157b..036db7b 100644 --- a/kinit-api/apps/vadmin/record/views.py +++ b/kinit-api/apps/vadmin/record/views.py @@ -19,24 +19,24 @@ app = APIRouter() # 日志管理 ########################################################### @app.get("/logins/", summary="获取登录日志列表") -async def get_record_login(params: LoginParams = Depends(), auth: Auth = Depends(login_auth)): - datas = await crud.LoginRecordDal(auth.db).get_datas(**params.dict()) - count = await crud.LoginRecordDal(auth.db).get_count(**params.to_count()) +async def get_record_login(p: LoginParams = Depends(), auth: Auth = Depends(login_auth)): + datas = await crud.LoginRecordDal(auth.db).get_datas(**p.dict()) + count = await crud.LoginRecordDal(auth.db).get_count(**p.to_count()) return SuccessResponse(datas, count=count) @app.get("/operations/", summary="获取操作日志列表") -async def get_record_operation(params: OperationParams = Depends(), db: DatabaseManage = Depends(get_database), +async def get_record_operation(p: OperationParams = Depends(), db: DatabaseManage = Depends(get_database), auth: Auth = Depends(login_auth)): - count = await db.get_count("operation_record", **params.to_count()) - datas = await db.get_datas("operation_record", schema=schemas.OpertionRecordSimpleOut, **params.dict()) + count = await db.get_count("operation_record", **p.to_count()) + datas = await db.get_datas("operation_record", v_schema=schemas.OpertionRecordSimpleOut, **p.dict()) return SuccessResponse(datas, count=count) @app.get("/sms/send/list/", summary="获取短信发送列表") -async def get_sms_send_list(params: SMSParams = Depends(), auth: Auth = Depends(login_auth)): - datas = await crud.SMSSendRecordDal(auth.db).get_datas(**params.dict()) - count = await crud.SMSSendRecordDal(auth.db).get_count(**params.to_count()) +async def get_sms_send_list(p: SMSParams = Depends(), auth: Auth = Depends(login_auth)): + datas = await crud.SMSSendRecordDal(auth.db).get_datas(**p.dict()) + count = await crud.SMSSendRecordDal(auth.db).get_count(**p.to_count()) return SuccessResponse(datas, count=count) diff --git a/kinit-api/apps/vadmin/system/crud.py b/kinit-api/apps/vadmin/system/crud.py index e86bb02..3193f60 100644 --- a/kinit-api/apps/vadmin/system/crud.py +++ b/kinit-api/apps/vadmin/system/crud.py @@ -30,8 +30,8 @@ class DictTypeDal(DalBase): """ data = {} for dict_type in dict_types: - dict_data = await DictTypeDal(self.db).\ - get_data(dict_type=dict_type, v_return_none=True, options=[self.model.details]) + dict_data = await DictTypeDal(self.db)\ + .get_data(dict_type=dict_type, v_return_none=True, v_options=[self.model.details]) if not dict_data: data[dict_type] = [] continue @@ -105,8 +105,14 @@ class SettingsTabDal(DalBase): """ model = models.VadminSystemSettingsTab options = [model.settings] - datas = await self.get_datas(limit=0, options=options, classify=("in", classify), disabled=False, - v_return_objs=True, hidden=hidden) + datas = await self.get_datas( + limit=0, + v_options=options, + classify=("in", classify), + disabled=False, + hidden=hidden, + v_return_objs=True + ) result = {} for tab in datas: tabs = {} @@ -115,4 +121,3 @@ class SettingsTabDal(DalBase): tabs[item.config_key] = item.config_value result[tab.tab_name] = tabs return result - diff --git a/kinit-api/apps/vadmin/system/views.py b/kinit-api/apps/vadmin/system/views.py index 1fecc7e..5c28575 100644 --- a/kinit-api/apps/vadmin/system/views.py +++ b/kinit-api/apps/vadmin/system/views.py @@ -27,9 +27,9 @@ app = APIRouter() # 字典类型管理 ########################################################### @app.get("/dict/types/", summary="获取字典类型列表") -async def get_dict_types(params: DictTypeParams = Depends(), auth: Auth = Depends(login_auth)): - datas = await crud.DictTypeDal(auth.db).get_datas(**params.dict()) - count = await crud.DictTypeDal(auth.db).get_count(**params.to_count()) +async def get_dict_types(p: DictTypeParams = Depends(), auth: Auth = Depends(login_auth)): + datas = await crud.DictTypeDal(auth.db).get_datas(**p.dict()) + count = await crud.DictTypeDal(auth.db).get_count(**p.to_count()) return SuccessResponse(datas, count=count) @@ -45,16 +45,17 @@ async def delete_dict_types(ids: IdList = Depends(), auth: Auth = Depends(login_ @app.post("/dict/types/details/", summary="获取多个字典类型下的字典元素列表") -async def post_dicts_details(auth: Auth = Depends(login_auth), - dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表")): +async def post_dicts_details( + auth: Auth = Depends(login_auth), + dict_types: List[str] = Body(None, title="字典元素列表", description="查询字典元素列表") +): datas = await crud.DictTypeDal(auth.db).get_dicts_details(dict_types) return SuccessResponse(datas) @app.get("/dict/types/options/", summary="获取字典类型选择项") async def get_dicts_options(auth: Auth = Depends(login_auth)): - datas = await crud.DictTypeDal(auth.db).get_select_datas() - return SuccessResponse(datas) + return SuccessResponse(await crud.DictTypeDal(auth.db).get_select_datas()) @app.put("/dict/types/{data_id}/", summary="更新字典类型") @@ -65,7 +66,7 @@ async def put_dict_types(data_id: int, data: schemas.DictType, auth: Auth = Depe @app.get("/dict/types/{data_id}/", summary="获取字典类型详细") async def get_dict_type(data_id: int, auth: Auth = Depends(login_auth)): schema = schemas.DictTypeSimpleOut - return SuccessResponse(await crud.DictTypeDal(auth.db).get_data(data_id, None, schema)) + return SuccessResponse(await crud.DictTypeDal(auth.db).get_data(data_id, None, v_schema=schema)) ########################################################### @@ -99,7 +100,7 @@ async def put_dict_details(data_id: int, data: schemas.DictDetails, auth: Auth = @app.get("/dict/details/{data_id}/", summary="获取字典元素详情") async def get_dict_detail(data_id: int, auth: Auth = Depends(login_auth)): schema = schemas.DictDetailsSimpleOut - return SuccessResponse(await crud.DictDetailsDal(auth.db).get_data(data_id, None, schema)) + return SuccessResponse(await crud.DictDetailsDal(auth.db).get_data(data_id, None, v_schema=schema)) ########################################################### diff --git a/kinit-api/core/crud.py b/kinit-api/core/crud.py index 682ec7b..d4ddba1 100644 --- a/kinit-api/core/crud.py +++ b/kinit-api/core/crud.py @@ -13,6 +13,7 @@ # SQLAlchemy join 内连接 # selectinload 官方文档: # https://www.osgeo.cn/sqlalchemy/orm/loading_relationships.html?highlight=selectinload#sqlalchemy.orm.selectinload + import datetime from typing import List from fastapi import HTTPException @@ -24,133 +25,156 @@ from sqlalchemy.orm import selectinload from starlette import status from core.logger import logger from sqlalchemy.sql.selectable import Select +from typing import Any class DalBase: - def __init__(self, db: AsyncSession, model, schema, key_models: dict = None): + def __init__(self, db: AsyncSession, model: Any, schema: Any, key_models: dict = None): self.db = db self.model = model self.schema = schema self.key_models = key_models - async def get_data(self, data_id: int = None, options: list = None, schema=None, keys: dict = None, **kwargs): + async def get_data( + self, + data_id: int = None, + v_options: list = None, + v_join_query: dict = None, + v_order: str = None, + v_return_none: bool = False, + v_schema: Any = None, + **kwargs + ): """ 获取单个数据,默认使用 ID 查询,否则使用关键词查询 - @param data_id: - @param keys: 外键字段查询,内连接 - @param options: 指示应使用select在预加载中加载给定的属性。 - @param schema: 指定使用的序列化对象 - @param kwargs: 关键词参数, - @param kwargs: v_order,排序,默认正序,为 desc 是倒叙 - @param kwargs: v_return_none,是否返回空 None,否认 抛出异常,默认抛出异常 + @param data_id: 数据 ID + @param v_join_query: 外键字段查询,内连接 + @param v_options: 指示应使用select在预加载中加载给定的属性。 + @param v_schema: 指定使用的序列化对象 + @param v_order: 排序,默认正序,为 desc 是倒叙 + @param v_return_none: 是否返回空 None,否认 抛出异常,默认抛出异常 + @param kwargs: 查询参数 """ - order = kwargs.pop("v_order", None) - return_none = kwargs.pop("v_return_none", False) - keys_exist = False - if keys: - for key, value in keys.items(): - if value and isinstance(value, dict): - for k, v in value.items(): - if v: - keys_exist = True - break - kwargs_exist = False - if kwargs: - for key, value in kwargs.items(): - if value and getattr(self.model, key, None): - kwargs_exist = True - break sql = select(self.model).where(self.model.delete_datetime.is_(None)) - if data_id or kwargs_exist or keys_exist: - if data_id: - sql = sql.where(self.model.id == data_id) - sql = self.add_filter_condition(sql, keys, options, **kwargs) - if order and (order == "desc" or order == "descending"): + if data_id: + sql = sql.where(self.model.id == data_id) + sql = self.add_filter_condition(sql, v_join_query, v_options, **kwargs) + if v_order and (v_order == "desc" or v_order == "descending"): sql = sql.order_by(self.model.create_datetime.desc()) queryset = await self.db.execute(sql) data = queryset.scalars().first() - if not data and return_none: + if not data and v_return_none: return None - if data and schema: - return schema.from_orm(data).dict() + if data and v_schema: + return v_schema.from_orm(data).dict() if data: return data raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="未找到此数据") - async def get_datas(self, page: int = 1, limit: int = 10, keys: dict = None, options: list = None, schema=None, - **kwargs): + async def get_datas( + self, + page: int = 1, + limit: int = 10, + v_join_query: dict = None, + v_options: list = None, + v_order: str = None, + v_order_field: str = None, + v_return_objs: bool = False, + v_start_sql: Any = None, + v_schema: Any = None, + **kwargs + ): """ 获取数据列表 - @param page: 页码 @param limit: 当前页数据量 - @param keys: 外键字段查询 - @param options: 指示应使用select在预加载中加载给定的属性。 - @param schema: 指定使用的序列化对象 - @param kwargs: v_order,排序,默认正序,为 desc 是倒叙 - @param kwargs: v_order_field,排序字段 - @param kwargs: v_return_objs,是否返回对象 - @param kwargs: v_start_sql,初始 sql + @param v_join_query: 外键字段查询 + @param v_options: 指示应使用select在预加载中加载给定的属性。 + @param v_schema: 指定使用的序列化对象 + @param v_order: 排序,默认正序,为 desc 是倒叙 + @param v_order_field: 排序字段 + @param v_return_objs: 是否返回对象 + @param v_start_sql: 初始 sql + @param kwargs: 查询参数 """ - order = kwargs.pop("v_order", None) - order_field = kwargs.pop("v_order_field", None) - return_objs = kwargs.pop("v_return_objs", False) - start_sql = kwargs.pop("v_start_sql", None) - if not isinstance(start_sql, Select): - start_sql = select(self.model).where(self.model.delete_datetime.is_(None)) - sql = self.add_filter_condition(start_sql, keys, options, **kwargs) - if order_field and (order == "desc" or order == "descending"): - sql = sql.order_by(getattr(self.model, order_field).desc(), self.model.id.desc()) - elif order_field: - sql = sql.order_by(getattr(self.model, order_field), self.model.id) - elif order == "desc" or order == "descending": + if not isinstance(v_start_sql, Select): + v_start_sql = select(self.model).where(self.model.delete_datetime.is_(None)) + sql = self.add_filter_condition(v_start_sql, v_join_query, v_options, **kwargs) + if v_order_field and (v_order == "desc" or v_order == "descending"): + sql = sql.order_by(getattr(self.model, v_order_field).desc(), self.model.id.desc()) + elif v_order_field: + sql = sql.order_by(getattr(self.model, v_order_field), self.model.id) + elif v_order == "desc" or v_order == "descending": sql = sql.order_by(self.model.id.desc()) if limit != 0: sql = sql.offset((page - 1) * limit).limit(limit) queryset = await self.db.execute(sql) - if return_objs: + if v_return_objs: return queryset.scalars().all() - if schema: - return [schema.from_orm(i).dict() for i in queryset.scalars().all()] + if v_schema: + return [v_schema.from_orm(i).dict() for i in queryset.scalars().all()] return [self.out_dict(i) for i in queryset.scalars().all()] - async def get_count(self, keys: dict = None, **kwargs): - """获取数据总数""" + async def get_count(self, v_join_query: dict = None, v_options: list = None, **kwargs): + """ + 获取数据总数 + @param v_join_query: 外键字段查询 + @param v_options: 指示应使用select在预加载中加载给定的属性。 + @param kwargs: 查询参数 + """ sql = select(func.count(self.model.id).label('total')).where(self.model.delete_datetime.is_(None)) - sql = self.add_filter_condition(sql, keys, **kwargs) + sql = self.add_filter_condition(sql, v_join_query, v_options, **kwargs) queryset = await self.db.execute(sql) return queryset.one()['total'] - async def create_data(self, data, return_obj: bool = False, options: list = None, schema=None): - """创建数据""" + async def create_data(self, data, v_options: list = None, v_return_obj: bool = False, v_schema: Any = None): + """ + 创建数据 + @param data: 创建数据 + @param v_options: 指示应使用select在预加载中加载给定的属性。 + @param v_schema: ,指定使用的序列化对象 + @param v_return_obj: ,是否返回对象 + """ if isinstance(data, dict): obj = self.model(**data) else: obj = self.model(**data.dict()) await self.flush(obj) - if options: - obj = await self.get_data(obj.id, options=options) - if return_obj: + if v_options: + obj = await self.get_data(obj.id, v_options=v_options) + if v_return_obj: return obj - if schema: - return schema.from_orm(obj).dict() + if v_schema: + return v_schema.from_orm(obj).dict() return self.out_dict(obj) - async def put_data(self, data_id: int, data, return_obj: bool = False, options: list = None, schema=None): + async def put_data( + self, + data_id: int, + data: Any, + v_options: list = None, + v_return_obj: bool = False, + v_schema: Any = None + ): """ 更新单个数据 + @param data_id: 修改行数据的 ID + @param data: 数据内容 + @param v_options: 指示应使用select在预加载中加载给定的属性。 + @param v_return_obj: ,是否返回对象 + @param v_schema: ,指定使用的序列化对象 """ - obj = await self.get_data(data_id, options=options) + obj = await self.get_data(data_id, v_options=v_options) obj_dict = jsonable_encoder(data) for key, value in obj_dict.items(): setattr(obj, key, value) await self.flush(obj) - if return_obj: + if v_return_obj: return obj - if schema: - return schema.from_orm(obj).dict() + if v_schema: + return v_schema.from_orm(obj).dict() return self.out_dict(obj) async def delete_datas(self, ids: List[int], soft: bool = False): @@ -160,67 +184,72 @@ class DalBase: @param soft: 是否执行软删除 """ if soft: - await self.db.execute(update(self.model).where(self.model.id.in_(ids)). - values(delete_datetime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + await self.db.execute( + update(self.model) + .where(self.model.id.in_(ids)) + .values(delete_datetime=datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + ) else: await self.db.execute(delete(self.model).where(self.model.id.in_(ids))) - def add_filter_condition(self, sql: select, keys: dict = None, options: list = None, **kwargs) -> select: + def add_filter_condition(self, sql: select, v_join_query: dict = None, v_options: list = None, **kwargs) -> select: """ 添加过滤条件,以及内连接过滤条件 @param sql: - @param keys: 外键字段查询,内连接 - @param options: 指示应使用select在预加载中加载给定的属性。 + @param v_join_query: 外键字段查询,内连接 + @param v_options: 指示应使用select在预加载中加载给定的属性。 @param kwargs: 关键词参数 """ - if keys and self.key_models: - for key, value in keys.items(): - model = self.key_models.get(key) - if model: - sql = sql.join(model) + if v_join_query and self.key_models: + for key, value in v_join_query.items(): + foreign_key = self.key_models.get(key) + if foreign_key and foreign_key.get("model"): + # 当外键模型在查询模型中存在多个外键时,则需要添加onclause属性 + sql = sql.join(foreign_key.get("model"), onclause=foreign_key.get("onclause")) for v_key, v_value in value.items(): if v_value is not None and v_value != "": - v_attr = getattr(model, v_key, None) + v_attr = getattr(foreign_key.get("model"), v_key, None) sql = self.filter_condition(sql, v_attr, v_value) else: logger.error(f"外键查询报错:{key}模型不存在,无法进行下一步查询。") - elif keys and not self.key_models: + elif v_join_query and not self.key_models: logger.error(f"外键查询报错:key_models 外键模型无配置项,无法进行下一步查询。") for field in kwargs: value = kwargs.get(field) if value is not None and value != "": attr = getattr(self.model, field, None) sql = self.filter_condition(sql, attr, value) - if options: - sql = sql.options(*[selectinload(i) for i in options]) + if v_options: + sql = sql.options(*[selectinload(i) for i in v_options]) return sql @classmethod - def filter_condition(cls, sql, attr, value): + def filter_condition(cls, sql: Any, attr: Any, value: Any): """ 过滤条件 """ if not attr: return sql if isinstance(value, tuple): - if value[0] == "date" and value[1]: - # 根据日期查询, 关键函数是:func.time_format和func.date_format - sql = sql.where(func.date_format(attr, "%Y-%m-%d") == value[1]) - elif value[0] == "like" and value[1]: - sql = sql.where(attr.like(f"%{value[1]}%")) - elif value[0] == "or" and value[1]: - sql = sql.where(or_(i for i in value[1])) - elif value[0] == "in" and value[1]: - sql = sql.where(attr.in_(value[1])) - elif value[0] == "between" and value[1]: - sql = sql.where(attr.between(value[1][0], value[1][1])) - elif value[0] == "month" and value[1]: - sql = sql.where(func.date_format(attr, "%Y-%m") == value[1]) + if value[1]: + if value[0] == "date": + # 根据日期查询, 关键函数是:func.time_format和func.date_format + sql = sql.where(func.date_format(attr, "%Y-%m-%d") == value[1]) + elif value[0] == "like": + sql = sql.where(attr.like(f"%{value[1]}%")) + elif value[0] == "or": + sql = sql.where(or_(i for i in value[1])) + elif value[0] == "in": + sql = sql.where(attr.in_(value[1])) + elif value[0] == "between": + sql = sql.where(attr.between(value[1][0], value[1][1])) + elif value[0] == "month": + sql = sql.where(func.date_format(attr, "%Y-%m") == value[1]) else: sql = sql.where(attr == value) return sql - async def flush(self, obj=None): + async def flush(self, obj: Any = None): """ 刷新到数据库 """ @@ -230,7 +259,7 @@ class DalBase: if obj: await self.db.refresh(obj) - def out_dict(self, data): + def out_dict(self, data: Any): """ 序列化 @param data: diff --git a/kinit-api/core/data_types.py b/kinit-api/core/data_types.py index 4c28dfb..e67a0df 100644 --- a/kinit-api/core/data_types.py +++ b/kinit-api/core/data_types.py @@ -35,3 +35,16 @@ class Telephone(str): @classmethod def validate(cls, v): return vali_telephone(v) + + +class DateStr(str): + + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, v): + if isinstance(v, str): + return v + return v.strftime("%Y-%m-%d") diff --git a/kinit-api/core/exception.py b/kinit-api/core/exception.py index 2646c30..cbc5371 100644 --- a/kinit-api/core/exception.py +++ b/kinit-api/core/exception.py @@ -108,8 +108,8 @@ def register_exception(app: FastAPI): status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=jsonable_encoder( { - "message": "接口异常!" - , "code": status.HTTP_500_INTERNAL_SERVER_ERROR + "message": "接口异常!", + "code": status.HTTP_500_INTERNAL_SERVER_ERROR } ), ) diff --git a/kinit-api/core/mongo/database_manage.py b/kinit-api/core/mongo/database_manage.py index b75d123..1bcbc5b 100644 --- a/kinit-api/core/mongo/database_manage.py +++ b/kinit-api/core/mongo/database_manage.py @@ -1,4 +1,5 @@ from abc import abstractmethod +from typing import Any class DatabaseManage: @@ -29,7 +30,16 @@ class DatabaseManage: pass @abstractmethod - async def get_datas(self, collection: str, page: int = 1, limit: int = 10, schema=None, **kwargs): + async def get_datas( + self, + collection: str, + page: int = 1, + limit: int = 10, + v_schema: Any = None, + v_order: str = None, + v_order_field: str = None, + **kwargs + ): pass @abstractmethod diff --git a/kinit-api/core/mongo/mongo_manage.py b/kinit-api/core/mongo/mongo_manage.py index b1ee460..40f1acf 100644 --- a/kinit-api/core/mongo/mongo_manage.py +++ b/kinit-api/core/mongo/mongo_manage.py @@ -1,4 +1,6 @@ import json +from typing import Any + from bson.json_util import dumps from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase from core.mongo import DatabaseManage @@ -30,15 +32,20 @@ class MongoManage(DatabaseManage): async def create_data(self, collection: str, data: dict) -> InsertOneResult: return await self.db[collection].insert_one(data) - async def get_datas(self, collection: str, schema: BaseModel = None, **kwargs): + async def get_datas( + self, + collection: str, + page: int = 1, + limit: int = 10, + v_schema: Any = None, + v_order: str = None, + v_order_field: str = None, + **kwargs + ): """ 使用 find() 要查询的一组文档。 find() 没有I / O,也不需要 await 表达式。它只是创建一个 AsyncIOMotorCursor 实例 当您调用 to_list() 或为循环执行异步时 (async for) ,查询实际上是在服务器上执行的。 """ - page = kwargs.pop("page", 1) - limit = kwargs.pop("limit", 10) - order = kwargs.pop("v_order", None) - order_field = kwargs.pop("v_order_field", None) params = self.filter_condition(**kwargs) cursor = self.db[collection].find(params) @@ -50,8 +57,8 @@ class MongoManage(DatabaseManage): async for row in cursor: del row['_id'] data = json.loads(dumps(row)) - if schema: - data = schema.parse_obj(data).dict() + if v_schema: + data = v_schema.parse_obj(data).dict() datas.append(data) return datas diff --git a/kinit-api/utils/excel/excel_manage.py b/kinit-api/utils/excel/excel_manage.py index 9a5725b..c5457b0 100644 --- a/kinit-api/utils/excel/excel_manage.py +++ b/kinit-api/utils/excel/excel_manage.py @@ -156,7 +156,8 @@ class ExcelManage: @param max_column: 最大列 """ for index in range(0, max_column): - # 设置单元格对齐方式 Alignment(horizontal=水平对齐模式,vertical=垂直对齐模式,text_rotation=旋转角度,wrap_text=是否自动换行) + # 设置单元格对齐方式 + # Alignment(horizontal=水平对齐模式,vertical=垂直对齐模式,text_rotation=旋转角度,wrap_text=是否自动换行) alignment = Alignment(horizontal='center', vertical='center', text_rotation=0, wrap_text=False) self.sheet.cell(row=row, column=index+1).alignment = alignment diff --git a/kinit-api/utils/ip_manage.py b/kinit-api/utils/ip_manage.py index c08f2de..5f635af 100644 --- a/kinit-api/utils/ip_manage.py +++ b/kinit-api/utils/ip_manage.py @@ -39,7 +39,7 @@ class IPManage: def __init__(self, ip: str): self.ip = ip - self.url = f"https://api.ip138.com/ip/?ip={ip}&datatype=jsonp&token={IP_PARSE_TOKEN}" + self.url = f"http://api.ip138.com/ip/?ip={ip}&datatype=jsonp&token={IP_PARSE_TOKEN}" async def parse(self): """