feat(compare): 新增只读当日比价额度查询 GET /compare/quota #216

Merged
guke merged 1 commits from feat-error-prompt-unify into main 2026-08-04 16:38:47 +08:00
Member

概述

新增只读接口 GET /api/v1/compare/quota,供客户端「跳外卖 App」型比价入口在点击时前置查询当日比价是否已达上限(100 次/日),超限就地提示、不进入比价流程。

配套客户端 PR:比价/领券异常提示统一 + 4 入口上限拦截(shaguabijia-app-android 同名分支)。

改动

  • app/repositories/comparison.py:新增只读 get_daily_compare_used(db, user_id, reset_at) —— 按 user_id + 北京时间自然日 COUNT,窗口计算逐行复刻写路径 reserve_daily_start,保证前置查询与真发起的 429 gate 口径不漂移。
  • app/schemas/compare_record.py:新增 CompareQuotaOut(exhausted, used, limit)
  • app/api/v1/compare_record.py:新增 GET /quota 端点,硬鉴权 CurrentUser、只读不预占;limit_policy.resolvephone + device_id(与 /compare/start 一致,命中 device 白名单)。

测试

  • pytest tests/test_compare_daily_limit.py8 passed(4 既有 + 4 新增,含 device 白名单 parity 测试,锁定 /quota/start 口径一致)。

合并 / 部署注意 ⚠️

  • 本 PR 应先于客户端 PR 合并 + 部署(客户端点击前置拦截依赖此接口;未部署时客户端 fail-open 放行)。
  • 只读、无副作用、不改写路径逻辑,风险低。
## 概述 新增只读接口 `GET /api/v1/compare/quota`,供客户端「跳外卖 App」型比价入口在点击时**前置查询当日比价是否已达上限**(100 次/日),超限就地提示、不进入比价流程。 配套客户端 PR:比价/领券异常提示统一 + 4 入口上限拦截(shaguabijia-app-android 同名分支)。 ## 改动 - `app/repositories/comparison.py`:新增只读 `get_daily_compare_used(db, user_id, reset_at)` —— 按 user_id + 北京时间自然日 COUNT,**窗口计算逐行复刻写路径 `reserve_daily_start`**,保证前置查询与真发起的 429 gate 口径不漂移。 - `app/schemas/compare_record.py`:新增 `CompareQuotaOut(exhausted, used, limit)`。 - `app/api/v1/compare_record.py`:新增 `GET /quota` 端点,硬鉴权 `CurrentUser`、只读不预占;`limit_policy.resolve` 传 `phone + device_id`(与 `/compare/start` 一致,命中 device 白名单)。 ## 测试 - `pytest tests/test_compare_daily_limit.py`:**8 passed**(4 既有 + 4 新增,含 device 白名单 parity 测试,锁定 `/quota` 与 `/start` 口径一致)。 ## 合并 / 部署注意 ⚠️ - 本 PR 应**先于客户端 PR 合并 + 部署**(客户端点击前置拦截依赖此接口;未部署时客户端 fail-open 放行)。 - 只读、无副作用、不改写路径逻辑,风险低。
guke added 1 commit 2026-08-04 14:50:30 +08:00
Author
Member

🤖 review-pr 深审结论

🟢 可合并 — 只读当日额度查询接口,计数口径与 /compare/start 严格同源;实跑测试全绿。置信度 0.9。

契约

GET /api/v1/compare/quota?device_id=<可选>(需登录)→ {exhausted: bool, used: int, limit: int|null}exhausted=true 即已达今日上限;limit=null 表示无限制(设备白名单)。

核对要点

  • 口径一致性(关键)get_daily_compare_usedreserve_daily_start 的计数逐行一致——同 day_start/day_end(北京自然日 naive)、同 reset_atmax(当日0点, reset_at)、同 where(user_id, created_at∈[day_start,day_end))都不按 status 过滤(发起即计数、含 failed)。created_at 全程存 naive 北京时间,比较口径对齐,无 8 小时时区错位。
  • 边界exhausted = limit is not None and used >= limit,与 reserveused >= limit → raise 完全一致。
  • 只读快照语义:不预占,强一致仍由 /start 的行锁保证;/quota 仅作前端①④入口点击的前置 toast 预检⚠️ 并发下可能出现 /quota 报未满但 /start 时已满——属只读预检固有语义,前端仍需兜底处理 /start 的超限响应(见配套前端 PR #385)。

构建/测试(已实跑)

.venv py3.12.7 + pytest:pytest tests/test_compare_daily_limit.py8 passed in 1.75s(4 个新 quota 用例:新用户/已超限/昨日不计入/设备白名单 parity + 4 个既有 daily_limit 用例,均过;测试库为隔离的临时 sqlite)。

正面

口径复刻严谨、时区处理正确、测试覆盖了新用户/超限/跨日/设备白名单四类关键场景,schema 字段有清晰 description。

🤖 自动深审 · 结论仅供参考,请以人工判断为准

## 🤖 review-pr 深审结论 🟢 **可合并** — 只读当日额度查询接口,计数口径与 `/compare/start` 严格同源;实跑测试全绿。置信度 0.9。 ### 契约 `GET /api/v1/compare/quota?device_id=<可选>`(需登录)→ `{exhausted: bool, used: int, limit: int|null}`。`exhausted=true` 即已达今日上限;`limit=null` 表示无限制(设备白名单)。 ### 核对要点 - **口径一致性(关键)**:`get_daily_compare_used` 与 `reserve_daily_start` 的计数**逐行一致**——同 `day_start/day_end`(北京自然日 naive)、同 `reset_at` 取 `max(当日0点, reset_at)`、同 `where(user_id, created_at∈[day_start,day_end))`、**都不按 status 过滤**(发起即计数、含 failed)。`created_at` 全程存 naive 北京时间,比较口径对齐,无 8 小时时区错位。 - **边界**:`exhausted = limit is not None and used >= limit`,与 `reserve` 的 `used >= limit → raise` 完全一致。 - **只读快照语义**:不预占,强一致仍由 `/start` 的行锁保证;`/quota` 仅作前端①④入口点击的**前置 toast 预检**。⚠️ 并发下可能出现 `/quota` 报未满但 `/start` 时已满——属只读预检固有语义,前端仍需兜底处理 `/start` 的超限响应(见配套前端 PR #385)。 ### 构建/测试(已实跑) `.venv` py3.12.7 + pytest:`pytest tests/test_compare_daily_limit.py` → **8 passed in 1.75s**(4 个新 quota 用例:新用户/已超限/昨日不计入/设备白名单 parity + 4 个既有 daily_limit 用例,均过;测试库为隔离的临时 sqlite)。 ### 正面 口径复刻严谨、时区处理正确、测试覆盖了新用户/超限/跨日/设备白名单四类关键场景,schema 字段有清晰 description。 <sub>🤖 自动深审 · 结论仅供参考,请以人工判断为准</sub>
guke merged commit bc2ed5de56 into main 2026-08-04 16:38:47 +08:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: WonderableAI/shaguabijia-app-server#216