feat(points): 实现积分流水列表功能
- 后端新增 GET /api/v1/points/ledger 接口 - 前端新增积分流水列表页面 - 积分中心添加「查看流水」入口 - 重命名 AccountDeleteScreen 为 AccountDataScreen - 流水列表支持分页加载和空状态展示
This commit is contained in:
@@ -1,18 +1,42 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
|
||||
from core.auth.models import CurrentUser
|
||||
from core.http.errors import ApiProblemError, problem_payload
|
||||
from v1.points.dependencies import get_points_service
|
||||
from v1.points.schemas import PackagesResponse, PackageInfo, PointsBalanceResponse
|
||||
from v1.points.schemas import (
|
||||
PackagesResponse,
|
||||
PackageInfo,
|
||||
PointsBalanceResponse,
|
||||
LedgerListResponse,
|
||||
LedgerItem,
|
||||
)
|
||||
from v1.points.service import PointsService
|
||||
from v1.users.dependencies import get_current_user
|
||||
|
||||
router = APIRouter(prefix="/points", tags=["points"])
|
||||
|
||||
|
||||
def _parse_cursor(cursor: str | None) -> datetime | None:
|
||||
if cursor is None:
|
||||
return None
|
||||
try:
|
||||
return datetime.fromisoformat(cursor.replace("Z", "+00:00"))
|
||||
except ValueError as exc:
|
||||
raise ApiProblemError(
|
||||
status_code=422,
|
||||
detail=problem_payload(
|
||||
code="POINTS_INVALID_CURSOR",
|
||||
detail="Points ledger cursor must be an ISO 8601 datetime",
|
||||
params={"cursor": cursor},
|
||||
),
|
||||
) from exc
|
||||
|
||||
|
||||
@router.get("/balance", response_model=PointsBalanceResponse)
|
||||
async def get_points_balance(
|
||||
service: Annotated[PointsService, Depends(get_points_service)],
|
||||
@@ -55,3 +79,32 @@ async def get_available_packages(
|
||||
for pkg in result.packages
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@router.get("/ledger", response_model=LedgerListResponse)
|
||||
async def get_points_ledger(
|
||||
service: Annotated[PointsService, Depends(get_points_service)],
|
||||
current_user: Annotated[CurrentUser, Depends(get_current_user)],
|
||||
limit: Annotated[int, Query(ge=1, le=100)] = 20,
|
||||
cursor: str | None = None,
|
||||
) -> LedgerListResponse:
|
||||
items, next_cursor, has_more = await service.get_ledger_list(
|
||||
user_id=current_user.id,
|
||||
limit=limit,
|
||||
cursor=_parse_cursor(cursor),
|
||||
)
|
||||
return LedgerListResponse(
|
||||
items=[
|
||||
LedgerItem(
|
||||
id=item.id,
|
||||
direction=item.direction,
|
||||
amount=item.amount,
|
||||
balanceAfter=item.balance_after,
|
||||
changeType=item.change_type,
|
||||
createdAt=item.created_at,
|
||||
)
|
||||
for item in items
|
||||
],
|
||||
nextCursor=next_cursor,
|
||||
hasMore=has_more,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user