47 lines
1.1 KiB
Python
47 lines
1.1 KiB
Python
"""通用分页与过滤工具,供各 API 路由复用。"""
|
||
from typing import Generic, TypeVar
|
||
|
||
from fastapi import Query
|
||
from pydantic import BaseModel, computed_field
|
||
|
||
T = TypeVar("T")
|
||
|
||
|
||
class PaginationParams:
|
||
"""依赖注入式分页参数(可直接用于 Depends)。"""
|
||
|
||
def __init__(
|
||
self,
|
||
page: int = Query(1, ge=1, description="页码,从 1 开始"),
|
||
page_size: int = Query(20, ge=1, le=100, description="每页条数"),
|
||
):
|
||
self.page = page
|
||
self.page_size = page_size
|
||
|
||
@property
|
||
def offset(self) -> int:
|
||
return (self.page - 1) * self.page_size
|
||
|
||
@property
|
||
def limit(self) -> int:
|
||
return self.page_size
|
||
|
||
|
||
class PaginatedResponse(BaseModel, Generic[T]):
|
||
"""通用分页响应结构。"""
|
||
|
||
items: list[T]
|
||
total: int
|
||
page: int
|
||
page_size: int
|
||
|
||
@computed_field # type: ignore[misc]
|
||
@property
|
||
def total_pages(self) -> int:
|
||
if self.page_size == 0:
|
||
return 0
|
||
import math
|
||
return math.ceil(self.total / self.page_size)
|
||
|
||
model_config = {"from_attributes": True}
|