2026-07-02 03:22:12 +08:00
|
|
|
|
"""分阶段 ACK DTO。
|
|
|
|
|
|
|
|
|
|
|
|
定义分阶段 ACK(FR-24)的枚举与不可变值对象,包括 ACK 策略、ACK 阶段
|
|
|
|
|
|
与 ACK 决策。所有枚举继承 ``str, Enum`` 以支持 JSON 序列化,DTO 均为
|
|
|
|
|
|
``dataclass(frozen=True)``,仅依赖标准库,用于入站消息的分阶段 ACK
|
|
|
|
|
|
决策与幂等控制。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
from enum import StrEnum
|
2026-07-03 19:18:13 +08:00
|
|
|
|
from typing import Literal
|
|
|
|
|
|
|
|
|
|
|
|
from yuxi.channels.contract.errors import ValidationError
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AckPolicy(StrEnum):
|
|
|
|
|
|
"""ACK 策略。
|
|
|
|
|
|
|
|
|
|
|
|
标识入站消息的 ACK 时机,用于控制 ACK 与消息处理流程的耦合关系。
|
|
|
|
|
|
继承 ``str, Enum`` 以支持 JSON 序列化与字符串比较。
|
|
|
|
|
|
|
|
|
|
|
|
取值:
|
|
|
|
|
|
AFTER_RECORD: 消息记录后 ACK。
|
|
|
|
|
|
AFTER_AGENT_DISPATCH: Agent 分发后 ACK。
|
|
|
|
|
|
AFTER_PERSIST: 持久化投递后 ACK。
|
|
|
|
|
|
MANUAL: 手动 ACK。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
AFTER_RECORD = "after_record"
|
|
|
|
|
|
AFTER_AGENT_DISPATCH = "after_agent_dispatch"
|
|
|
|
|
|
AFTER_PERSIST = "after_persist"
|
|
|
|
|
|
MANUAL = "manual"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AckStage(StrEnum):
|
|
|
|
|
|
"""ACK 阶段。
|
|
|
|
|
|
|
|
|
|
|
|
标识 ACK 决策所属的处理阶段,用于分阶段 ACK 的状态机推进。继承
|
|
|
|
|
|
``str, Enum`` 以支持 JSON 序列化与字符串比较。
|
|
|
|
|
|
|
|
|
|
|
|
取值:
|
|
|
|
|
|
RECORD: 记录阶段。
|
|
|
|
|
|
DISPATCH: 分发阶段。
|
|
|
|
|
|
PERSIST: 持久化阶段。
|
|
|
|
|
|
MANUAL: 手动阶段。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
RECORD = "record"
|
|
|
|
|
|
DISPATCH = "dispatch"
|
|
|
|
|
|
PERSIST = "persist"
|
|
|
|
|
|
MANUAL = "manual"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
|
class AckDecision:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
"""ACK 决策(FR-24)。
|
2026-07-02 03:22:12 +08:00
|
|
|
|
|
|
|
|
|
|
描述一次 ACK 决策的结果,包括决策动作、所属阶段与幂等键,用于
|
|
|
|
|
|
分阶段 ACK 的决策传递与幂等控制。
|
|
|
|
|
|
|
|
|
|
|
|
字段:
|
2026-07-03 19:18:13 +08:00
|
|
|
|
decision: 决策动作(ack | nack | retry | pending)。
|
2026-07-02 03:22:12 +08:00
|
|
|
|
stage: ACK 阶段。
|
|
|
|
|
|
idempotency_key: 幂等键(可选)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2026-07-03 19:18:13 +08:00
|
|
|
|
decision: Literal["ack", "nack", "retry", "pending"]
|
2026-07-02 03:22:12 +08:00
|
|
|
|
stage: AckStage
|
|
|
|
|
|
idempotency_key: str | None = None
|
2026-07-03 19:18:13 +08:00
|
|
|
|
|
|
|
|
|
|
def __post_init__(self) -> None:
|
|
|
|
|
|
"""校验 decision 取值合法。
|
|
|
|
|
|
|
|
|
|
|
|
``decision`` 必须为 ``ack`` / ``nack`` / ``retry`` / ``pending`` 之一,在构造时
|
|
|
|
|
|
即抛出 ``ValidationError``,避免非法决策动作导致 ACK 状态机推进
|
|
|
|
|
|
异常(INV-8)。
|
|
|
|
|
|
"""
|
|
|
|
|
|
if self.decision not in ("ack", "nack", "retry", "pending"):
|
|
|
|
|
|
raise ValidationError("decision", "must be one of ack, nack, retry, pending")
|