feat(ingestion): import bypos-collector JSONL into goods #20

Merged
lixu merged 1 commits from devin/1782268418-bypos-importer into main 2026-06-24 10:35:27 +08:00
Owner

Summary

新增「采集器 JSONL → goods/天工库」的导入链路,把 tools/bypos-collector 采到的中心库商品档案入库。只导入 status=hit 的记录,按 GTIN 去重 upsert,重复导入幂等。本地用 docker postgres(全部迁移)实测 49 项测试全过,并端到端导入实采的 demo/products JSONL 验证。

改动

  • adapters/bypos.py — 新增纯函数 transform_bypos(rec):把采集器一行 JSONL 转成内部 product 形状。
    • barcode→gtin(校验 EAN 校验位);name→name;
    • spec 仅当能解析为质量/体积(如 500mL/5kg)时填 net_content,否则原样留在 attributes.spec(20支/1X24 这类计数规格不污染净含量);
    • unit/area/license/in_price/sell_price 存入 attributes;sell_price>0 → msrp;
    • 69 开头条码 country_of_origin="中国",产地省市留 attributes.origin_area
  • etl/load.py — 新增 ensure_bypos_source / load_bypos_record(_safe)_ensure_manufacturer
    • 无 ingredients/营养 → 不写 food_detail;
    • MSRP/product_source 按 source 删后插,故重复导入不产生重复行;
    • upsert 时 attributes = product.attributes || EXCLUDED.attributes(合并而非覆盖)。
  • jobs/import_bypos.py — CLI:python -m opengoods.jobs.import_bypos --input products.jsonl [--limit N] [--dsn ...],支持 .jsonl/.jsonl.gz
  • tests/test_bypos.py — transform 纯函数用例 + DB roundtrip(无库自动跳过)+ 幂等校验。
  • tools/bypos-collector/README.md — 补充导入用法与字段映射。

实测

$ python -m opengoods.jobs.import_bypos --input demo.jsonl
loaded=4 skipped=0 errored=0
$ python -m opengoods.jobs.import_bypos --input products.jsonl
loaded=3 skipped=6 errored=0   # 6 = 1 miss + 5 范围未命中,正确跳过

入库结果(节选):6920459905012 康师傅冰红茶490ml | 490ml | 中国 | 瓶 | 浙江杭州 | MSRP 3.00。来源记为 bypos中心库,带字段级 provenance。

注:bypos中心库 为厂商目录数据(非开放许可),source.license 标为 proprietary,公开站对外展示/导出需注意来源约束。

## Summary 新增「采集器 JSONL → goods/天工库」的导入链路,把 `tools/bypos-collector` 采到的中心库商品档案入库。只导入 `status=hit` 的记录,按 GTIN 去重 upsert,**重复导入幂等**。本地用 docker postgres(全部迁移)实测 49 项测试全过,并端到端导入实采的 demo/products JSONL 验证。 ### 改动 - `adapters/bypos.py` — 新增纯函数 `transform_bypos(rec)`:把采集器一行 JSONL 转成内部 product 形状。 - `barcode→gtin`(校验 EAN 校验位);`name→name`; - `spec` 仅当能解析为质量/体积(如 `500mL`/`5kg`)时填 `net_content`,否则原样留在 `attributes.spec`(`20支`/`1X24` 这类计数规格不污染净含量); - `unit/area/license/in_price/sell_price` 存入 `attributes`;`sell_price>0 → msrp`; - 69 开头条码 `country_of_origin="中国"`,产地省市留 `attributes.origin_area`。 - `etl/load.py` — 新增 `ensure_bypos_source` / `load_bypos_record(_safe)` 与 `_ensure_manufacturer`。 - 无 ingredients/营养 → **不写 `food_detail`**; - MSRP/`product_source` 按 source **删后插**,故重复导入不产生重复行; - upsert 时 `attributes = product.attributes || EXCLUDED.attributes`(合并而非覆盖)。 - `jobs/import_bypos.py` — CLI:`python -m opengoods.jobs.import_bypos --input products.jsonl [--limit N] [--dsn ...]`,支持 `.jsonl`/`.jsonl.gz`。 - `tests/test_bypos.py` — transform 纯函数用例 + DB roundtrip(无库自动跳过)+ 幂等校验。 - `tools/bypos-collector/README.md` — 补充导入用法与字段映射。 ### 实测 ``` $ python -m opengoods.jobs.import_bypos --input demo.jsonl loaded=4 skipped=0 errored=0 $ python -m opengoods.jobs.import_bypos --input products.jsonl loaded=3 skipped=6 errored=0 # 6 = 1 miss + 5 范围未命中,正确跳过 ``` 入库结果(节选):`6920459905012 康师傅冰红茶490ml | 490ml | 中国 | 瓶 | 浙江杭州 | MSRP 3.00`。来源记为 `bypos中心库`,带字段级 provenance。 注:`bypos中心库` 为厂商目录数据(非开放许可),source.license 标为 `proprietary`,公开站对外展示/导出需注意来源约束。
lixu added 1 commit 2026-06-24 10:34:19 +08:00
feat(ingestion): import bypos-collector JSONL into goods
CI / Python (ingestion) (pull_request) Successful in 16s
CI / Migrations (postgres) (pull_request) Successful in 24s
CI / Go (api) (pull_request) Successful in 51s
f04da0a135
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
lixu merged commit 8f9a03a929 into main 2026-06-24 10:35:27 +08:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: lixu/goods#20