Files
shaguabijia-app-server/docs/database/comparison_record.md
T
marco 4ee6de2548 feat(cps): 活动支持编辑(PATCH /activities/{id})
- CpsActivityUpdate schema + repo update_activity(部分更新,非 None 覆盖)
- router 按「合并后最终值」校验平台必填项(同新建口径),写审计 cps.activity.update
- 可改名/平台/对应字段/落地页图/备注/状态

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 22:13:35 +08:00

6.2 KiB

comparison_record — 比价记录(每次比价完整明细)

模型 app/models/comparison.py · 仓库 app/repositories/comparison.py · 接口 compare-record-report / compare-records / compare-record-detail · ← 索引 · 总览

每完成一次比价(外卖/电商/领券),客户端在 done 帧后用带 JWT 的通道上报一条。App「我的比价记录」列表/详情的数据源,也是比价战绩里程碑解锁进度的计数源(status='success' 条数),还被「上报更低价」反查原最低价。

savings_record 的区别:本表是「每一次比价行为的完整明细」(不省钱、甚至失败也记);savings_record 是「真正下单成交省了多少」。两表独立、互不喂数据。 与 price_observation / store_mapping 的区别:本表是用户视角(登录后按 user_id 存「我的比价记录」);后两张是 server 侧无条件沉淀的平台/门店视角客观事实(价格事实 / 跨平台店铺身份映射),与本表 trace_id 同源但不互相 join,各存各的视角。

用在哪 / 增删改查

  • C / U(upsert,幂等):POST /compare/record(upsert_record)。按 (user_id, trace_id) 查:不存在→新建;已存在→整行覆盖(客户端重试/重复上报时,更完整的那次胜出)。best_*/saved_amount_cents/is_source_best/status_derivecomparison_results 算出(协议已按 price 升序、rank=1 最便宜),不信客户端自算。
  • D:无(关联的 price_report 也只把 comparison_record_id 置空,不删本表)。
  • R:GET /compare/records(列表,created_at 倒序游标 + 「已下单」标记)、GET /compare/records/{id}(详情,限本人);count_success 给里程碑;get_stats(status='success' 计数 + saved_amount_cents 求和)给 GET /compare/stats 喂「我的」页省钱战绩卡(完成比价 + 累计发现可省,比价口径);report.py 反查 best_*;admin 大盘/明细。

字段

类型 约束 / 默认 说明(取值 / join)
id Integer PK, autoincrement price_report.comparison_record_id 引用
user_id Integer FK→user.id, index, NOT NULL 归属用户
device_id String(64) nullable 设备号(多设备区分 / 与不鉴权期对账)
business_type String(16) NOT NULL, default food, index 取值:food(当前唯一接通)/ ecom / coupon
trace_id String(64) NOT NULL pricebot 侧 trace_id(关联调试落盘 + 幂等键)
trace_url String(512) nullable 本次比价公网调试链接(price.shaguabijia.com/traces/{dir}/);dir 名含 pricebot 落盘时分秒,前端/server 拼不出必须存。查看接口按 user.debug_trace_enabled(或本机 agent 调试 include_trace)决定返不返回。旧记录 / 未开上云为 null
source_platform_id / _name String(32) nullable 源平台代号 / 中文名
source_package String(128) nullable 源平台 Android 包名
source_price_cents Integer nullable 源平台到手价(分)
best_platform_id / _name String(32) nullable 最优平台代号 / 中文名(= rank=1)
best_price_cents Integer nullable 最优价(分)。price_report 反查作"原最低价",校验上报价必须更低
best_deeplink String(1024) nullable 最优平台商家深链(再次比价/直达用);2026-06 起新比价才有,旧记录 null
saved_amount_cents Integer nullable 源价 − 最优价(可 0/负:源平台本就最便宜)
is_source_best Boolean nullable 源平台就是最便宜(= 这次没省到)
store_name String(128) nullable 店铺名。savings_record.shop_name 按字符串相等关联,给本记录打「已下单」
total_dish_count / skipped_dish_count Integer nullable 菜品总数 / 目标平台没找到被跳过数
status String(16) NOT NULL, default success 取值:success(有非源且有价的目标结果)/ failed(出错/没采到目标价)。里程碑只数 success
information String(256) nullable done 帧文案;成功=摘要,失败=具体原因(前端失败时当原因展示)
items JSON(PG: JSONB) NOT NULL, default [] 下单菜品 [{name, qty, specs?}]
comparison_results JSON(PG: JSONB) NOT NULL, default [] 逐平台对比 [{platform_id,platform_name,package,price(元),is_source,rank,coupon_saved(元),coupon_name,applied_coupons}];coupon_saved=该平台主优惠额(美团红包/淘宝平台红包/京东百亿补贴,只取一笔),coupon_name=优惠来源名(展示用),applied_coupons=[{name,amount}] 多券明细
skipped_dish_names JSON(PG: JSONB) NOT NULL, default [] 被跳过的菜名
raw_payload JSON(PG: JSONB) nullable 客户端原始上报全量(calibration + done.params),取数兜底
created_at DateTime(tz) server_default now(), index 时间

ordered(已下单)是瞬态字段,不在表里:list_records 读取时按 store_name ∈ 该用户 source='compare' 的 savings_record.shop_name 集合 现挂到实例上供出参用。

关系 / Join Key

  • user_iduser.id(多对一)。
  • ← 被 price_report.comparison_record_id 引用(选中某条比价记录上报更低价);price_report 同时读本表 best_price_cents/best_platform_*/store_name/items 做快照与校验。
  • savings_record(语义 join,无 FK):store_name == savings_record.shop_name(source='compare')→ 标「已下单」,店级。
  • comparison_milestone_claim 间接依赖:解锁进度 = 本表 status='success' 计数。

索引与约束

  • PK id;index user_idbusiness_typecreated_at;复合 index ix_comparison_status_created(status, created_at)(按 status='success' 过滤 + 近期排序的聚合/轮播,避免随数据量退化为全表扫);UNIQUE(user_id, trace_id) = uq_comparison_user_trace(幂等覆盖)。

注意

  • 4 个 JSON 列用 JSON().with_variant(JSONB(),"postgresql")(SQLite 退化 JSON)。结构化金额列存「分」,comparison_results.price/coupon_saved 原样存「元」。