"""ابزارهای صفحه‌بندی مشترک (offset/limit). از این مدل‌ها در همه سرویس‌ها برای پاسخ‌های لیستی استفاده می‌شود. """ from __future__ import annotations from typing import Generic, Sequence, TypeVar from pydantic import BaseModel, Field T = TypeVar("T") DEFAULT_PAGE_SIZE = 20 MAX_PAGE_SIZE = 500 class PaginationParams(BaseModel): """پارامترهای صفحه‌بندی ورودی.""" page: int = Field(default=1, ge=1, description="شماره صفحه از ۱") page_size: int = Field( default=DEFAULT_PAGE_SIZE, ge=1, le=MAX_PAGE_SIZE, description="تعداد آیتم در هر صفحه", ) @property def offset(self) -> int: return (self.page - 1) * self.page_size @property def limit(self) -> int: return self.page_size class PageMeta(BaseModel): page: int page_size: int total_items: int total_pages: int class Page(BaseModel, Generic[T]): """پاسخ لیستی صفحه‌بندی‌شده.""" items: Sequence[T] meta: PageMeta @classmethod def create( cls, items: Sequence[T], total_items: int, params: PaginationParams, ) -> "Page[T]": total_pages = (total_items + params.page_size - 1) // params.page_size if params.page_size else 0 return cls( items=items, meta=PageMeta( page=params.page, page_size=params.page_size, total_items=total_items, total_pages=total_pages, ), )