Files
shaguabijia-app-server/docs/api/compare-record-report.md
T
ouzhou da7ce69494 feat(ad): eCPM 上报接口 + 冷却策略解耦纯函数 + savings JSONB 跨库修复 (#8)
- 新增 POST /api/v1/ad/ecpm-report:激励视频展示后客户端上报本次 eCPM,落 ad_ecpm_record 做内部收益统计(model/repository/schema/alembic 迁移 + 接口文档)
- 看广告冷却策略抽到 app/core/ad_cooldown.py 纯函数;ad_reward.today_status 只取数据,换策略只改这一处
- savings.dishes 改 JSON().with_variant(JSONB,"postgresql"):SQLite 无 visit_JSONB 会让 create_all 编译崩,修复后测试套件恢复

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: OuYingJun1024 <1034284404@qq.com>
Reviewed-on: #8
Co-authored-by: ouzhou <ouzhou@wonderable.ai>
Co-committed-by: ouzhou <ouzhou@wonderable.ai>
2026-06-01 22:23:15 +08:00

3.7 KiB
Raw Blame History

POST /api/v1/compare/record — 上报一次比价结果(幂等)

所属:比价记录组(前缀 /api/v1/compare | 鉴权:Bearer | ← 返回 API 索引

比价 done 帧后,客户端用带 JWT 的通道上报一条比价结果,落 comparison_record 表,作为「我的比价记录」数据源 + 用户级行为画像。

⚠️ 与不鉴权的透传端点 /api/v1/price/step 不同:那是转发壳,本接口按用户维度落库,必须鉴权。 本轮只做 server 端;客户端在 done 帧后调本接口的改动另起一轮(见 待办与技术债.md P1)。

入参(JSON body

字段刻意对齐 pricebot 协议(02_api_protocol.mdcalibration + done.params),客户端把 Phase1 的 calibration 和 done 帧 params 零翻译映射上来即可。

字段 类型 必填 默认 说明
trace_id string pricebot 侧 trace_id。幂等键:同用户同 trace_id 重复上报覆盖、返回同一 id
business_type string food food(外卖,当前唯一接通) / ecom(电商) / coupon(领券)
device_id string | null null 设备号
store_name string | null null 店铺名(外卖,来自 calibration.result
source_platform_id string | null null 源平台代号,如 taobao_flash
source_platform_name string | null null 源平台中文名
source_package string | null null 源平台 Android 包名
source_price float | null null 源平台到手价(
items Item[] [] 下单菜品,{name, qty, specs?}
comparison_results Result[] [] 逐平台对比,见下表(price 单位
skipped_dish_count int | null null 目标平台未找到、跳过的菜品数
skipped_dish_names string[] [] 被跳过的菜名
total_dish_count int | null null 原始菜品总数
information string | null null done 帧文案(成功摘要 / 失败具体原因),持久化为列并在列表/详情返回,前端失败时当原因展示
status string | null null success / failed不传则服务端派生

Item{ name: string, qty: int=1, specs: string[]\|null }

Resultcomparison_results 元素){ platform_id, platform_name, package, price(元,float\|null), is_source(bool), rank(int\|null) }

服务端派生(客户端不用算)

comparison_results 派生并落结构化列:

  • best_*:取最便宜的一家(按 rank 升序、再按 price),写 best_platform_id/name/best_price_cents
  • source_price_cents:优先用 source_price,缺失则取 comparison_resultsis_source=true 行的价
  • saved_amount_cents = 源价 最优价(可为 0 / 负:源平台本来就最便宜)
  • is_source_best:最便宜的一家是否为源平台(= 这次没省到)
  • status(未显式传时):存在「非源且有价」的结果 → success,否则 → failed
  • 金额一律 round(元 × 100)

comparison_results / items / skipped_dish_names 原样存 JSON;完整上报 body 另存 raw_payload(详情接口可取回)。

出参

响应 200{ "id": int }(写入或已存在记录的 id

错误

  • 401 未鉴权
  • 422trace_id 或字段类型不符

说明

失败的比价(done 帧只有源/空 comparison_results)也会落一条 status=failed——「越详细越好」,是否在 UI 展示由前端过滤。