feat(savings): 省钱战绩卡比价口径 + 关停 savings demo 兜底
- 新增 GET /api/v1/compare/stats(comparison_record: status=success 计数 + saved_amount_cents 求和),供「我的」页省钱战绩卡比价口径(完成比价 + 累计发现可省),不要求下单 - savings.py 停用 demo seeder(ensure_seeded 等移除),只计真实 source='compare';简化 beat_percent 为 compare-only - 测试: compare/stats 用例 + savings/welfare 改纯真实语义 - 文档: 新增 compare-stats.md;更新 savings-battle.md / savings_record.md / comparison_record.md / API README Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
| 12a | `POST /api/v1/compare/record` | Bearer | [详情](./compare-record-report.md) |
|
||||
| 12b | `GET /api/v1/compare/records` | Bearer | [详情](./compare-records.md) |
|
||||
| 12c | `GET /api/v1/compare/records/{id}` | Bearer | [详情](./compare-record-detail.md) |
|
||||
| 12e | `GET /api/v1/compare/stats` | Bearer | [详情](./compare-stats.md)(「我的」页省钱战绩卡:完成比价数 + 累计发现可省) |
|
||||
| **比价战绩里程碑**(前缀 `/api/v1/compare`;福利页「记录比价战绩」,按成功比价数解锁逐档发金币) |||
|
||||
| 12d | `GET /api/v1/compare/milestones` | Bearer | [详情](./compare-milestones.md) |
|
||||
| 12e | `POST /api/v1/compare/milestones/{milestone}/claim` | Bearer | [详情](./compare-milestone-claim.md) |
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# GET /api/v1/compare/stats — 比价口径战绩(「我的」页省钱战绩卡)
|
||||
|
||||
> 所属:比价记录组(前缀 `/api/v1/compare`) | 鉴权:Bearer | [← 返回 API 索引](./README.md)
|
||||
|
||||
## 入参
|
||||
无(用户由 token 确定)。
|
||||
|
||||
## 出参
|
||||
响应 `200`:`CompareStatsOut`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `compare_count` | int | 累计完成比价次数(`comparison_record` 中 `status='success'` 的记录数) |
|
||||
| `discovered_saved_cents` | int | 累计发现可省额(分;各成功比价 `saved_amount_cents`=源价−最低价 之和,null 不计) |
|
||||
|
||||
## 说明
|
||||
「我的」页**省钱战绩卡**的数据源(**比价口径**):
|
||||
|
||||
- 卡主额「累计发现可省 ¥X」← `discovered_saved_cents`
|
||||
- 卡三列之「完成比价(次)」← `compare_count`
|
||||
- 卡「未比价锁定态」判据:`compare_count == 0`(没比过价 → 盖锁定蒙层,引导「立即开始」去首页比价)
|
||||
|
||||
> ⚠️ **与下单口径解耦**:本接口只看「比过价」(`comparison_record`),**不要求下单**。区别于 `savings_record`(下单成交记账,见 [savings-summary](./savings-summary.md) / [savings-records](./savings-records.md))——那是订单记录/省钱明细的源。两表互不喂数据。
|
||||
|
||||
> 卡另两项数据源:「赚取金币」← `GET /wallet/account.total_coin_earned`;「领取优惠券」后端暂无累计字段(前端先占位)。
|
||||
@@ -13,7 +13,11 @@
|
||||
| `week_saved_cents` | int | 本周已省(分) |
|
||||
| `beat_percent` | int | 超过百分之多少用户 |
|
||||
| `streak_days` | int | 连续省钱天数 |
|
||||
| `compare_count` | int | 累计完成比价次数(= 比价上报记录数;有真实 `compare` 记录用真实数,否则 demo 兜底) |
|
||||
| `compare_count` | int | 累计完成比价次数(= 真实 `compare` 上报记录数;无记录为 0) |
|
||||
|
||||
## 说明
|
||||
「我的」页「省钱战绩」卡数据源(三列:本周已省 / 完成比价 / 连续比价)。`compare_count` 与 `/summary` 的 `order_count` 同源(均 = 有效记录数),对应原型「完成比价(次)」列。接口已通但无真实业务写入时走 demo 兜底(seed 约 23 条)。
|
||||
下单成交口径(`savings_record` 源)的战绩字段。`compare_count` 与 `/summary` 的 `order_count` 同源(均 = 真实**下单上报**记录数)。
|
||||
|
||||
> ⚠️ **2026-06 起停用 demo 兜底**:只计真实比价下单上报(`source='compare'`)。下单上报链路已通(Android `PriceBotService` → `POST /api/v1/order/report`)。
|
||||
|
||||
> ⚠️ **「我的」页省钱战绩卡已不读本接口**:#23 起卡改为**比价口径**,「完成比价 / 累计发现可省」走 [`GET /api/v1/compare/stats`](./compare-stats.md)(`comparison_record`,比过价就计、不要求下单),「赚取金币」走 `/wallet/account.total_coin_earned`。本 `/battle`(及 `compare_count`/`week_saved_cents`/`beat_percent`/`streak_days`)现仅供订单口径/历史用途,不上战绩卡。
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
## 用在哪 / 增删改查
|
||||
- **C / U(upsert,幂等)**:`POST /compare/record`(`upsert_record`)。按 `(user_id, trace_id)` 查:不存在→新建;已存在→整行覆盖(客户端重试/重复上报时,**更完整的那次胜出**)。`best_*`/`saved_amount_cents`/`is_source_best`/`status` 由 `_derive` 从 `comparison_results` 算出(协议已按 price 升序、rank=1 最便宜),不信客户端自算。
|
||||
- **D**:无(关联的 `price_report` 也只把 `comparison_record_id` 置空,不删本表)。
|
||||
- **R**:`GET /compare/records`(列表,`created_at` 倒序游标 + 「已下单」标记)、`GET /compare/records/{id}`(详情,限本人);`count_success` 给里程碑;`report.py` 反查 `best_*`;admin 大盘/明细。
|
||||
- **R**:`GET /compare/records`(列表,`created_at` 倒序游标 + 「已下单」标记)、`GET /compare/records/{id}`(详情,限本人);`count_success` 给里程碑;`get_stats`(`status='success'` 计数 + `saved_amount_cents` 求和)给 [`GET /compare/stats`](../api/compare-stats.md) 喂「我的」页省钱战绩卡(完成比价 + 累计发现可省,**比价口径**);`report.py` 反查 `best_*`;admin 大盘/明细。
|
||||
|
||||
## 字段
|
||||
| 列 | 类型 | 约束 / 默认 | 说明(取值 / join) |
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
|
||||
profile 页「累计帮你省了 / 省钱战绩 / 省钱明细」的唯一数据源:**真正下单成交后**省了多少记一行。是「记账闭环」的落库终点——客户端比价后点结果链接跳走付款,无障碍 watcher 捕获支付成功 + 金额归因(±1 元/5 分钟窗口)→ 上报 `POST /order/report`。
|
||||
|
||||
> **两种来源(`source` 字段区分)**:
|
||||
> **来源(`source` 字段)**:
|
||||
> - `source='compare'`:**真实下单上报**(经 `/order/report` 的 `create_from_report` 写,2026-06 已接通)。
|
||||
> - `source='demo'`:用户从无任何记录时,首次进省钱页**懒种子** `ensure_seeded` 按 user_id 幂等灌 ~23 条演示数据。
|
||||
> 统计口径:**只要有 ≥1 条 `compare` 记录,就只用 compare**(demo 全忽略);否则用 demo 兜底。聚合(SUM/分组/连续天数/分位)都是生产级真实计算。
|
||||
> - `source='demo'`:旧演示种子。⚠️ **2026-06 起停用** demo 兜底(`ensure_seeded` 已移除),不再产生 demo 行;历史遗留的 demo 行不再被任何统计计入。
|
||||
> 统计口径:**只用 `source='compare'`**。新用户无真实记录时 summary/battle/records 全空/0(配合「我的」页省钱战绩卡「未比价锁定态」)。聚合(SUM/分组/连续天数/分位)都是生产级真实计算。
|
||||
|
||||
## 用在哪 / 增删改查
|
||||
- **C(插入)**:
|
||||
- `POST /order/report`(`create_from_report`)→ 写一行 `source='compare'`,省额 = `original_price_cents − paid`(下限 0)。按 `(user_id, client_event_id)` 幂等,重复上报返回旧行不新增。
|
||||
- `ensure_seeded` → 用户无任何 savings 记录时,懒灌 demo(`source='demo'`),进 summary/battle/records 接口时触发。
|
||||
- ~~`ensure_seeded` demo 懒种子~~:**2026-06 移除**(不再灌 demo)。
|
||||
- **U / D**:无。
|
||||
- **R**:`GET /savings/summary`(累计省/单数/均省)、`/savings/battle`(本周省/连续天数/超过 X% 用户/比价次数)、`/savings/records`(明细分页);`comparison.py` 读本表 `shop_name`(source=compare)集合给比价记录打「已下单」。
|
||||
|
||||
@@ -47,4 +47,4 @@ profile 页「累计帮你省了 / 省钱战绩 / 省钱明细」的唯一数据
|
||||
|
||||
## 注意
|
||||
- `dishes` 用 `JSON().with_variant(JSONB(),"postgresql")`(SQLite 退化 JSON)。
|
||||
- `beat_percent`(超过百分之多少用户)按各用户累计省下金额做真实分位,每用户口径与展示一致(有 compare 用 compare 之和,否则 demo 之和)。
|
||||
- `beat_percent`(超过百分之多少用户)按各用户**真实(compare)**累计省下金额做真实分位(demo 不计)。
|
||||
|
||||
Reference in New Issue
Block a user