"""乐天市场(ichiba)加购字段抽取的纯函数与常量 `__INITIAL_STATE__.purchase` 的字段映射在两处使用: - `app/scraping/parsers/item.py::_purchase_info` — `/api/item_detail` 返回的 PurchaseInfo - `app/trading/worker/site_interact.py::_extract_purchase_fields` — trading 加购链路 两者都解析同一份站点 JSON,本模块抽出**双方都要用**的核心逻辑: - 常量:普通购买事件标识、库存类型 → inventory_flag 映射 - `inventory_flag_for(inventory_type)` — 多规格判定 - `basket_domain_of(sell_type)` — 抽 basketDomain + 反转义 `\\u002F` - `base_form_fields(shop_id, item_id, inventory_flag)` — 加购表单四件套 **不**放这里: - `PurchaseInfo` pydantic 模型 — 是 scraping 的对外契约,4 个子站共用,留在 scraping/models - variant_id / choice 自动选择策略 — trading 独有(scraping 把决策权留给上游) - options 结构化解析 — scraping 独有(trading 用原始 dict) - 子站(books/biccamera/brandavenue)的加购契约 — 各自独立,不走这里 依赖约束:仅 `typing.Any`(标准库),不 import scraping / trading / gateway, 符合架构测试 tests/test_architecture.py 的依赖方向规则。 """ from __future__ import annotations from typing import Any # 站点前端构造加购表单时固定带的事件标识(普通购买 normalPurchase) NORMAL_PURCHASE_EVENT: str = "ES01_003_001" # 库存类型 → inventory_flag # 多规格商品(sku.variants[] 非空)inventoryType="multiple" → flag "2" # 单一库存商品 → flag "1" # 注意:scraping 早期版本写过 int 2/1,但表单最终要 str,统一在这里就转成 str INVENTORY_FLAG_MULTIPLE: str = "2" INVENTORY_FLAG_DEFAULT: str = "1" _INVENTORY_FLAG_MAP: dict[str, str] = {"multiple": INVENTORY_FLAG_MULTIPLE} def inventory_flag_for(inventory_type: Any) -> str: """从 __INITIAL_STATE__.purchase.sku.inventoryType 取 inventory_flag inventory_type="multiple" → "2"(多规格,调用方需选 variant_id) 其他(含 None / 未知值)→ "1"(单一库存) """ key = inventory_type if isinstance(inventory_type, str) else "" return _INVENTORY_FLAG_MAP.get(key, INVENTORY_FLAG_DEFAULT) def basket_domain_of(sell_type: dict[str, Any]) -> str: """从 purchase.sellType.normalPurchase 抽 basketDomain 并反转义 站点 JSON 里 `/` 被编码为 `\\u002F`,POST 前要还原。 sell_type 不是 dict 或 basketDomain 缺失时返回空串。 """ if not isinstance(sell_type, dict): return "" raw = sell_type.get("basketDomain") if not isinstance(raw, str): return "" return raw.replace("\\u002F", "/") def base_form_fields( *, shop_id: Any, item_id: Any, inventory_flag: str, ) -> dict[str, str]: """构造加购表单的基础四件套 shop_bid / item_id / inventory_flag / __event 是 rakuten 主站加购的固定字段。 variant_id / choice / units 由调用方按业务策略再加(trading 自动选,scraping 留给上游)。 """ return { "shop_bid": str(shop_id) if shop_id else "", "item_id": str(item_id) if item_id else "", "inventory_flag": inventory_flag, "__event": NORMAL_PURCHASE_EVENT, }