feat(gateway): 账号只读查询通道——从已登录账号取真实订单
上游要的不只是网关记的任务状态镜像,还有「已登录账号在站点上的真实订单」,
但账号只在 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>
This commit is contained in:
@@ -241,3 +241,39 @@ class InvalidTaskStateError(AppError):
|
||||
retryable=False,
|
||||
status_code=409,
|
||||
)
|
||||
|
||||
|
||||
# ---- 账号只读查询通道(仅网关进程使用,见 docs/order-gateway.md §11)----
|
||||
# 与下单任务不同:查询是只读的,**可以安全重投**,因此这两类错误标 retryable=True,
|
||||
# 上游重发一张查询单不会有任何副作用。
|
||||
|
||||
|
||||
class QueryNotFoundError(AppError):
|
||||
"""查询单不存在(或已过保留期被清理)"""
|
||||
|
||||
def __init__(self, query_id: str):
|
||||
super().__init__(
|
||||
message=f"查询单不存在:{query_id}",
|
||||
code="QUERY_NOT_FOUND",
|
||||
err_code=6005,
|
||||
retryable=False,
|
||||
status_code=404,
|
||||
)
|
||||
self.query_id = query_id
|
||||
|
||||
|
||||
class QueryLeaseInvalidError(AppError):
|
||||
"""查询单租约无效:不是持有者、已被重投给别人或已终结
|
||||
|
||||
最常见的触发场景是 worker 执行超时、查询单被 sweep 重投后,原 worker 才姗姗
|
||||
来迟地回结果——这时结果必须被拒绝,否则会覆盖掉新一轮的执行结果。
|
||||
"""
|
||||
|
||||
def __init__(self, message: str = "查询单租约无效"):
|
||||
super().__init__(
|
||||
message=message,
|
||||
code="QUERY_LEASE_INVALID",
|
||||
err_code=6006,
|
||||
retryable=True,
|
||||
status_code=409,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user