a4d214964a
- 数据访问层统一: app/crud/{ad_reward,savings,signin,task,wallet}.py 移入
app/repositories/(与 user.py 同目录),删除 crud/;更新 10 处 import
(api/v1/* + scripts/* + repositories 内部交叉引用 app.crud→app.repositories)
- alembic/versions 9 个迁移文件去掉 hex 前缀,改成可读文件名(如
welfare_tables_coin_account_coin_txn.py);**仅重命名文件**,文件内
revision/down_revision 不动 → 迁移链与已迁移库的 alembic_version 不受影响
(alembic heads/history 验证链完好,单一 head c8d9e0f1a2b3)
- 测试: 37 passed(1 个 coupon 代理失败为连不到 pricebot 上游的环境问题,与本次无关)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
60 lines
1.9 KiB
Python
60 lines
1.9 KiB
Python
"""省钱 endpoint(profile 的「累计帮你省了」「省钱战绩」「省钱明细」)。
|
|
|
|
路由前缀 `/api/v1/savings`:
|
|
GET /summary 累计省下 + 订单数 + 平均每单省
|
|
GET /battle 本周已省 + 超过百分比 + 连续省钱天数
|
|
GET /records 省钱明细(游标分页)
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Query
|
|
|
|
from app.api.deps import CurrentUser, DbSession
|
|
from app.repositories import savings as crud_savings
|
|
from app.schemas.welfare import (
|
|
SavingsBattleOut,
|
|
SavingsRecordOut,
|
|
SavingsRecordPage,
|
|
SavingsSummaryOut,
|
|
)
|
|
|
|
logger = logging.getLogger("shagua.savings")
|
|
|
|
router = APIRouter(prefix="/api/v1/savings", tags=["savings"])
|
|
|
|
|
|
@router.get("/summary", response_model=SavingsSummaryOut, summary="累计帮你省了")
|
|
def summary(user: CurrentUser, db: DbSession) -> SavingsSummaryOut:
|
|
s = crud_savings.get_summary(db, user.id)
|
|
return SavingsSummaryOut(
|
|
total_saved_cents=s.total_saved_cents,
|
|
order_count=s.order_count,
|
|
avg_saved_cents=s.avg_saved_cents,
|
|
)
|
|
|
|
|
|
@router.get("/battle", response_model=SavingsBattleOut, summary="省钱战绩")
|
|
def battle(user: CurrentUser, db: DbSession) -> SavingsBattleOut:
|
|
b = crud_savings.get_battle(db, user.id)
|
|
return SavingsBattleOut(
|
|
week_saved_cents=b.week_saved_cents,
|
|
beat_percent=b.beat_percent,
|
|
streak_days=b.streak_days,
|
|
)
|
|
|
|
|
|
@router.get("/records", response_model=SavingsRecordPage, summary="省钱明细(游标分页)")
|
|
def records(
|
|
user: CurrentUser,
|
|
db: DbSession,
|
|
limit: int = Query(20, ge=1, le=100),
|
|
cursor: int | None = Query(None, description="上一页末条 id"),
|
|
) -> SavingsRecordPage:
|
|
items, next_cursor = crud_savings.list_records(db, user.id, limit=limit, cursor=cursor)
|
|
return SavingsRecordPage(
|
|
items=[SavingsRecordOut.model_validate(it) for it in items],
|
|
next_cursor=next_cursor,
|
|
)
|