feat(trading): support multi-item purchase intents

This commit is contained in:
2026-08-31 11:41:39 +08:00
parent 865bbe3724
commit f76c0a1518
7 changed files with 291 additions and 57 deletions
+78
View File
@@ -11,6 +11,7 @@ clear_cart 与 _dump_debug_snapshot 用不依赖 Playwright 的 fake page 覆盖
"""
from __future__ import annotations
import asyncio
import json
from pathlib import Path
from typing import Any
@@ -39,6 +40,7 @@ from app.trading.worker.site_interact import (
_DELETE_BUTTON_SELECTOR,
_extract_error_message,
_extract_purchase_fields,
_normalize_intent_items,
_OrderListAccumulator,
_parse_checkout_summary,
_parse_initial_state,
@@ -70,6 +72,82 @@ def _wrap_state(state: dict[str, Any]) -> str:
)
# ---- 下单意图兼容 ----
def test_normalize_intent_items_accepts_legacy_single_item():
assert _normalize_intent_items({
"item_url": "https://item.rakuten.co.jp/shop/x/",
"quantity": 2,
}) == [{
"item_url": "https://item.rakuten.co.jp/shop/x/",
"quantity": 2,
"variant_id": None,
"choice": None,
}]
def test_normalize_intent_items_accepts_multiple_items_and_string_urls():
assert _normalize_intent_items({
"items": [
{"item_url": "https://item.rakuten.co.jp/shop/x/", "quantity": 2},
"https://item.rakuten.co.jp/shop/y/",
]
}) == [
{
"item_url": "https://item.rakuten.co.jp/shop/x/",
"quantity": 2,
"variant_id": None,
"choice": None,
},
{
"item_url": "https://item.rakuten.co.jp/shop/y/",
"quantity": 1,
"variant_id": None,
"choice": None,
},
]
def test_normalize_intent_items_rejects_empty_items():
with pytest.raises(InvalidRequestError):
_normalize_intent_items({"items": []})
async def test_add_to_cart_processes_all_items_and_keeps_legacy_state():
site = SiteInteractor.__new__(SiteInteractor)
site._lock = asyncio.Lock()
site._per_task_state = {}
calls: list[dict[str, Any]] = []
async def fake_add(**kwargs):
calls.append(kwargs)
index = len(calls)
return {
"item_id": str(index),
"shop_bid": "shop",
"basket_domain": "https://basket",
"response_html": f"html-{index}",
"screenshot": b"",
}
site._add_to_cart_with_fields = fake_add
snapshot = await site.add_to_cart(_make_task(intent={
"items": [
{"item_url": "https://item.rakuten.co.jp/shop/x/"},
{"item_url": "https://item.rakuten.co.jp/shop/y/", "quantity": 3},
]
}))
assert [call["item_url"] for call in calls] == [
"https://item.rakuten.co.jp/shop/x/",
"https://item.rakuten.co.jp/shop/y/",
]
assert calls[1]["quantity"] == 3
assert site._per_task_state["t1"]["item_id"] == "1"
assert site._per_task_state["t1"]["item_ids"] == ["1", "2"]
assert snapshot.html == "html-2"
# ---- _parse_initial_state ----