上游要的不只是网关记的任务状态镜像,还有「已登录账号在站点上的真实订单」,
但账号只在 NAT 后本地机上,只能经网关队列走。新增独立查询通道(§11):
- gateway 单开 account_queries 表 + QueryStatus 状态机,接口
POST /api/account/queries(幂等)/ lease / {id}/result / {id}
- 不复用下单任务队列:查询是只读,租约过期可安全重投(与下单「绝不自动
重投」相反),且不该被全局并发度 1 堵死、task_reports 是订单镜像不能污染
- 本地交易服务起第二条常驻循环 query_runner,领到即调 SiteInteractor 真读:
order_list 复用已实测的 list_recent_orders(规范化字段 + 站点
orderListData 原文),order_detail 复用 fetch_order_detail(配送阶段 +
页面 __INITIAL_STATE__ 原样透传,结构未经真实样本,不抽字段)
- 账号级串行仍由 SiteInteractor 的锁保证;每次执行套超时按失败回报
- 错误码 6005/6006(查询通道,可重试只读区别于 6001-6004);/health 暴露
queued_query_count;结果体积上限先丢原始 JSON
openapi.json 重导,docs/order-gateway.md §11、README、.env.example 补全
配置与实测边界。全量测试 404→454 通过。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
47 lines
2.5 KiB
Python
47 lines
2.5 KiB
Python
"""交易服务容器:集中管理交易侧服务实例,用于依赖注入
|
|
|
|
持有三类东西:
|
|
- 登录态会话(auth_session)——加购与下单必须用同一份 cookie,全程共享
|
|
- 站点交互器(site)——持有 Playwright BrowserContext,加购/校验/清空/删除共用。
|
|
不依赖 gateway URL:HTTP 接口与 worker 都要用它,故总是构造。
|
|
- 本地下单 worker(可选)——RAKUTEN_ORDER_GATEWAY_URL 留空时整套不构造
|
|
|
|
worker 的子组件(gateway_client / local_db / evidence_store / runner)都在
|
|
app.trading.worker 包内,与抓取/网关互不 import;worker 需要商品数据时走抓取
|
|
服务的 HTTP 接口,需要任务调度时走网关的 HTTP 接口。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from app.shared.config import Settings
|
|
from app.trading.services.auth_session import AuthSession
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class TradingContainer:
|
|
"""交易服务容器
|
|
|
|
与抓取容器最本质的差别不是字段多少,而是**这个进程有状态**:登录态、
|
|
订单、页面证据都属于某个具体账号,因此交易服务只能单实例运行
|
|
(或按账号分片),不能像抓取服务那样随意横向扩容。
|
|
|
|
site 字段始终构造(HTTP /api/cart/* 接口与 worker 都依赖它)。
|
|
worker_* 字段在 RAKUTEN_ORDER_GATEWAY_URL 留空时为 None——此时交易服务
|
|
只跑登录态与购物车接口,不起后台 worker。
|
|
"""
|
|
|
|
settings: Settings
|
|
auth_session: AuthSession
|
|
# 站点交互器:持有 Playwright + 账号 cookie,加购/校验/清空/删除共用一份。
|
|
# 不依赖 gateway URL——HTTP 接口与 worker 都要用它,故总是构造。
|
|
site: object | None = None # app.trading.worker.site_interact.SiteInteractor
|
|
# 以下四项仅在 order_gateway_url 配置时构造;否则为 None,worker 不启动
|
|
worker_client: object | None = None # app.trading.worker.client.GatewayClient
|
|
worker_local_db: object | None = None # app.trading.worker.local_db.LocalDB
|
|
worker_evidence: object | None = None # app.trading.worker.evidence.EvidenceStore
|
|
worker_runner: object | None = None # app.trading.worker.runner.WorkerRunner
|
|
# 账号只读查询的第二条循环(docs/order-gateway.md §11)。与下单 worker 同生共死,
|
|
# 但跑在各自的 asyncio 任务里:下单一跑几分钟,查询不该排在它后面。
|
|
query_runner: object | None = None # app.trading.worker.query_runner.QueryRunner
|