786b7d3721
- api/: Go(chi) 只读 API 骨架, /healthz + 版本化路由(占位), Dockerfile, 单测 - ingestion/: Python 采集/ETL 包骨架, units 单位归一化(纯函数+测试), adapter 协议 - docker-compose.yml: postgres + redis + minio + api - .github/workflows/ci.yml: Go build/vet/test + Python ruff/pytest - docs/data-contract.md(两端共享契约) + docs/disclaimer.md(不提供购买声明) - migrations/ 占位(M1 起填充) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
37 lines
1.9 KiB
Markdown
37 lines
1.9 KiB
Markdown
# 数据契约 (Data Contract) v0.1
|
||
|
||
本契约是 Go(API) 与 Python(ingestion) 两端共享的"事实约定",避免两端对字段含义理解不一致。
|
||
|
||
> 写入责任:**仅 Python (ingestion) 通过 ETL 写入数据库**;Go (API) **只读**。所有写入必须经过单位归一化与字段级溯源。
|
||
|
||
## 1. 边界原则
|
||
- 系统只采集与提供**客观商品信息**;不包含任何购买/交易语义的字段或端点。
|
||
- 价格仅收录**官方建议零售价 (MSRP)** 的静态快照,必须带 `currency`/`region`/`source`/`effective_date`。
|
||
|
||
## 2. 固定枚举
|
||
| 字段 | 取值 |
|
||
|------|------|
|
||
| `product.status` | `active` / `merged` / `deprecated` |
|
||
| `food_detail.nutrition_basis` | `per_100g` / `per_100ml` / `per_serving` |
|
||
| `unit.dimension` | `mass` / `volume` / `energy` / `count` / `ratio` / `length` / `duration` |
|
||
| `source.license` | `ODbL` / `CC0` / `proprietary` / ... |
|
||
| `product_image.kind` | `front` / `ingredients` / `nutrition` / `other` |
|
||
|
||
## 3. 单位规则
|
||
- 数值字段同时保存**原始值 + 单位**与**归一化值 + 基准单位**(canonical)。
|
||
- 质量 → `g`,体积 → `ml`,能量 → `kJ`(同时保留 `kcal`)。
|
||
- 归一化逻辑由 `ingestion/opengoods/units.py` 提供(纯函数,含测试),换算因子是唯一事实来源。
|
||
- 营养成分统一折算到品类模板规定的基准(`per_100g` / `per_100ml`)。
|
||
|
||
## 4. 标识与可空性
|
||
- `product.gtin`:8/12/13/14 位数字,可空(无条码商品),非空时全局唯一。
|
||
- `product.quality_score` ∈ [0, 1]。
|
||
- 货币用 ISO 4217(`CNY` 等),国家/地区用简短代码(`CN` 等)。
|
||
|
||
## 5. 溯源 (Provenance)
|
||
- 每条数据通过 `product_source` 记录来源、URL、贡献字段、抓取时间与原始快照。
|
||
- 对外 API 在 `sources` 中透明返回来源与其许可。
|
||
|
||
## 6. 版本
|
||
- 本契约随 schema 演进版本化;任何 schema 变更需同步更新:迁移(SQL) + 本契约 + `docs/openapi.yaml`。
|