"""任务与订单状态枚举:网关与本地 worker 共享的词汇表 放在 shared 层是因为这是网关与 worker 之间的 HTTP 契约——两侧都需要知道合法 取值,不能由任何一方私有持有。shared 本身不引入对 scraping/trading 的依赖, 这个模块也一样:纯枚举与状态集合,不引用任何业务模块。 两层状态不要混:task.status 描述「这个任务被谁领了、做完没有」,order state 描述「这笔订单在站点上走到哪一步」。前者权威方是网关,后者权威方是本地 trading, 网关只存镜像。详见 docs/order-gateway.md §3。 迁移图(task.status): queued ──lease──> leased ──首次 report──> running ──terminal report──> succeeded ▲ │ │ └─> failed │ └──── 租约过期 ──────────┴──> stale └─> needs_human └── 只有人工介入才能从 stale 回到 leased(reclaim) 注意 stale **不**自动回 queued——下单不可逆,自动重投等于再买一次。 """ from __future__ import annotations from enum import StrEnum class TaskStatus(StrEnum): """任务状态(网关权威)""" QUEUED = "queued" # 已入队,等待 worker 领取 LEASED = "leased" # 已被 worker 领走,尚未首次 report RUNNING = "running" # worker 已首次 report,正在进行 SUCCEEDED = "succeeded" # 终态:成功 FAILED = "failed" # 终态:worker 明确失败 NEEDS_HUMAN = "needs_human" # 终态:需要人介入(如 3DS、核对不出结论) STALE = "stale" # 租约过期,等人工 reclaim,不自动重投 # 终态集合:到达后任何 renew / report / reclaim 都报 6003 TERMINAL_STATUSES: frozenset[TaskStatus] = frozenset( {TaskStatus.SUCCEEDED, TaskStatus.FAILED, TaskStatus.NEEDS_HUMAN} ) # 处于「执行中」的状态:这些状态存在时,lease 一律返回空(全局并发度 1) ACTIVE_STATUSES: frozenset[TaskStatus] = frozenset({TaskStatus.LEASED, TaskStatus.RUNNING}) # 可以被普通 lease 领走的状态:只有 queued LEASABLE_STATUSES: frozenset[TaskStatus] = frozenset({TaskStatus.QUEUED}) # 可以被 reclaim 领走的状态:只有 stale RECLAIMABLE_STATUSES: frozenset[TaskStatus] = frozenset({TaskStatus.STALE}) class QueryStatus(StrEnum): """账号只读查询单状态(网关权威,见 docs/order-gateway.md §11) 与 TaskStatus 刻意分成两套词汇表,因为安全约束正好相反: - 下单是不可逆写操作 → 租约过期只能置 stale 等人工 reclaim,绝不自动重投 - 查询是只读操作 → 租约过期直接回 queued 自动重投,重复执行没有副作用 没有 running:查询是「领走 → 一次性回结果」,中间没有需要单独表达的进行态, 也不需要续租(超时就重投)。 """ QUEUED = "queued" # 已入队,等待 worker 领取 LEASED = "leased" # 已被 worker 领走,等待回结果 SUCCEEDED = "succeeded" # 终态:拿到结果 FAILED = "failed" # 终态:worker 明确失败,或重投次数用尽 EXPIRED = "expired" # 终态:超过查询单 TTL 仍未完成(多半是 worker 不在线) # 查询单终态集合:到达后 result 一律报 6006,sweep 也不再动它 QUERY_TERMINAL_STATUSES: frozenset[QueryStatus] = frozenset( {QueryStatus.SUCCEEDED, QueryStatus.FAILED, QueryStatus.EXPIRED} ) class AccountQueryKind(StrEnum): """账号只读查询的种类 放在 shared 是因为它是网关与 worker 之间的 HTTP 契约的一部分:网关不解释 `params` 的内容(与 intent 同样原样透传),但**校验 kind 合法**——不然上游 拼错一个字符,要等 worker 领走、执行、回报失败才知道,比当场 422 差得多。 """ ORDER_LIST = "order_list" # 订单列表(可带时间窗与翻页上限) ORDER_DETAIL = "order_detail" # 单笔订单详情(按注文番号) class OrderState(StrEnum): """订单状态(本地权威,网关只存镜像) 沿用 docs/order-gateway.md §3 给的取值。网关本身不解释这些值,只是字符串透传。 """ CREATED = "created" IN_CART = "in_cart" ORDERED = "ordered" AWAITING_PAYMENT = "awaiting_payment" PAID = "paid" SHIPPED = "shipped" DELIVERED = "delivered" CANCELLED = "cancelled"