Files
goods/docs/api.md
busenalbantoglu51424 a61d1c558c
CI / Go (api) (pull_request) Successful in 1m2s
CI / Python (ingestion) (pull_request) Successful in 27s
CI / Migrations (postgres) (pull_request) Successful in 24s
docs: 补充档案回流接口调用说明(docs/api.md + 前台 API 文档页)
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-25 01:41:39 +00:00

176 lines
6.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OpenGoods 公共 API 开发者文档
天工商品档案公共仓(OpenGoods)提供**公开、只读**的商品事实 REST API:按条码/名称查询商品的客观资料(品牌、品类、净含量、产地、配料、营养成分、Nutri-Score、厂商建议零售价快照等)。返回均为 JSON(UTF-8)。**本服务不含任何购买/交易接口。**
- 基础地址:`https://goods.tangshasha.com/api/v1`
- 机器可读规范(OpenAPI 3):`GET /api/v1/openapi.json`
- 交互式文档:站点「API 调用说明」页
## 鉴权
API 默认**匿名可用**,无需任何凭证即可调用。匿名请求按来源 IP 计入一个较低的默认每分钟额度。
如需更高额度并让用量归属到你,可向运营方申请一枚 **API Key**(形如 `og_live_xxxxxxxx`),请求时二选一携带:
```bash
curl -H "X-API-Key: og_live_xxxxxxxx" \
"https://goods.tangshasha.com/api/v1/products/search?q=牛奶"
# 或
curl -H "Authorization: Bearer og_live_xxxxxxxx" \
"https://goods.tangshasha.com/api/v1/products/search?q=牛奶"
```
> 仅在创建时返回一次明文 Key,请妥善保存。服务端只存储其 SHA-256 哈希。
## 限流
采用**固定窗口**限流(每分钟)。每个响应都会回写以下响应头:
| 响应头 | 含义 |
| --- | --- |
| `X-RateLimit-Limit` | 当前窗口允许的最大请求数 |
| `X-RateLimit-Remaining` | 当前窗口剩余可用次数 |
| `X-RateLimit-Reset` | 窗口重置的 Unix 时间戳(秒) |
| `Retry-After` | 仅在超额(429)时返回,建议等待的秒数 |
- 超过额度:`429 Too Many Requests`,错误码 `rate_limited`
- Key 无效或已吊销:`401 Unauthorized`,错误码 `invalid_api_key`
## 错误格式
非 2xx 响应体统一为:
```json
{ "error": { "code": "not_found", "message": "…", "request_id": "…" } }
```
## 分页
列表类接口支持 `page`(默认 `1`)与 `size`(默认 `20`,最大 `100`),响应含 `page`/`size`/`total`
## 端点
### `GET /products/search` — 搜索商品
按名称做三元组(trigram)模糊搜索,**可容忍错别字**;支持品类/品牌/产地过滤;结果按相关度(名称相似度 × 数据质量分)排序。
| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `q` | 否 | 关键词(名称/条码),模糊匹配;留空则按质量分返回全部 |
| `category` | 否 | 品类编码(含子树),如 `food.beverages` |
| `brand` | 否 | 品牌名(模糊匹配),如 `Ferrero` |
| `country` | 否 | 产地前缀(不区分大小写),如 `China` |
| `page` | 否 | 页码,默认 1 |
| `size` | 否 | 每页条数,默认 20,最大 100 |
```bash
curl "https://goods.tangshasha.com/api/v1/products/search?q=nutela&country=Italy"
```
```json
{
"items": [
{
"id": "…",
"gtin": "3017624010701",
"name": "Nutella",
"brand": "Ferrero",
"category_path": "food.snacks.chocolate",
"country_of_origin": "Italy",
"quality_score": 0.81,
"score": 0.71
}
],
"page": 1,
"size": 20,
"total": 1
}
```
`score` 为名称相关度(提供 `q` 时返回,01),未提供 `q` 时为 `null`
### `GET /products/barcode/{gtin}` — 按条码查询
```bash
curl "https://goods.tangshasha.com/api/v1/products/barcode/5449000000996"
```
### `GET /products/{id}` — 商品详情
按商品 UUID 获取完整档案(含配料、营养、添加剂、图片、MSRP 等)。
### `GET /products/{id}/nutriments` — 商品营养成分
### `GET /products/{id}/msrp` — 厂商建议零售价快照
官方建议零售价历史快照,仅供参考,不含任何购买入口。
### `GET /brands` — 品牌列表(分页)
### `GET /categories` — 品类树
### `GET /sources/{id}` — 数据来源
## 档案回流(写接口,需 API Key)
> 仅供进销存等机器调用方使用:把档案里**尚未收录**的商品批量回流到站点,进入人工审核队列,审核通过后才会收录。**必须携带 API Key**(与上文同一类 `og_live_` 密钥),不会直接写入商品。
### `POST /api/public/backflow` — 批量回流未收录商品
- 鉴权:请求头携带 `X-API-Key: og_live_xxxxxxxx`(或 `Authorization: Bearer og_live_xxxxxxxx`)。缺失/无效/已吊销返回 `401`
- 请求体:商品对象**数组**(与公众投稿同结构),单次最多 `1000` 条。常用字段:
| 字段 | 必填 | 说明 |
| --- | --- | --- |
| `name` | 是 | 商品名称 |
| `gtin` | 否 | 条码(GTIN)。强烈建议提供,用于去重 |
| `brand_name` | 否 | 品牌名 |
| `category_id` | 否 | 品类编码,如 `food.beverages` |
| `net_content_value` / `net_content_unit` | 否 | 净含量数值 / 单位 |
| `country_of_origin` | 否 | 产地 |
| `ingredients_text` | 否 | 配料表 |
| `nutriments` | 否 | 营养成分对象 |
| `msrp` | 否 | 零售价快照数组,元素含 `amount`/`currency`/`region`/`effective_date` |
| `note` | 否 | 备注 |
> 来源会自动标记为 `source="backflow"`,在后台审核队列中与公众投稿区分,无需调用方提供。
- 去重(按 `gtin` 逐条判断,互不影响):
- 该条码已收录为商品 → `exists`,跳过;
- 已存在同条码的待审核回流 → `duplicate`,跳过(避免反复刷队列);
- 否则入队 → `queued`status=`pending`,等待后台审核);
- 名称为空等 → `invalid`
```bash
curl -X POST "https://goods.tangshasha.com/api/public/backflow" \
-H "X-API-Key: og_live_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '[
{"name":"某某牛奶 250ml","gtin":"6901234567890","brand_name":"某品牌",
"net_content_value":250,"net_content_unit":"ml",
"msrp":[{"amount":3.5,"currency":"CNY","region":"CN"}]},
{"name":"已收录商品","gtin":"5449000000996"}
]'
```
```json
{
"total": 2,
"queued": 1,
"exists": 1,
"duplicate": 0,
"invalid": 0,
"results": [
{ "gtin": "6901234567890", "name": "某某牛奶 250ml", "status": "queued", "id": "<submission-id>" },
{ "gtin": "5449000000996", "name": "已收录商品", "status": "exists", "reason": "该条码商品已收录" }
]
}
```
审核通过后,系统按提交内容**新建商品**;若审核时该条码已存在商品,则**补全**到已有商品(逻辑与公众投稿一致)。
## 免责声明
数据可能存在误差或滞后,按「现状」提供,不构成医疗/购买建议。商品资料版权归各原始来源所有,请遵循其许可(如 OpenFoodFacts 的 ODbL),引用时请注明天工商品档案公共仓及原始来源。