# 商品档案公益 API 系统 — 规划方案 (v1.0 定稿) > 公益网站/服务:采集全网商品信息,提供商品参数查询 API。**只收集 + 只提供信息,不涉及任何购买行为。** --- ## 1. 已锁定的决策 | # | 决策 | 结论 | |---|------|------| | 1 | 品类 | 首批 **食品快消** | | 2 | 价格 | 只收 **官方标准零售价 (MSRP)**,静态字段,带来源/时间/地区/币种 + 免责说明;**不收实时电商价、不提供购买入口** | | 3 | 技术栈 | **Go**(对外API/核心服务) + **Python**(采集/ETL/爬虫),经 PostgreSQL + Redis/队列解耦 | | 4 | 种子数据 | ✅ **先导入 Open Food Facts 食品 dump**,最快拥有真实数据 | | 5 | 数据许可 | 因采用 OFF → 对外数据库用 **ODbL** 并署名来源;CC0 来源(USDA)可自由混入 | ### 1.1 我先用的默认值(如不同意请指出,否则按此执行) - **Go Web 框架**:`chi` + 标准库 `net/http`(轻量、稳定、易维护)。 - **地域范围**:先用 OFF **全球食品库**起步,后续接 **GS1-China** 补强中国市场数据。 - **数据库迁移工具**:Go 侧用 `golang-migrate`(纯 SQL 迁移,两端共享同一套 schema)。 - **部署**:初期 Docker Compose 一键起全套(Postgres/Redis/Go API/Python worker)。 --- ## 2. 目标架构(定稿) ``` 数据源(OFF dump / OFF API / USDA / GS1) │ ▼ Python: 采集 adapters → ETL(清洗/归一/去重/质量评分) │ ┌────▼─────────┐ 图片 ┌──────────┐ │ PostgreSQL │◀───────▶│ S3/MinIO │ │ (商品档案主库)│ └──────────┘ └────▲─────────┘ │ 只读 (+Redis缓存/限流) ▼ Go: 公开 REST API + OpenAPI 文档 各种软件 / 开发者 ``` - **解耦契约**:两端通过共享 PostgreSQL schema + 一份《数据契约文档》协作,互不直接调用。 - **Go 端只读主库**(或读副本);**Python 端负责写入**。 --- ## 3. 仓库结构(计划,写代码时落地) ``` goods/ ├── README.md ├── docker-compose.yml # postgres + redis + minio + api + worker ├── docs/ │ ├── data-contract.md # 两端共享的字段契约 │ └── openapi.yaml # API 契约 ├── migrations/ # 共享 SQL 迁移 (golang-migrate) ├── api/ # Go: 对外只读 API │ ├── cmd/server/main.go │ ├── internal/{handler,store,model,middleware}/ │ └── go.mod └── ingestion/ # Python: 采集 + ETL ├── pyproject.toml ├── adapters/{openfoodfacts,usda,gs1}.py ├── etl/{normalize,dedup,quality}.py └── jobs/{seed_off_dump,scheduler}.py ``` ## 4. 数据模型 & API 契约 (沿用 v0.2:`product` 主表 + `food` 食品字段 + `msrp` 价格 + 辅助实体;API 以 `GET /products/barcode/{gtin}` 为核心,全只读、无交易端点。详见 v0.2 附件。) --- ## 5. 可执行任务拆分(按里程碑,写代码时逐项落地) **M0 — 工程地基** - [ ] 初始化 Go module (`api/`) + Python 项目 (`ingestion/`) - [ ] `docker-compose.yml`:Postgres + Redis + MinIO - [ ] CI(Go: build/vet/test;Python: ruff/pytest) - [ ] `docs/data-contract.md` 初版 **M1 — 数据模型** - [ ] `migrations/`:product / food / msrp / brand / manufacturer / category / source / attribute_definition / merge_log - [ ] JSONB + GIN 索引;gtin 唯一索引 **M2 — 种子数据 (Python)** - [ ] 下载 OFF 食品 dump(CSV) - [ ] `seed_off_dump`:字段映射 → 入库(含营养/成分/过敏原/图片URL) - [ ] USDA(CC0) 营养补全(可选) **M3 — MVP API (Go)** - [ ] 路由 + handler:barcode / id / search / nutriments / msrp / brands / categories / sources - [ ] 统一响应、分页、`fields=` 裁剪、错误处理 - [ ] Redis 缓存 + IP 限流;`/healthz` + OpenAPI 文档 **M4 — 采集管线 (Python)** - [ ] adapter:OFF API(增量更新)+ GS1(条码补全) - [ ] ETL:清洗/单位归一/去重合并/质量评分/溯源 - [ ] 调度(定时增量更新) **M5 — 开放与规模化** - [ ] 搜索引擎(PG 全文 → OpenSearch)、CDN 缓存 - [ ] 免费 API Key(防滥用+统计)、众包纠错后台 - [ ] 开发者文档站 + 开放数据许可声明 + 站点"不提供购买"声明 --- ## 6. 下一步 规划已定稿。**你说先不写代码,所以我暂停在这里**。等你说"开始",我就从 **M0 工程地基** 动手,搭好骨架后开 PR 给你看。也可以先只做某个里程碑(比如先 M0+M1 把骨架和数据模型立起来)。