2026-02-01 17:28:00 +08:00
|
|
|
|
from collections.abc import Sequence
|
2025-12-10 20:52:44 +08:00
|
|
|
|
from dataclasses import asdict, dataclass
|
2026-01-30 03:10:21 +08:00
|
|
|
|
from typing import Any, TypeVar
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
2026-03-03 22:04:40 +08:00
|
|
|
|
from sqlalchemy import func
|
2025-12-10 20:52:44 +08:00
|
|
|
|
from sqlalchemy.orm import Query
|
|
|
|
|
|
|
|
|
|
|
|
T = TypeVar("T")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
|
class PaginationMeta:
|
|
|
|
|
|
total: int
|
|
|
|
|
|
limit: int
|
|
|
|
|
|
offset: int
|
|
|
|
|
|
count: int
|
|
|
|
|
|
|
|
|
|
|
|
def to_dict(self) -> dict:
|
|
|
|
|
|
return asdict(self)
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-30 03:10:21 +08:00
|
|
|
|
def paginate_query(query: Query, limit: int, offset: int) -> tuple[int, list[T]]:
|
2025-12-10 20:52:44 +08:00
|
|
|
|
"""
|
|
|
|
|
|
对 SQLAlchemy 查询应用 limit/offset,并返回总数与结果列表。
|
|
|
|
|
|
"""
|
2026-03-03 22:04:40 +08:00
|
|
|
|
total = int(query.order_by(None).with_entities(func.count()).scalar() or 0)
|
2025-12-10 20:52:44 +08:00
|
|
|
|
records = query.offset(offset).limit(limit).all()
|
|
|
|
|
|
return total, records
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def paginate_sequence(
|
|
|
|
|
|
items: Sequence[T], limit: int, offset: int
|
2026-01-30 03:10:21 +08:00
|
|
|
|
) -> tuple[list[T], PaginationMeta]:
|
2025-12-10 20:52:44 +08:00
|
|
|
|
"""
|
|
|
|
|
|
对内存序列应用分页,返回切片和元数据。
|
|
|
|
|
|
"""
|
|
|
|
|
|
total = len(items)
|
|
|
|
|
|
sliced = list(items[offset : offset + limit])
|
|
|
|
|
|
meta = PaginationMeta(total=total, limit=limit, offset=offset, count=len(sliced))
|
|
|
|
|
|
return sliced, meta
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-30 03:10:21 +08:00
|
|
|
|
def build_pagination_payload(items: list[dict], meta: PaginationMeta, **extra: Any) -> dict:
|
2025-12-10 20:52:44 +08:00
|
|
|
|
"""
|
|
|
|
|
|
构建标准分页响应 payload。
|
|
|
|
|
|
"""
|
2025-12-15 14:30:53 +08:00
|
|
|
|
payload: dict = {"items": items, "meta": meta.to_dict()}
|
2025-12-10 20:52:44 +08:00
|
|
|
|
payload.update(extra)
|
|
|
|
|
|
return payload
|