"""统一响应模型。 所有接口响应体形如 `{code, message, data}`(PROJECT_STANDARDS 第 3 节)。 分页响应字段为 `list/total/pageNum/pageSize`(同规范第 14 节)。 字段名采用驼峰(规范约定)而 Python 属性用蛇形(语言惯例), 两者通过 pydantic 的 alias 映射,序列化时输出驼峰。 """ from pydantic import BaseModel, ConfigDict, Field from app.core.constants import CODE_SUCCESS class ApiResponse[T](BaseModel): """统一响应包装。泛型参数为 `data` 的类型。""" code: int = Field(default=CODE_SUCCESS, description="状态码,与 HTTP 状态码一致") message: str = Field(default="success", description="提示信息") data: T | None = Field(default=None, description="业务数据") def ok[T](data: T | None = None, message: str = "success") -> ApiResponse[T]: """构造成功响应。 统一出口便于日后调整成功码或附加字段时只改一处。 """ return ApiResponse(code=CODE_SUCCESS, message=message, data=data) class PageResult[T](BaseModel): """分页结果。 Python 侧使用蛇形属性名,序列化后输出规范要求的驼峰字段名。 """ model_config = ConfigDict(populate_by_name=True) records: list[T] = Field( default_factory=list, alias="list", serialization_alias="list", description="当前页数据" ) total: int = Field(default=0, description="总记录数") page_num: int = Field( default=1, alias="pageNum", serialization_alias="pageNum", description="当前页码" ) page_size: int = Field( default=10, alias="pageSize", serialization_alias="pageSize", description="每页条数" ) class PageQuery(BaseModel): """分页查询入参基类。业务查询参数继承本类扩展。""" model_config = ConfigDict(populate_by_name=True) page_num: int = Field(default=1, ge=1, alias="pageNum", description="页码,从 1 开始") page_size: int = Field(default=10, ge=1, le=200, alias="pageSize", description="每页条数") @property def offset(self) -> int: """转换为 SQL OFFSET。""" return (self.page_num - 1) * self.page_size