Files
goods/docs/planning/history/v0.2-go-python-foodfmcg.md
lixu 54175238ad docs: 归档天工·商品标签(OpenGoods)规划文档
新增 docs/planning/ 规划文档归档:
- 00-final-plan: 最终规划(决策+架构+M0~M5任务清单)
- 01-detailed-design-v2.0: 分类/单位/数据库/API/治理/采集/部署/众包
- 02-advanced-topics-v3.0: OpenAPI/全表DDL/数据契约/OFF映射/中国合规/测试/安全/图片/SLA/竞品
- history/: v0.1~v1.0 演进记录
更新 README 为项目介绍并链接规划文档。

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-06-08 06:05:29 +00:00

204 lines
13 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.
# 商品档案公益 API 系统 — 规划方案 (v0.2)
> 公益网站/服务:**采集全网商品信息**,对外提供**商品参数查询 API**。
> 原则:**只收集 + 只提供信息,不涉及任何购买/下单/比价导购**。
> 本版根据你的反馈定稿四件事:① 收录**官方标准零售价(MSRP)** ② 首批聚焦**食品快消** ③ **Go(系统) + Python(采集)** 多语言架构 ④ 附**开放数据源清单**。
---
## 0. 你已确认的决策
| # | 决策 | 说明 |
|---|------|------|
| 1 | **价格 = 官方标准零售价 (MSRP)** | 厂商指导价/官方建议零售价,属**静态属性**,带来源+时间+币种标注;**不收录实时电商售价、不提供购买入口** |
| 2 | **首批品类 = 食品快消 (Food & FMCG)** | 先把食品的数据模型打磨好(成分、营养、过敏原、规格、保质期…) |
| 3 | **技术栈 = Go + Python** | Go 写对外 API/核心服务;Python 写采集/ETL/爬虫;通过 PostgreSQL + 消息队列解耦 |
| 4 | **数据源 = 暂无,后期提供** | 本版先给出可立即接入的开放数据源清单 |
---
## 1. Go + Python 多语言架构(核心)
这是一个很经典且合理的组合。两端**不直接互相调用**,而是通过**共享数据库 + 消息队列**解耦,各自独立部署、独立扩展。
```
┌────────────────────── Python 侧 (采集/数据) ──────────────────────┐
│ │
数据源 ─▶│ 采集 Workers (爬虫/适配器) ─▶ ETL 清洗/标准化/去重 ─▶ 入库 │
│ httpx / Playwright / scrapy pandas / 规则引擎 │
└───────────────────────────┬────────────────────────────────────────┘
│ 写入
┌───────▼────────┐ ┌──────────────┐
│ PostgreSQL │◀──────▶│ 对象存储 S3 │ (商品图)
│ (商品档案主库) │ └──────────────┘
└───────▲────────┘
│ 只读
┌───────────────────────────┴────────────────────────────────────────┐
│ Go 侧 (对外服务) │
│ 公开 API (REST/JSON) + Redis 缓存/限流 + 搜索网关 + OpenAPI │
│ Gin/Echo/Chi/标准库 │
└───────────────────────────┬────────────────────────────────────────┘
各种软件 / 开发者消费
```
### 1.1 职责划分
| 子系统 | 语言 | 职责 |
|--------|------|------|
| **公开 API 服务** | **Go** | 对外只读 API、限流、缓存、鉴权(可选 API Key)、检索网关、高并发承载 |
| **采集 Workers** | **Python** | 每个数据源一个 adapter,抓取/调用 API、遵守 robots、限速、产出原始数据 |
| **ETL / 数据处理** | **Python** | 清洗、字段映射、单位归一、实体去重与合并、质量评分 |
| **调度 / 队列** | Python(worker) + Redis/消息队列 | 定时任务、增量更新、任务分发 |
| **存储** | PostgreSQL + Redis + S3 | 主库 / 缓存+限流 / 图片 |
| **检索** | 初期 PG 全文 → 后期 OpenSearch | 商品名/参数搜索与分面 |
### 1.2 为什么这样分?
- **Go 做 API**:编译型、单二进制部署、并发模型适合高 QPS 的只读公益 API,运维简单。
- **Python 做采集**:爬虫/解析/数据处理生态最强(scrapy、playwright、pandas),迭代快。
- **解耦点 = 数据库**:Go 端**只读**主库(或读副本),Python 端负责写入。两端通过稳定的表结构约定协作,互不阻塞;将来任一端换语言/重写都不影响另一端。
- **契约**:用数据库 schema + 一份内部「数据契约文档」固定字段含义,避免两端理解不一致。
---
## 2. 食品快消数据模型(细化)
食品参数差异大,沿用 **核心字段 + JSONB 灵活属性 + 营养结构化子表**。字段设计大量参考 Open Food Facts(成熟的食品开放库)。
### 2.1 商品主表 `product`
```jsonc
{
"id": "uuid",
"gtin": "6901234567892", // 条码(主键标识), EAN-13/UPC/EAN-8
"name": "示例牌 巧克力榛子酱 400g",
"brand": "示例牌", // -> brand
"manufacturer": "示例食品有限公司", // 生产商
"category": "食品/酱料/巧克力酱", // -> category 树 (可对齐 GS1 GPC / OFF categories)
"net_content": {"value": 400, "unit": "g"}, // 净含量
"country_of_origin": "中国",
"shelf_life": {"value": 12, "unit": "月"}, // 保质期
"storage": "常温避光保存",
"images": ["S3_URL", ...],
"msrp": { ... }, // 官方标准零售价, 见 2.3
"food": { ... }, // 食品专属结构化字段, 见 2.2
"attributes": [ {"key":"","value":"","unit":""} ], // 其余灵活参数(JSONB)
"identifiers": {"ean":"", "upc":"", "off_id":""},
"sources": [ {"source":"", "url":"", "fetched_at":"", "fields":["msrp"]} ],
"quality_score": 0.0,
"status": "active|merged|deprecated",
"created_at": "", "updated_at": ""
}
```
### 2.2 食品专属字段 `food`(结构化)
```jsonc
{
"ingredients_text": "白砂糖, 棕榈油, 榛子(13%), ...", // 配料表原文
"ingredients": [ {"name":"白砂糖","rank":1}, ... ], // 解析后(可选)
"allergens": ["坚果", "大豆", "乳"], // 过敏原
"additives": ["E322 卵磷脂"], // 添加剂
"nutriments": { // 营养成分(每100g/100ml)
"energy_kj": 2252, "energy_kcal": 539,
"fat_g": 30.9, "saturated_fat_g": 10.6,
"carbohydrates_g": 57.5, "sugars_g": 56.3,
"protein_g": 6.3, "salt_g": 0.107
},
"nutrition_basis": "per_100g", // per_100g | per_100ml | per_serving
"serving_size": "15g",
"is_vegetarian": null, "is_vegan": null, // 可空
"nutri_score": "C", // 若引用 OFF
"labels": ["无添加", "清真"] // 认证/标签
}
```
### 2.3 官方标准零售价 `msrp`(重点)
```jsonc
{
"amount": 29.90,
"currency": "CNY",
"type": "msrp", // 仅 msrp/官方指导价; 不存实时电商成交价
"region": "CN", // 适用地区(价格随地区不同)
"source": "厂商官网/官方价目表",
"source_url": "https://...",
"effective_date": "2026-01-01", // 价格生效/采集时间
"note": "官方建议零售价, 实际售价以零售商为准; 本站不提供购买"
}
```
> 设计要点:价格是**带时间戳的历史快照**而非实时报价;明确 `type=msrp`、标注地区与来源;响应里附免责说明。**坚决不出现购买/跳转链接。**
### 2.4 辅助实体
`brand` / `manufacturer` / `category`(品类树) / `source`(数据来源登记) / `attribute_definition`(参数字典: 标准名·别名·单位) / `merge_log`(实体合并记录, 保留溯源)。
---
## 3. 可立即接入的开放数据源清单(食品快消)
按"合规性 / 可用性"排序。这些可作为**种子数据 + 采集 adapter 的首批对象**。
| 数据源 | 内容 | 许可 | 接入方式 | 备注 |
|--------|------|------|----------|------|
| **Open Food Facts** ⭐ | 全球食品(成分/营养/过敏原/Nutri-Score/图片) | **ODbL**(数据)+DbCL+CC-BY-SA(图) | REST API + **每夜全量 dump**(CSV/MongoDB, ~9GB) | 食品首选;可贡献回写;限速 15 req/min/IP(读) |
| **USDA FoodData Central** ⭐ | 美国食品营养成分(含 Branded 品牌库) | **CC0(公共领域)** | REST API(需免费 key) + JSON/CSV 下载 | 营养数据权威;商业可用 |
| **GS1 / Verified by GS1**(中国商品信息服务平台) | 条码→品牌/规格/厂商(官方登记) | 受限(需企业/接口授权) | 网页查询 + API(≤1000 GTIN/次) | **条码→商品**最权威来源;2亿+条;中国数据首选 |
| **brocade.io** | 开放 GTIN/条码产品库 | 开源/开放 | 免费 REST(免鉴权读) | 数据量有限,可作补充 |
| **3023data 等条码接口** | 中国物品编码+UPC+ISBN | 商业(0.005~0.02元/次) | REST API | **付费**,作兜底补全,非首选 |
| 各国**监管公开数据** | 食品备案/标签/能效等 | 多为公开 | 各平台 | 后续按需逐个评估合规 |
**参考用开源项目(架构/数据模型借鉴,非数据源)**
- Open Food Facts Server (Product Opener) — 食品库的完整实现,可学其字段与流程
- UnoPIM / PCMT / brocade.io — 开源 PIM / 商品主数据系统,借鉴建模与去重
> 建议:**先用 Open Food Facts 全量 dump 作种子数据**(直接有海量真实食品),再用 GS1/USDA 做补全与校验。这样 MVP 阶段就有真实可查的数据。
---
## 4. 公开 API 契约(Go 实现,只读)
```
GET /api/v1/products/barcode/{gtin} # ★最常用: 条码查档案
GET /api/v1/products/{id} # 内部ID查
GET /api/v1/products/search # ?q=&brand=&category=&allergen_free=&page=&size=&fields=
GET /api/v1/products/{id}/nutriments # 仅营养
GET /api/v1/products/{id}/msrp # 仅官方零售价(含来源/时间/免责)
GET /api/v1/brands | /categories # 品牌 / 品类树
GET /api/v1/sources/{id} # 数据来源透明说明
GET /healthz | /api/v1/openapi.json # 健康检查 / 机读文档
```
约定:版本化 `/v1/`;统一响应 `{data, meta(分页), sources(溯源)}`;分页 + `fields=` 裁剪;匿名按 IP 限流,可选免费 API Key 提配额;CDN+Redis 缓存(参数变化慢,命中率高);数据采用开放许可(CC BY / ODbL,注意 OFF 的 ODbL 传染性);**无任何购买/交易端点**。
---
## 5. 合规与边界(公益项目重点)
- **数据源许可要分清**:OFF 是 **ODbL**(衍生数据库需同样开放+署名),USDA 是 **CC0**(最宽松)。混用时要按最严格许可对外标注,避免许可冲突。
- 爬取守 robots.txt / 服务条款,礼貌限速,标明 User-Agent 身份。
- 只采**客观参数**;营销文案/评测原文不照搬(链接来源即可)。
- 无个人数据(PII),只处理商品信息。
- 站点显著声明:**仅提供信息、不提供购买、不构成消费建议**;价格为官方指导价历史快照。
- 提供权利方**纠错/下架**联系渠道。
---
## 6. 里程碑(仍不写代码,仅规划,供确认)
| 阶段 | 目标 | 关键产出 |
|------|------|----------|
| **M0 工程地基** | 仓库骨架 | Go API 骨架 + Python 采集骨架 + PostgreSQL + Docker Compose + CI + 数据契约文档 |
| **M1 数据模型** | 食品 schema | 主表/食品字段/MSRP/辅助实体 的迁移与字典 |
| **M2 种子数据** | 有真实数据 | 导入 Open Food Facts dump(食品子集) + USDA 营养补全 |
| **M3 MVP API (Go)** | 可查询 | 条码/ID/搜索/营养/MSRP + OpenAPI 文档 + 限流缓存 |
| **M4 采集管线 (Python)** | 自动更新 | 1~2 个 adapter(OFF API / GS1) + ETL + 去重 + 质量评分 + 调度 |
| **M5 开放与规模化** | 上线 | 搜索引擎 + CDN + API Key + 众包纠错后台 + 开发者文档站 + 开放数据许可 |
---
## 7. 待你确认/补充
1. **价格范围**:确认只收「官方指导价 (MSRP)」、不碰实时电商价?(建议是)
2. **OFF 的 ODbL 许可**:可接受(意味着我们对外的数据库也要用 ODbL 并署名 OFF)?还是更想用 CC0 来源(USDA)为主以保持宽松?
3. **种子数据**:同意先导入 Open Food Facts 食品 dump 作为启动数据吗?
4. **Go Web 框架偏好**Gin / Echo / Chi / 标准库 net/http,有偏好吗?(无偏好我默认 Chi 或标准库,轻量)
5. **地域范围**:首批面向中国市场商品,还是中外都收?(影响优先用 GS1-China 还是 OFF 全球库)
> 你确认后,我把它定为 v1.0 规划,并据此拆成可执行的工程任务清单(仍按你的节奏,需要我动手写代码时再开始)。