比价记录改后端 harvest: 透传壳落库替代客户端主动 POST

compare.py 透传壳新增后端 harvest(不再依赖客户端 POST /compare/record):
- 帧0(客户端首帧不带 trace_id)由 app-server 用 uuid 签发 trace_id、注入转发 body、
  回填响应顶层,并按 trace_id 建 running 行(harvest_running)
- done 帧(command=done 且 continue=false)更新为 success/failed 终态,并对本次新落成
  success 幂等发一次邀请奖(harvest_done + try_reward_on_compare)
- /trace/finalize(用户终止/Phase1 未识别,无 done)更新为 cancelled/failed,
  不降级已 success(harvest_abort)
- 软鉴权 OptionalUser(deps.py 新增 get_current_user_optional):新客户端带 JWT 绑
  user_id,老客户端匿名建行、user_id 暂空,由其后续 POST /compare/record 按 trace_id
  reconcile;新版覆盖够高后可收紧成硬鉴权
- 结构化日志(logging.py 加 trace_id_ctx ContextVar):请求级 trace_id 贯穿 harvest 各阶段

comparison_record 表结构调整(迁移 comparison_record_trace_unique):
- 唯一键 (user_id,trace_id) → trace_id 单列(harvest 帧0 建行时 user_id 可能暂缺)
- user_id 改可空

repositories/comparison.py 加 harvest_running/harvest_done/harvest_abort;
compare_record.py 的 POST /record 降级为灰度期老客户端兼容(按 trace_id 幂等、不降级 success);
补 tests/test_compare_harvest.py,test_compare_proxy/milestone 适配。

文档同步: docs/database/comparison_record.md 更新表结构(唯一键/user_id 可空/status 取值)
与写入模型; docs/api/compare/compare-record-report.md 标注 POST /record 灰度定位。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-04 01:56:46 +08:00
parent ea60dc391b
commit 635d127f95
12 changed files with 842 additions and 99 deletions
+4 -4
View File
@@ -2,10 +2,10 @@
> 所属:比价记录组(前缀 `/api/v1/compare` | 鉴权:Bearer | [← 返回 API 索引](../README.md)
比价 `done` 帧后,客户端用**带 JWT 的通道**上报一条比价结果,落 `comparison_record` 表,作为「我的比价记录」数据源 + 用户级行为画像。
比价 `done` 帧后上报一条比价结果,落 `comparison_record` 表,作为「我的比价记录」数据源 + 用户级行为画像。
> ⚠️ 与不鉴权的透传端点 [`/api/v1/price/step`](./compare-price-step.md) 不同:那是转发壳,本接口按用户维度落库,**必须鉴权**
> 本轮只做 server 端;客户端在 done 帧后调本接口的改动另起一轮(见 [待办与技术债.md](../guides/待办与技术债.md) P1
> ⚠️ **灰度定位(2026-07)**:写 `comparison_record` 现以透传壳 `compare.py` 的**后端 harvest** 为主(帧0 建 `running` 行 → done/finalize 落终态,新客户端不再 POST)。本接口降级为**老客户端兼容**通道,与 harvest 按 `trace_id` reconcile(**success 不被降级**);新版覆盖够高后可下线本 POST
> 与软鉴权透传端点 [`/api/v1/price/step`](./compare-price-step.md) 不同:那是转发壳(顺带 harvest 落库),本接口按用户维度显式上报,**必须鉴权**
## 入参(JSON body
@@ -13,7 +13,7 @@
| 字段 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
| `trace_id` | string | ✅ | — | pricebot 侧 trace_id。**幂等键**:同用户同 trace_id 重复上报覆盖、返回同一 id |
| `trace_id` | string | ✅ | — | 一次比价的唯一标识(app-server 帧0 签发)。**幂等键(trace_id 单列唯一)**同 trace_id 重复上报覆盖、返回同一 id;与 harvest 行按它 reconcile |
| `business_type` | string | ❌ | `food` | `food`(外卖,当前唯一接通) / `ecom`(电商) / `coupon`(领券) |
| `device_id` | string \| null | ❌ | null | 设备号 |
| `store_name` | string \| null | ❌ | null | 店铺名(外卖,来自 calibration.result |