Files
shaguabijia-app-server/docs/database/savings_record.md
T
marco b4780e256e docs(database): 补全 19 表文档 + 新增 OVERVIEW 总览
- 重写 15 张已有表文档:每张补「用在 App 哪/增删改查时机/字段取值/join key 指向」
- 新建 4 张缺失表文档:wechat_transfer_authorization / ad_watch_log / price_report / app_config
- 新增 OVERVIEW.md:功能↔表映射 + 写入路径 + 表间关系(含语义 join key)+ ER + 资金模型
- README 索引补齐到 19 表 + 置顶 OVERVIEW + 通用约定补全
- 纠正过时:withdraw_order(reviewing/rejected/user_name)、savings_record(/order/report 真实写入)、coin_transaction(biz_type)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 00:25:57 +08:00

51 lines
4.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# savings_record — 省钱记录(profile 省钱战绩源)
> 模型 `app/models/savings.py` · 仓库 `app/repositories/savings.py` · 接口 [savings-summary](../api/savings-summary.md) / [savings-battle](../api/savings-battle.md) / [savings-records](../api/savings-records.md) · 写入源 `POST /api/v1/order/report`([compare-record 相关](../api/compare-record-report.md)) · [← 索引](./README.md) · [总览](./OVERVIEW.md)
profile 页「累计帮你省了 / 省钱战绩 / 省钱明细」的唯一数据源:**真正下单成交后**省了多少记一行。是「记账闭环」的落库终点——客户端比价后点结果链接跳走付款,无障碍 watcher 捕获支付成功 + 金额归因(±1 元/5 分钟窗口)→ 上报 `POST /order/report`
> **两种来源(`source` 字段区分)**:
> - `source='compare'`:**真实下单上报**(经 `/order/report` 的 `create_from_report` 写,2026-06 已接通)。
> - `source='demo'`:用户从无任何记录时,首次进省钱页**懒种子** `ensure_seeded` 按 user_id 幂等灌 ~23 条演示数据。
> 统计口径:**只要有 ≥1 条 `compare` 记录,就只用 compare**(demo 全忽略);否则用 demo 兜底。聚合(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 接口时触发。
- **U / D**:无。
- **R**:`GET /savings/summary`(累计省/单数/均省)、`/savings/battle`(本周省/连续天数/超过 X% 用户/比价次数)、`/savings/records`(明细分页);`comparison.py` 读本表 `shop_name`(source=compare)集合给比价记录打「已下单」。
## 字段
| 列 | 类型 | 约束 / 默认 | 说明(取值 / join) |
|---|---|---|---|
| `id` | Integer | PK, autoincrement | |
| `user_id` | Integer | FK→user.id, index, NOT NULL | 归属用户 |
| `order_amount_cents` | Integer | NOT NULL | 订单到手价/实付(分) |
| `saved_amount_cents` | Integer | NOT NULL | 本单省下(分,可为 0;= original 实付,下限 0) |
| `platform` | String(32) | nullable | 下单平台(美团外卖/淘宝闪购/京东外卖) |
| `title` | String(128) | nullable | 明细卡标题(compare 来源 = 门店名) |
| `shop_name` | String(128) | nullable | 店铺名。**与 `comparison_record.store_name` 字符串相等关联**(打「已下单」) |
| `dishes` | JSON(PG: JSONB) | NOT NULL, default [] | 菜名列表(前 2 道展示,其余"还有 N 道") |
| `source` | String(16) | NOT NULL, default `compare` | 取值:`compare`(真实下单上报)/ `demo`(演示种子) |
| `original_price_cents` | Integer | nullable | 源平台原价(分);compare 上报带,demo 为 null |
| `compared_price_cents` | Integer | nullable | 我们当时给出的比价价(分,审计/备用) |
| `pay_channel` | String(16) | nullable | 支付渠道:`wechat` / `alipay`(归因 watcher 判定) |
| `platform_package` | String(128) | nullable | 实际下单平台包名 |
| `source_platform_name` | String(32) | nullable | 源平台展示名(如「美团」),用于"原价 ¥X(美团)" |
| `source_deeplink` | String(512) | nullable | 源平台重进链接(预留) |
| `client_event_id` | String(64) | nullable | 客户端幂等键(UUID);demo 行为 null |
| `device_id` | String(128) | nullable | 上报设备号 |
| `created_at` | DateTime(tz) | server_default now(), index | 时间 |
## 关系 / Join Key
- `user_id``user.id`(多对一)。
-`comparison_record`(语义 join,无 FK):本表 `shop_name`(source=compare)== `comparison_record.store_name` → 给那条比价记录标「已下单」。注意是**店名对齐、店级**(下单上报不带 trace_id,无法逐条精确对应)。
## 索引与约束
- PK `id`;index `user_id``created_at`;UNIQUE(`user_id`, `client_event_id`) = `uq_savings_user_event`(真实上报幂等;demo 行 `client_event_id=NULL` 不参与冲突,允许多 NULL)。
## 注意
- `dishes``JSON().with_variant(JSONB(),"postgresql")`(SQLite 退化 JSON)。
- `beat_percent`(超过百分之多少用户)按各用户累计省下金额做真实分位,每用户口径与展示一致(有 compare 用 compare 之和,否则 demo 之和)。