@ (#111)
docs/api目录文档分类和补全 --------- Co-authored-by: guke <guke@autohome.com.cn> Reviewed-on: #111
This commit was merged in pull request #111.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# POST /api/v1/analytics/events — 批量上报埋点事件
|
||||
|
||||
> 所属:Analytics 组(前缀 `/api/v1/analytics`) | 鉴权:无(不强制登录,未登录态也要采集行为) | [← 返回 API 索引](../README.md)
|
||||
|
||||
批量接收新手引导(及后续)埋点,append 落 `analytics_event` 表。`user_id` 由客户端在 body 可选带上,不靠 Bearer。服务端补 `client_ip`(X-Forwarded-For)与 `server_at`(接收时间)。
|
||||
|
||||
## 入参
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `device_id` | string | ✅(≤64) | 设备 ID |
|
||||
| `user_id` | int \| null | ❌ | 登录用户 ID(未登录可空) |
|
||||
| `sent_at` | int \| null | ❌ | 批次发送时间(epoch ms) |
|
||||
| `oem` | string \| null | ❌ | 厂商(如 `Xiaomi`) |
|
||||
| `os` | string \| null | ❌ | 操作系统(如 `Android 14`) |
|
||||
| `model` | string \| null | ❌ | 机型(如 `24115RA8EC`) |
|
||||
| `app_ver` | string \| null | ❌ | App 版本 |
|
||||
| `channel` | string \| null | ❌ | 渠道 |
|
||||
| `events` | list[object] | ✅(1-200 条) | 事件数组 |
|
||||
| `events[].event` | string | ✅(≤64) | 事件名(如 `onboarding_start`) |
|
||||
| `events[].client_ts` | int | ✅ | 端事件发生时间(epoch ms) |
|
||||
| `events[].session_id` | string \| null | ❌ | 会话 ID |
|
||||
| `events[].page` | string \| null | ❌ | 页面标识 |
|
||||
| `events[].network` | string \| null | ❌ | 网络类型(如 `wifi`) |
|
||||
| `events[].props` | dict | ❌ | 事件属性(key-value) |
|
||||
|
||||
Mock 入参:
|
||||
```json
|
||||
{
|
||||
"device_id": "android_abc123def456",
|
||||
"user_id": 42,
|
||||
"sent_at": 1719993700000,
|
||||
"oem": "Xiaomi",
|
||||
"os": "Android 14",
|
||||
"model": "24115RA8EC",
|
||||
"app_ver": "0.1.5",
|
||||
"channel": "official",
|
||||
"events": [
|
||||
{
|
||||
"event": "onboarding_start",
|
||||
"client_ts": 1719993600000,
|
||||
"session_id": "sess_a1b2c3",
|
||||
"page": "onboarding",
|
||||
"network": "wifi",
|
||||
"props": {"step": "1", "source": "fresh_install"}
|
||||
},
|
||||
{
|
||||
"event": "onboarding_step_complete",
|
||||
"client_ts": 1719993615000,
|
||||
"session_id": "sess_a1b2c3",
|
||||
"page": "onboarding",
|
||||
"network": "wifi",
|
||||
"props": {"step": "1", "duration_ms": "15000"}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 出参
|
||||
|
||||
响应 `200`:`AnalyticsIngestOut`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `ok` | bool | 固定 `true` |
|
||||
| `received` | int | 成功写入的条数 |
|
||||
|
||||
Mock 出参:
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"received": 2
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码
|
||||
- `422` `events` 为空或超过 200 条 / 字段类型不符
|
||||
|
||||
## 说明
|
||||
- `user_id` 不靠 JWT:未登录态也要采集行为(新手引导可能在登录前)
|
||||
- 每批最多 200 条,建议客户端攒到一定量再批量上报
|
||||
- `client_ts` 是端侧时间(客户端时钟),`server_at` 由服务端补(可靠时间轴)
|
||||
@@ -0,0 +1,30 @@
|
||||
# CPS 群发短链落地(cps-redirect 族)
|
||||
|
||||
> 所属:cps-redirect 组(**无前缀**,挂域名根;源 `app/api/v1/cps_redirect.py`) | 鉴权:**公网无鉴权**(群里任何人点都要能跳/能领) | [← 返回 API 索引](../README.md)
|
||||
>
|
||||
> 落库:点击落 [`cps_click`](../database/cps_click.md)、微信落地页用户落 [`cps_wx_user`](../database/cps_wx_user.md);短链由 [`cps_link`](../database/cps_link.md) 解析。
|
||||
|
||||
运营把 `/c/{code}` 短链发到微信群,用户点击 → 记点击 → 按平台 302 跳转(美团/京东)或返回 H5 淘宝落地页。**记点击/写用户失败绝不阻断用户跳转/领券**(try/except 吞掉)。
|
||||
|
||||
## GET /c/{code} — 短链落地
|
||||
- 微信内 + 还没拿到 openid + 配了服务号 → 先 `snsapi_base` 静默授权拿 openid,再 302 回本页。
|
||||
- 非微信 / 已有 openid cookie / 未配服务号 → 直接走原逻辑(openid 可能为 None,**绝不阻断**)。
|
||||
- 记 `visit`(写 `cps_click`)后:**美团/京东** 302 跳 `target_url`;**淘宝** 返回 H5 落地页(主视觉图 + 「复制口令」按钮)。
|
||||
- `code` 不存在/失效 → 302 跳兜底(`https://www.meituan.com/`)。
|
||||
|
||||
**入参**:路径 `code`(短链码)。**出参**:`302 Redirect` 或 `200 HTML`(淘宝)。
|
||||
|
||||
## POST /c/{code}/copy — 淘宝落地页点了「复制口令」
|
||||
淘宝落地页是 H5,用户点「复制口令」才算转化 → 记一条 `copy` 事件(带 openid cookie)。
|
||||
|
||||
**入参**:路径 `code`;body 空。**出参**:`{ "ok": true }`。
|
||||
|
||||
## GET /wx/oauth/cb — 微信网页授权回调(`include_in_schema=False`)
|
||||
`state='base:{code}'`(静默拿 openid)或 `'uinfo:{code}'`(补昵称头像)。换 openid(+userinfo)→ upsert [`cps_wx_user`](../database/cps_wx_user.md) → 种 cookie(`wx_openid` / `wx_uinfo`,30 天)→ 302 回落地页。**任何失败兜底回落地页,绝不阻断领券**。`uinfo` 回流带 `?authed=1`,落地页据此自动触发复制(把「点领券→授权→复制」衔接成一步)。
|
||||
|
||||
## GET /MP_verify_*.txt — 微信「网页授权域名」归属校验(`include_in_schema=False`)
|
||||
微信请求 `coupon.shaguabijia.com/MP_verify_xxx.txt` 验证域名归属;端点放域名根、返回文件名核心串(纯文本)。为微信网页授权(拿 openid)接入做准备。
|
||||
|
||||
## 说明
|
||||
- **两阶授权**:`base` 静默拿 openid(统计主力、不打扰);`userinfo` 在用户点「领券」时触发(交互触发避免微信快照页),拿昵称/头像/unionid。
|
||||
- **`cps_click` 不阻断**:落地/跳转优先级高于统计——丢几条点击可接受,挡住用户领券不可接受。
|
||||
@@ -0,0 +1,40 @@
|
||||
# GET /api/v1/feedback/config — 反馈页二维码卡配置
|
||||
|
||||
> 所属:Feedback 组(前缀 `/api/v1/feedback`) | 鉴权:Bearer | [← 返回 API 索引](../README.md)
|
||||
|
||||
运营后台配的反馈页「加群二维码」卡配置(开关 + 二维码图 + 三行文案)。客户端进反馈页时拉取,据此渲染整张「加群二维码」卡。
|
||||
|
||||
## 入参
|
||||
|
||||
无(`user_id` 从 JWT 取)。
|
||||
|
||||
## 出参
|
||||
|
||||
响应 `200`:`FeedbackQrConfigOut`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `enabled` | bool | 是否展示加群二维码卡 |
|
||||
| `image_url` | string \| null | 二维码图片 URL(相对 `/media` 路径);空 → 客户端走本地兜底 |
|
||||
| `title` | string | 卡片标题(如 `加入用户反馈群`) |
|
||||
| `group_name` | string | 群名称(如 `傻瓜比价用户群`) |
|
||||
| `subtitle` | string | 副标题/说明文案(如 `扫码加入,你的声音我们听得见`) |
|
||||
|
||||
Mock 出参:
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"image_url": "/media/feedback_qr/qr_default.png",
|
||||
"title": "加入用户反馈群",
|
||||
"group_name": "傻瓜比价用户群",
|
||||
"subtitle": "扫码加入,你的声音我们听得见"
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码
|
||||
- `401` 未鉴权 / token 失效
|
||||
|
||||
## 说明
|
||||
- `image_url` 是相对路径,客户端按自己的 `BASE_URL` 拼绝对地址
|
||||
- `enabled=false` 时客户端隐藏整张卡
|
||||
- 配置在 admin 后台 `feedback_qr` 表维护
|
||||
@@ -0,0 +1,71 @@
|
||||
# GET /api/v1/feedback/records — 我的反馈历史
|
||||
|
||||
> 所属:Feedback 组(前缀 `/api/v1/feedback`) | 鉴权:Bearer | [← 返回 API 索引](../README.md)
|
||||
|
||||
查询当前用户提交的反馈历史,支持按状态筛选。
|
||||
|
||||
## 入参(query)
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `status` | string \| null | ❌ | 筛选状态:`pending` / `adopted` / `rejected`;不传 = 全部 |
|
||||
|
||||
Mock 请求:
|
||||
```
|
||||
GET /api/v1/feedback/records?status=adopted
|
||||
```
|
||||
|
||||
## 出参
|
||||
|
||||
响应 `200`:`FeedbackRecordsOut`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `records` | list[FeedbackRecordOut] | 反馈记录列表 |
|
||||
| `records[].id` | int | 反馈 ID |
|
||||
| `records[].content` | string | 反馈正文 |
|
||||
| `records[].scene` | string \| null | 问题场景(比价结果页反馈时带,如 `找错商品`) |
|
||||
| `records[].images` | list[string] | 截图 URL 列表 |
|
||||
| `records[].status` | string | `pending` / `adopted` / `rejected` |
|
||||
| `records[].reject_reason` | string \| null | 驳回原因 |
|
||||
| `records[].reward_coins` | int \| null | 采纳后发的金币数 |
|
||||
| `records[].admin_reply` | string \| null | 管理员回复 |
|
||||
| `records[].created_at` | datetime | 提交时间 |
|
||||
| `counts` | object | 三态计数(不受 status 筛选影响) |
|
||||
| `counts.all` | int | 总数 |
|
||||
| `counts.pending` | int | 待处理 |
|
||||
| `counts.adopted` | int | 已采纳 |
|
||||
| `counts.rejected` | int | 已驳回 |
|
||||
|
||||
Mock 出参:
|
||||
```json
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"id": 56,
|
||||
"content": "比价结果显示美团 28.5 元,但实际下单时涨到了 32 元",
|
||||
"scene": "价格不一致",
|
||||
"images": ["/media/feedback/u42_f1e2d3c4b5a60708.jpg"],
|
||||
"status": "adopted",
|
||||
"reject_reason": null,
|
||||
"reward_coins": 500,
|
||||
"admin_reply": "感谢反馈,已核实并修复",
|
||||
"created_at": "2026-07-02T15:20:00Z"
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"all": 3,
|
||||
"pending": 1,
|
||||
"adopted": 2,
|
||||
"rejected": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码
|
||||
- `400` 无效的 `status` 值(仅 `pending`/`adopted`/`rejected` 合法)
|
||||
- `401` 未鉴权
|
||||
|
||||
## 说明
|
||||
- `counts` 始终基于全量(不受 `status` 筛选影响),供前端筛选 chip
|
||||
- `scene` 仅比价结果页反馈时带(区分普通反馈 vs 比价场景反馈)
|
||||
@@ -0,0 +1,40 @@
|
||||
# POST /api/v1/feedback — 提交反馈
|
||||
|
||||
> 所属:Feedback 组(前缀 `/api/v1/feedback`) | 鉴权:Bearer access_token | [← 返回 API 索引](../README.md)
|
||||
|
||||
## 入参
|
||||
**multipart/form-data**:
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `content` | string | ✓ | 反馈正文,**1-200 字**(strip 后) |
|
||||
| `contact` | string | ✗ | 联系方式(微信/QQ/手机号),**≤128 字**。原型改版后客户端已不再采集、不传该字段(后端默认空串);保留字段兼容旧端 |
|
||||
| `images` | file[] | ✗ | 截图,**最多 6 张**,每张走头像同款校验(JPEG/PNG/WebP,≤ 5 MB,魔数嗅探) |
|
||||
|
||||
## 出参
|
||||
响应 `200`:
|
||||
```json
|
||||
{
|
||||
"id": 123,
|
||||
"status": "new",
|
||||
"created_at": "2026-05-29T12:34:56Z"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `id` | int | 反馈单 ID |
|
||||
| `status` | string | `new`(待处理)/ `handled`(已处理) |
|
||||
| `created_at` | datetime | 提交时间(UTC) |
|
||||
|
||||
> 不返回上传的 image URL——这是给运营后台看的,客户端通常不需要。
|
||||
|
||||
## 错误码
|
||||
- `400` 内容为空 / 内容超 200 字 / 联系方式超 128 字 / 图片超 6 张 / 单图非法(空/过大/格式不对)
|
||||
- `401` 未带 token / token 无效或过期 / 用户被禁用
|
||||
- `422` 缺 `content` 字段
|
||||
|
||||
## 说明
|
||||
- **反馈绑用户**:`feedback.user_id = current_user.id`,便于回访
|
||||
- **截图落盘**:存到 `settings.MEDIA_ROOT/feedback/`,URL 写进 `feedback.images`(JSON 列,SQLite 是 TEXT、PG 是 JSON/JSONB——为不影响现有迁移用 JSON 而非 JSONB)
|
||||
- **截图可读**:`GET /media/feedback/<file>` 静态服务暴露,详见 [API 索引 — /media 静态服务](./README.md#media-静态服务)
|
||||
- 文件名随机 `u<user_id>_<rand>.<ext>`,杜绝路径穿越
|
||||
@@ -0,0 +1,15 @@
|
||||
# GET /health — 健康检查
|
||||
|
||||
> 所属:Meta | 鉴权:无 | [← 返回 API 索引](../README.md)
|
||||
|
||||
## 入参
|
||||
无
|
||||
|
||||
## 出参
|
||||
响应 `200`:`{ "status": "ok" }`
|
||||
|
||||
## 错误码
|
||||
无
|
||||
|
||||
## 说明
|
||||
无
|
||||
@@ -0,0 +1,74 @@
|
||||
# POST /api/v1/order/report — 上报归因订单
|
||||
|
||||
> 所属:Order 组(前缀 `/api/v1/order`) | 鉴权:Bearer | [← 返回 API 索引](../README.md)
|
||||
|
||||
比价后 5 分钟内点链接下单、支付金额与比价价相差 ≤1 元时,客户端上报归因订单。落 `savings_record`(`source='compare'`),客户端幂等键防重。
|
||||
|
||||
## 入参
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `client_event_id` | string | ✅(≤64) | 客户端幂等键(UUID) |
|
||||
| `platform` | string | ✅(≤32) | 平台展示名(如 `美团`) |
|
||||
| `platform_package` | string \| null | ❌ | 平台包名 |
|
||||
| `pay_channel` | string | ✅(≤16) | 支付渠道(`wechat` / `alipay`) |
|
||||
| `compared_price_cents` | int | ✅(≥0) | 我们给出的比价价(分) |
|
||||
| `paid_amount_cents` | int | ✅(≥0) | 实际支付金额(分) |
|
||||
| `device_id` | string \| null | ❌ | 设备 ID |
|
||||
| `shop_name` | string \| null | ❌ | 门店名(如 `肯德基宅急送(天北路店)`) |
|
||||
| `dishes` | list[string] | ❌ | 菜品名列表 |
|
||||
| `original_price_cents` | int \| null | ❌ | 源平台原价(分),省额 = 原价 − 实付 |
|
||||
| `source_platform_name` | string \| null | ❌ | 源平台展示名(如 `美团`) |
|
||||
| `source_deeplink` | string \| null | ❌ | 源平台重进链接(预留,本期只存不展示) |
|
||||
|
||||
Mock 入参:
|
||||
```json
|
||||
{
|
||||
"client_event_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"platform": "美团",
|
||||
"platform_package": "com.sankuai.meituan",
|
||||
"pay_channel": "wechat",
|
||||
"compared_price_cents": 2850,
|
||||
"paid_amount_cents": 2800,
|
||||
"device_id": "android_abc123def456",
|
||||
"shop_name": "肯德基宅急送(天北路店)",
|
||||
"dishes": ["香辣鸡腿堡套餐", "可口可乐(中)"],
|
||||
"original_price_cents": 4200,
|
||||
"source_platform_name": "美团",
|
||||
"source_deeplink": null
|
||||
}
|
||||
```
|
||||
|
||||
## 出参
|
||||
|
||||
响应 `200`:`OrderReportOut`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `id` | int | 省钱记录 ID |
|
||||
| `platform` | string | 平台 |
|
||||
| `pay_channel` | string | 支付渠道 |
|
||||
| `compared_price_cents` | int | 比价价(分) |
|
||||
| `paid_amount_cents` | int | 实付金额(分) |
|
||||
| `duplicated` | bool | 是否为重复上报(幂等命中) |
|
||||
|
||||
Mock 出参:
|
||||
```json
|
||||
{
|
||||
"id": 1234,
|
||||
"platform": "美团",
|
||||
"pay_channel": "wechat",
|
||||
"compared_price_cents": 2850,
|
||||
"paid_amount_cents": 2800,
|
||||
"duplicated": false
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码
|
||||
- `401` 未鉴权 / token 失效
|
||||
- `422` 必填字段缺失或类型不符
|
||||
|
||||
## 说明
|
||||
- 记账唯一真相表是 `savings_record`(`source='compare'`)
|
||||
- `client_event_id` 幂等防重(网络重试不重复记)
|
||||
- 省额 = `original_price_cents − paid_amount_cents`(若原价可用)
|
||||
@@ -0,0 +1,81 @@
|
||||
# GET /api/v1/report/records — 上报更低价记录列表
|
||||
|
||||
> 所属:Report 组(前缀 `/api/v1/report`) | 鉴权:Bearer | [← 返回 API 索引](../README.md)
|
||||
|
||||
当前用户的上报更低价记录列表,支持按状态筛选。
|
||||
|
||||
## 入参(query)
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `status` | string \| null | ❌ | 筛选状态:`pending` / `approved` / `rejected`;不传 = 全部 |
|
||||
|
||||
Mock 请求:
|
||||
```
|
||||
GET /api/v1/report/records?status=pending
|
||||
```
|
||||
|
||||
## 出参
|
||||
|
||||
响应 `200`:`ReportRecordsOut`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `records` | list[ReportRecordOut] | 上报记录列表 |
|
||||
| `records[].id` | int | 记录 ID |
|
||||
| `records[].store_name` | string \| null | 门店名 |
|
||||
| `records[].dish_summary` | string \| null | 菜品摘要(如 `香辣鸡腿堡套餐、可口可乐`) |
|
||||
| `records[].original_platform_id` | string \| null | 原最低价来源平台 ID |
|
||||
| `records[].original_platform_name` | string \| null | 原最低价来源平台名 |
|
||||
| `records[].original_price_cents` | int \| null | 原最低价(分) |
|
||||
| `records[].reported_platform_id` | string | 上报平台标识 |
|
||||
| `records[].reported_platform_name` | string | 上报平台名(如 `京东外卖`) |
|
||||
| `records[].reported_price_cents` | int | 上报更低价(分) |
|
||||
| `records[].images` | list[string] | 截图 URL 列表 |
|
||||
| `records[].status` | string | `pending` / `approved` / `rejected` |
|
||||
| `records[].reject_reason` | string \| null | 驳回原因(rejected 时) |
|
||||
| `records[].reward_coins` | int \| null | 通过后发的金币数 |
|
||||
| `records[].created_at` | datetime | 提交时间 |
|
||||
| `counts` | object | 四态计数(不受 status 筛选影响,供前端 chip 展示) |
|
||||
| `counts.all` | int | 总数 |
|
||||
| `counts.pending` | int | 审核中 |
|
||||
| `counts.approved` | int | 已通过 |
|
||||
| `counts.rejected` | int | 未通过 |
|
||||
|
||||
Mock 出参:
|
||||
```json
|
||||
{
|
||||
"records": [
|
||||
{
|
||||
"id": 89,
|
||||
"store_name": "肯德基宅急送(天北路店)",
|
||||
"dish_summary": "香辣鸡腿堡套餐、可口可乐(中)",
|
||||
"original_platform_id": "meituan-waimai",
|
||||
"original_platform_name": "美团外卖",
|
||||
"original_price_cents": 2850,
|
||||
"reported_platform_id": "jd-waimai",
|
||||
"reported_platform_name": "京东外卖",
|
||||
"reported_price_cents": 1880,
|
||||
"images": ["/media/price_report/u42_a1b2c3d4e5f6g7h8.jpg"],
|
||||
"status": "pending",
|
||||
"reject_reason": null,
|
||||
"reward_coins": null,
|
||||
"created_at": "2026-07-03T10:30:00Z"
|
||||
}
|
||||
],
|
||||
"counts": {
|
||||
"all": 3,
|
||||
"pending": 1,
|
||||
"approved": 1,
|
||||
"rejected": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码
|
||||
- `400` 无效的 `status` 值(仅 `pending`/`approved`/`rejected` 合法)
|
||||
- `401` 未鉴权
|
||||
|
||||
## 说明
|
||||
- `counts` 始终基于全量(不受 `status` 筛选影响),供前端筛选 chip 显示各状态数量
|
||||
- 价格单位均为分(`*_cents`),客户端 ÷100 显示元
|
||||
@@ -0,0 +1,57 @@
|
||||
# POST /api/v1/report — 提交上报更低价
|
||||
|
||||
> 所属:Report 组(前缀 `/api/v1/report`) | 鉴权:Bearer | [← 返回 API 索引](../README.md)
|
||||
|
||||
众包纠偏:用户发现比价记录中某平台有更低价格时提交上报。需附截图证明,原最低价由 `comparison_record_id` 反查(不信任客户端传的快照),提交价必须 < 原最低价。提交后 `status=pending`,人工审核通过后发奖。
|
||||
|
||||
## 入参
|
||||
|
||||
**multipart/form-data**:
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| `comparison_record_id` | int | ✅ | 比价记录 ID |
|
||||
| `reported_platform_id` | string | ✅ | 上报平台标识:`meituan-waimai` / `jd-waimai` / `taobao-shanguang` |
|
||||
| `reported_price` | string | ✅ | 用户填的更低价(元,如 `23.5`) |
|
||||
| `images` | file[] | ✅(1–4 张) | 截图证明 |
|
||||
|
||||
Mock 入参(curl 示例):
|
||||
```bash
|
||||
curl -X POST https://app-api.shaguabijia.com/api/v1/report \
|
||||
-H "Authorization: Bearer <access_token>" \
|
||||
-F "comparison_record_id=5678" \
|
||||
-F "reported_platform_id=jd-waimai" \
|
||||
-F "reported_price=18.8" \
|
||||
-F "images=@screenshot1.png" \
|
||||
-F "images=@screenshot2.png"
|
||||
```
|
||||
|
||||
## 出参
|
||||
|
||||
响应 `200`:`ReportSubmitOut`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `id` | int | 上报记录 ID |
|
||||
| `status` | string | 固定 `pending`(待审核) |
|
||||
| `created_at` | datetime | 提交时间(ISO 8601 UTC) |
|
||||
|
||||
Mock 出参:
|
||||
```json
|
||||
{
|
||||
"id": 89,
|
||||
"status": "pending",
|
||||
"created_at": "2026-07-03T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码
|
||||
- `400` 价格不合法(≤0 / 格式错)/ 上报价 ≥ 原最低价 / 图片问题(空/超 4 张/格式不对)/ 不支持的上报平台
|
||||
- `401` 未鉴权
|
||||
- `404` 比价记录不存在或不属于当前用户
|
||||
|
||||
## 说明
|
||||
- 原最低价由 `comparison_record_id` → `ComparisonRecord.best_price_cents` 反查
|
||||
- 校验(D):`reported_price_cents < original_price_cents`,否则 400
|
||||
- 截图落盘 `MEDIA_ROOT/price_report/`,文件名随机防覆盖
|
||||
- 发奖走人工审核(admin 后台操作),不在此端点
|
||||
@@ -0,0 +1,45 @@
|
||||
# POST /api/v1/trace/finalize — 比价 trace 收尾上云
|
||||
|
||||
> 所属:透传端点(前缀 `/api/v1`,外卖比价) | 鉴权:无(MVP 阶段不鉴权) | [← 返回 API 索引](../README.md)
|
||||
|
||||
透传到 pricebot-backend。用户终止 / Phase 1 未识别没走到 done 帧时,pricebot 没上云也没回传 `trace_url`。客户端收尾时打这个,pricebot 按 `trace_id` 一致性 hash 落到处理这条 trace 的同一进程(dir_cache 在那才能算对 trace 目录),打包上云返回 `{trace_url}`。
|
||||
|
||||
## 入参
|
||||
|
||||
透传 pricebot,客户端按 pricebot 协议组装。关键字段:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `device_id` | string | 设备 ID |
|
||||
| `trace_id` | string | 比价 trace 标识 |
|
||||
| *(透传)* | | 其余字段由 pricebot 定义,本端点不做校验 |
|
||||
|
||||
Mock 入参:
|
||||
```json
|
||||
{
|
||||
"device_id": "android_abc123def456",
|
||||
"trace_id": "tr_20260703_m3n4o5p6",
|
||||
"reason": "user_cancelled"
|
||||
}
|
||||
```
|
||||
|
||||
## 出参
|
||||
|
||||
透传 pricebot 原始响应,通常包含 `trace_url`。
|
||||
|
||||
Mock 出参:
|
||||
```json
|
||||
{
|
||||
"trace_url": "https://trace.shaguabijia.com/tr_20260703_m3n4o5p6",
|
||||
"ok": true
|
||||
}
|
||||
```
|
||||
|
||||
## 错误码
|
||||
- `502` pricebot 不可达或返回 5xx
|
||||
- `400` 请求体不是合法 JSON
|
||||
|
||||
## 说明
|
||||
- 一致性 hash 按 `trace_id` 路由到同一 pricebot 实例(确保 dir_cache 命中)
|
||||
- MVP 阶段不鉴权
|
||||
- 与 `/intent/recognize`、`/price/step` 等同属外卖比价透传族
|
||||
Reference in New Issue
Block a user