Files
ouzhou 766666601e feat(platform): 首页门面三统计 + 运营后台展示模式配置 (#22)
Co-authored-by: OuYingJun1024 <1034284404@qq.com>
Reviewed-on: #22
Reviewed-by: marco <marco@wonderable.ai>
Co-authored-by: ouzhou <ouzhou@wonderable.ai>
Co-committed-by: ouzhou <ouzhou@wonderable.ai>
2026-06-07 23:17:27 +08:00

4.3 KiB
Raw Permalink Blame History

savings_record — 省钱记录(profile 省钱战绩源)

模型 app/models/savings.py · 仓库 app/repositories/savings.py · 接口 savings-summary / savings-battle / savings-records · 写入源 POST /api/v1/order/report(compare-record 相关) · ← 索引 · 总览

profile 页「累计帮你省了 / 省钱战绩 / 省钱明细」的唯一数据源:真正下单成交后省了多少记一行。是「记账闭环」的落库终点——客户端比价后点结果链接跳走付款,无障碍 watcher 捕获支付成功 + 金额归因(±1 元/5 分钟窗口)→ 上报 POST /order/report

来源(source 字段):

  • source='compare':真实下单上报(经 /order/reportcreate_from_report 写,2026-06 已接通)。
  • 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 demo 懒种子2026-06 移除(不再灌 demo)。
  • 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_iduser.id(多对一)。
  • comparison_record(语义 join,无 FK):本表 shop_name(source=compare)== comparison_record.store_name → 给那条比价记录标「已下单」。注意是店名对齐、店级(下单上报不带 trace_id,无法逐条精确对应)。

索引与约束

  • PK id;index user_idcreated_at;UNIQUE(user_id, client_event_id) = uq_savings_user_event(真实上报幂等;demo 行 client_event_id=NULL 不参与冲突,允许多 NULL)。

注意

  • dishesJSON().with_variant(JSONB(),"postgresql")(SQLite 退化 JSON)。
  • beat_percent(超过百分之多少用户)按各用户**真实(compare)**累计省下金额做真实分位(demo 不计)。