# 药帮忙加入购物车。 from __future__ import annotations import requests import urllib3 import selection_path # noqa: F401 from cart_add import KIND_OK, KIND_SYSTEM, AddOutcome, classify_mall_message from cart_match import CartOffer from http_util import set_session_cookies urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # 搜索页加车现行地址。旧口 /new-front/cart/add-product 已 404。 YBM_ADD_URL = "https://www.ybm100.com/new-front/cart/change-cart" # 按再搜到的 sku 把数量加入药帮忙购物车。没有 sku 当系统失败。 def add_ybm_cart( cookies: dict[str, str], offer: CartOffer, qty: int, *, session: requests.Session | None = None, ) -> AddOutcome: sku = _sku_id(offer) if not sku: return AddOutcome(kind=KIND_SYSTEM, reason="缺少 sku") own = session is None client = session or requests.Session() try: if own: set_session_cookies(client, cookies) response = client.post( YBM_ADD_URL, json=_body(offer, sku, qty), headers=_headers(), timeout=30, verify=False, ) response.raise_for_status() return _outcome_from_json(response.json()) except Exception as exc: return AddOutcome(kind=KIND_SYSTEM, reason=str(exc)) finally: if own: client.close() # 从报价里取药帮忙加车用的 skuId。 def _sku_id(offer: CartOffer) -> str: raw = _raw(offer) return str(raw.get("skuId") or raw.get("id") or offer.sku_id or "") # 搜索页加车体:amount 是数量,direct=1 是列表加车。merchantId 有就带,没有留空。 def _body(offer: CartOffer, sku: str, qty: int) -> dict: raw = _raw(offer) merchant = raw.get("merchantId") if merchant in (None, ""): merchant = "" return { "skuId": int(sku) if str(sku).isdigit() else sku, "amount": int(qty), "merchantId": merchant, "mddata": "", "direct": 1, "qtdata": "{}", } # 报价里的原始商品字段。没有 raw 就用 extra 本身。 def _raw(offer: CartOffer) -> dict: extra = offer.extra if isinstance(offer.extra, dict) else {} raw = extra.get("raw") if isinstance(extra.get("raw"), dict) else extra return raw if isinstance(raw, dict) else {} # 药帮忙加车请求头。 def _headers() -> dict[str, str]: return { "Accept": "application/json, text/plain, */*", "Content-Type": "application/json", "Origin": "https://www.ybm100.com", "Referer": "https://www.ybm100.com/new/", "X-Requested-With": "XMLHttpRequest", "User-Agent": ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36" ), } # 把加车回包收成成功、业务失败或系统失败。看不懂的回包当系统失败。 # # 现行口成功时顶层是 code=200 / msg=操作成功。data.message 可能带中包装提示, # 页面仍当加车成功;选品也不再按中包装拆量,这里不把提示打成失败。 def _outcome_from_json(payload: object) -> AddOutcome: if not isinstance(payload, dict): return AddOutcome(kind=KIND_SYSTEM, reason="加购返回异常") success = payload.get("success") code = payload.get("code") if success is True or code in (0, "0", 200, "200"): return AddOutcome(kind=KIND_OK) data = payload.get("data") if isinstance(payload.get("data"), dict) else {} blob = str(data.get("message") or payload.get("msg") or payload.get("message") or "").strip() if blob: return classify_mall_message(blob) return AddOutcome(kind=KIND_SYSTEM, reason="加购失败")