import json from dataclasses import dataclass from fastapi import Query from app.common.enums import QueueEnum from app.core.validator import DateTimeStr @dataclass class QueryParam: """公共基类 —— 自动按 MRO 链式调用所有 __post_init__,子类无需 super()""" def __init_subclass__(cls, **kwargs: object) -> None: super().__init_subclass__(**kwargs) # 收集 MRO 中除 QueryParam 自身外所有显式定义的 __post_init__ hooks: list = [_unwrap_query_defaults] for base in cls.__mro__: fn = base.__dict__.get("__post_init__") if callable(fn) and fn is not QueryParam.__post_init__: hooks.append(fn) hooks.reverse() # MRO 顺序(_unwrap → 父类 → 子类) def chained_post_init(self: object) -> None: for hook in hooks: hook(self) cls.__post_init__ = chained_post_init def __post_init__(self) -> None: pass def _unwrap_query_defaults(self: object) -> None: """将 @dataclass 字段中残留的 FastAPI Query(...) 默认值替换为 None""" for f in self.__dataclass_fields__: # type: ignore[attr-defined] raw = getattr(self, f) if raw is not None and type(raw).__name__ == "Query": setattr(self, f, None) @dataclass class PaginationQueryParam(QueryParam): """分页 —— 自动继承 page_no / page_size / order_by,子类无需重复声明""" page_no: int = Query(default=1, description="当前页码", ge=1) page_size: int = Query(default=10, description="每页数量", ge=1, le=100) order_by: str | None = Query( default=None, description="排序字段,格式:[{'field1': 'asc'}, {'field2': 'desc'}]", ) def __post_init__(self) -> None: if self.order_by and isinstance(self.order_by, str): try: self.order_by = json.loads(self.order_by) except ValueError: self.order_by = [{"id": "desc"}] else: self.order_by = [{"id": "desc"}] @dataclass class BaseQueryParam(QueryParam): """created_time + updated_time —— 子类自动继承""" created_time: list[DateTimeStr] | None = Query( None, description="创建时间范围", examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"], ) updated_time: list[DateTimeStr] | None = Query( None, description="更新时间范围", examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"], ) def __post_init__(self) -> None: ct = self.created_time if isinstance(ct, list) and len(ct) == 2: self.created_time = (QueueEnum.between.value, (ct[0], ct[1])) ut = self.updated_time if isinstance(ut, list) and len(ut) == 2: self.updated_time = (QueueEnum.between.value, (ut[0], ut[1])) @dataclass class UserByQueryParam(QueryParam): """created_id + updated_id —— 子类自动继承""" created_id: int | None = Query(None, description="创建人") updated_id: int | None = Query(None, description="更新人") def __post_init__(self) -> None: if isinstance(self.created_id, int): self.created_id = (QueueEnum.eq.value, self.created_id) if isinstance(self.updated_id, int): self.updated_id = (QueueEnum.eq.value, self.updated_id) @dataclass class TenantByQueryParam(QueryParam): """tenant_id —— 子类自动继承""" tenant_id: int | None = Query(None, description="租户ID") def __post_init__(self) -> None: if isinstance(self.tenant_id, int): self.tenant_id = (QueueEnum.eq.value, self.tenant_id)