# -*- coding = utf-8 -*- # @Time : 2025/2/13 下午12:18 # @File : crud.py # @Software : PyCharm # @Author : xingc # @Desc : from datetime import datetime from typing import List, Optional, Union, NewType, Dict, Any, Tuple from sqlalchemy import func, desc from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select from .models import User, Card, SearchCardCache Model = NewType('Model', Union[User, Card, SearchCardCache]) class CRUD: def __init__(self, session: AsyncSession, model: Model): self.session = session self.model = model async def get(self, filters: dict) -> Optional[Model]: async with self.session.begin(): result = await self.session.execute(select(self.model).filter( *self.build_filter_conditions(filters)) ) record = result.scalar_one_or_none() return record async def get_paginated(self, filters: dict, page: int = 1, page_size: int = 10) -> Tuple[List[Model], int]: async with self.session.begin(): filter_conditions = self.build_filter_conditions(filters) if filters else [] query = select(self.model).filter(*filter_conditions).order_by(desc(self.model.created_at)) total_query = select(func.count()).select_from(self.model).filter(*filter_conditions) total_result = await self.session.execute(total_query) total = total_result.scalar() query = query.limit(page_size).offset((page - 1) * page_size) result = await self.session.execute(query) records = result.scalars().all() return records, total async def get_grouped_count(self, group_by_field: str, filters: Dict[str, Any] = None) -> Dict[Any, int]: filter_conditions = self.build_filter_conditions(filters) if filters else [] group_by = getattr(self.model, group_by_field) query = select(group_by, func.count()).filter(*filter_conditions).group_by(group_by) async with self.session.begin(): result = await self.session.execute(query) records = result.all() return {record[0]: record[1] for record in records} async def create(self, data: dict, filters: dict) -> Union[Model, bool]: async with self.session.begin(): result = await self.session.execute(select(self.model).filter( *self.build_filter_conditions(filters)) ) if result.scalars().first() is not None: return False record = self.model(**data) self.session.add(record) await self.session.commit() return record async def get_all(self, filters: dict = None) -> List[Model]: async with self.session.begin(): result = await self.session.execute( select(self.model) if filters is None else select(self.model).filter( *self.build_filter_conditions(filters)).order_by(desc(self.model.insert_time)) ) records = result.scalars().all() return records async def update(self, data: dict, filters: dict) -> Optional[Model]: async with self.session.begin(): result = await self.session.execute(select(self.model).filter( *self.build_filter_conditions(filters)) ) record = result.scalars().first() if not record: return None count = 0 for key, value in data.items(): if any([ value is None, value == getattr(record, key), key in ['created_at', 'updated_at'] ]): continue setattr(record, key, value) count += 1 if count > 0: await self.session.commit() return record async def updates(self, data: dict, filters: dict) -> Optional[Model]: async with self.session.begin(): result = await self.session.execute(select(self.model).filter( *self.build_filter_conditions(filters)) ) records = result.scalars().all() if len(records) == 0: return None count = 0 for record in records: for key, value in data.items(): if any([ value is None, value == getattr(record, key), key in ['insert_time', 'update_time'] ]): continue setattr(record, key, value) count += 1 if count > 0: await self.session.commit() return records async def delete(self, filters: dict) -> bool: async with self.session.begin(): result = await self.session.execute(select(self.model).filter( *self.build_filter_conditions(filters)) ) record = result.scalars().first() if not record: return False await self.session.delete(record) await self.session.commit() return True def build_filter_conditions(self, filters: Dict[str, Any]) -> tuple: conditions = [] for key, value in filters.items(): if any([ value is None, value == '', ]): continue if isinstance(value, datetime) and value.tzinfo is not None: value = value.astimezone() # 解析字段名和操作符 field_name, operator = key, None if '__' in key: field_name, operator = key.split('__', 1) else: field_name = key field = getattr(self.model, field_name) if operator == 'like': # 模糊匹配 conditions.append(field.like(f'%{value}%')) elif operator == 'gt': # 大于 conditions.append(field > value) elif operator == 'lt': # 小于 conditions.append(field < value) elif operator == 'eq': # 等于 conditions.append(field == value) elif operator == 'ne': # 不等于 conditions.append(field != value) elif operator == 'in': # 在集合中 conditions.append(field.in_(value)) else: # 如果没有匹配的操作符,默认为等于 conditions.append(field == value) return tuple(conditions)