111 lines
3.7 KiB
Python
111 lines
3.7 KiB
Python
# 药帮忙加入购物车。
|
||
|
||
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="加购失败")
|