""" 通用分页器 """ from math import ceil from typing import Generic, List, Optional, TypeVar from pydantic import BaseModel, Field T = TypeVar("T") class PaginationParams(BaseModel): """分页参数""" page: int = Field(1, ge=1, description="页码(从 1 开始)") page_size: int = Field(20, ge=1, le=100, description="每页数量(最大 100)") @property def offset(self) -> int: """计算偏移量""" return (self.page - 1) * self.page_size @property def limit(self) -> int: """返回限制数量""" return self.page_size class PaginationMeta(BaseModel): """分页元数据""" page: int = Field(..., description="当前页码") page_size: int = Field(..., description="每页数量") total: int = Field(..., description="总记录数") total_pages: int = Field(..., description="总页数") has_next: bool = Field(..., description="是否有下一页") has_prev: bool = Field(..., description="是否有上一页") @classmethod def from_params( cls, params: PaginationParams, total: int, ) -> "PaginationMeta": """从参数和总数创建元数据""" total_pages = ceil(total / params.page_size) if total > 0 else 0 return cls( page=params.page, page_size=params.page_size, total=total, total_pages=total_pages, has_next=params.page < total_pages, has_prev=params.page > 1, ) class PaginatedResponse(BaseModel, Generic[T]): """分页响应""" data: List[T] = Field(..., description="数据列表") pagination: PaginationMeta = Field(..., description="分页信息") @classmethod def create( cls, data: List[T], params: PaginationParams, total: int, ) -> "PaginatedResponse[T]": """创建分页响应""" return cls( data=data, pagination=PaginationMeta.from_params(params, total), ) def paginate( items: List[T], params: PaginationParams, ) -> PaginatedResponse[T]: """ 内存分页(适用于 InMemory Repository) Args: items: 完整列表 params: 分页参数 Returns: 分页响应 """ total = len(items) start = params.offset end = start + params.limit page_data = items[start:end] return PaginatedResponse.create( data=page_data, params=params, total=total, )