Files
xiaoxia-saas/packages/application/common/pagination.py
T
2026-06-21 06:52:19 +08:00

103 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
通用分页器
"""
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,
)