"""管理员消息 DTO。 定义管理员消息发送的命令与结果值对象,包括管理员发送命令与发送结果。 所有 DTO 均为 ``dataclass(frozen=True)``,仅依赖标准库与契约层内部类型, 用于管理员向目标会话或用户批量发送消息并聚合失败与跳过详情。集合字段 使用 tuple 以保证 frozen dataclass 的不可变语义。 """ from __future__ import annotations from dataclasses import dataclass from typing import Literal from yuxi.channels.contract.dtos.common import ( FailureDetail, MessageContent, Operator, SkipDetail, ) from yuxi.channels.contract.errors import ValidationError @dataclass(frozen=True) class AdminSendCmd: """管理员发送命令(FR-19)。 由管理员消息端口方法引用,描述管理员向目标会话或用户发送消息的请求, 携带目标、内容、会话策略、幂等键与操作人,支持复用 / 新建 / 临时 三种会话策略。 字段: target: 目标会话或用户。 content: 消息内容。 conversation_policy: 会话策略(reuse | reuse-or-create | new)。 idempotency_key: 幂等键。 operator: 操作人(审计用)。 target_type: 目标类型(session_id 或 peer_id,默认 session_id)。 """ target: str content: MessageContent conversation_policy: Literal["reuse", "reuse-or-create", "new"] idempotency_key: str operator: Operator target_type: Literal["session_id", "peer_id"] = "session_id" def __post_init__(self) -> None: """校验必填字段非空(FR-19)。 ``target`` 与 ``idempotency_key`` 必须非空,在构造时即抛出 ``ValidationError``,adapter 不再做该校验(INV-8)。 ``conversation_policy`` 的合法值校验由 ``AdminMessageService`` 统一执行(``_CONVERSATION_POLICIES``),DTO 层不重复校验,避免 与 service 层默认值回退(``or _DEFAULT_CONVERSATION_POLICY``) 冲突。 ``target_type`` 默认 ``"session_id"``(按会话 ID 触达), ``"peer_id"`` 表示按对端 ID 触达(需配合 ``channel_type`` / ``account_id`` 路由信息,由 ``_resolveTarget`` 解析)。 """ if not self.target: raise ValidationError("target", "must not be empty") if not self.idempotency_key: raise ValidationError("idempotency_key", "must not be empty") @dataclass(frozen=True) class AdminSendResult: """管理员发送结果(FR-19)。 描述管理员批量发送消息的执行结果,聚合成功消息 ID、失败详情与跳过 详情,用于结果汇报与重试策略决策。集合字段使用 tuple 以保证不可变。 字段: message_ids: 成功发送的消息 ID 列表。 failures: 失败详情列表(默认空 tuple)。 skipped: 跳过详情列表(默认空 tuple)。 """ message_ids: tuple[str, ...] failures: tuple[FailureDetail, ...] = () skipped: tuple[SkipDetail, ...] = ()