feat(trading): support multi-item purchase intents
This commit is contained in:
@@ -15,7 +15,7 @@ from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Any
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from app.shared.errors import AppError
|
||||
@@ -55,24 +55,44 @@ def _normalize_item_url(url: str | None) -> str | None:
|
||||
return f"{parts.scheme}://{parts.netloc}{parts.path.rstrip('/')}"
|
||||
|
||||
|
||||
def _intent_item_urls(intent: dict[str, Any]) -> list[str]:
|
||||
"""读取新旧意图中的商品 URL,供恢复核对使用。"""
|
||||
raw_items = intent.get("items")
|
||||
if raw_items is None:
|
||||
raw_items = [intent]
|
||||
if not isinstance(raw_items, list):
|
||||
return []
|
||||
urls: list[str] = []
|
||||
for item in raw_items:
|
||||
if isinstance(item, str):
|
||||
url = item
|
||||
elif isinstance(item, dict):
|
||||
url = item.get("item_url")
|
||||
else:
|
||||
url = None
|
||||
normalized = _normalize_item_url(url)
|
||||
if normalized:
|
||||
urls.append(normalized)
|
||||
return urls
|
||||
|
||||
|
||||
async def verify_on_site(
|
||||
task: LeaseTask, *, gateway: "GatewayClient", site: "SiteInteractor"
|
||||
) -> VerifyResult:
|
||||
"""核对一笔任务是否已在站点上下过单
|
||||
|
||||
核对链路:intent.item_url → 查任务创建时间(GET /api/orders/{task_id},
|
||||
LeaseTask 本身不带 created_at)→ 拉「创建时间之后」的订单列表 → 按商品 URL
|
||||
比对。任何一环拿不到足够信息都返回 UNKNOWN,不猜——尤其是 NOT_ORDERED,
|
||||
核对链路:intent.items(或兼容的 intent.item_url)→ 查任务创建时间
|
||||
(GET /api/orders/{task_id},LeaseTask 本身不带 created_at)→ 拉「创建时间之后」
|
||||
的订单列表 → 按商品 URL 比对。任何一环拿不到足够信息都返回 UNKNOWN,不猜——尤其是 NOT_ORDERED,
|
||||
只有在确认翻完了窗口内的全部订单后才允许返回,否则「没找到」可能只是没翻
|
||||
到那一页。
|
||||
"""
|
||||
intent = task.intent or {}
|
||||
item_url = intent.get("item_url")
|
||||
if not item_url:
|
||||
targets = set(_intent_item_urls(intent))
|
||||
if not targets:
|
||||
return VerifyResult(
|
||||
VerifyVerdict.UNKNOWN, detail="intent 缺 item_url,无法比对商品"
|
||||
VerifyVerdict.UNKNOWN, detail="intent 缺商品 URL(item_url/items),无法比对商品"
|
||||
)
|
||||
target = _normalize_item_url(item_url)
|
||||
|
||||
try:
|
||||
task_detail = await gateway.get_task(task.task_id)
|
||||
@@ -96,11 +116,16 @@ async def verify_on_site(
|
||||
detail=f"订单列表查询失败:{type(exc).__name__}: {exc}",
|
||||
)
|
||||
|
||||
matches = [
|
||||
entry
|
||||
for entry in window.entries
|
||||
if any(_normalize_item_url(it.item_url) == target for it in entry.items)
|
||||
]
|
||||
matches = []
|
||||
for entry in window.entries:
|
||||
entry_urls = {
|
||||
normalized
|
||||
for normalized in (_normalize_item_url(it.item_url) for it in entry.items)
|
||||
if normalized
|
||||
}
|
||||
# 多商品任务必须在同一笔订单中全部命中,避免部分匹配误判为已下单。
|
||||
if targets.issubset(entry_urls):
|
||||
matches.append(entry)
|
||||
if len(matches) == 1:
|
||||
return VerifyResult(
|
||||
VerifyVerdict.ALREADY_ORDERED,
|
||||
|
||||
Reference in New Issue
Block a user