ForcePilot/backend/package/yuxi/channels/contract/dtos/control.py

90 lines
3.4 KiB
Python
Raw Normal View History

"""控制面 DTO。
定义控制面端口的命令与结果值对象包括控制命令与控制结果所有 DTO 均为
``dataclass(frozen=True)``仅依赖标准库与契约层内部类型用于向导诊断
白名单目录配对配置会话插件健康审计等控制面操作的命令传递
与结果返回集合字段使用 tuple 以保证 frozen dataclass 的不可变语义
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Literal
from yuxi.channels.contract.dtos.channel import ChannelType
from yuxi.channels.contract.dtos.common import FailureDetail, Operator
from yuxi.channels.contract.errors import ValidationError
@dataclass(frozen=True)
class ControlCmd:
"""控制命令CON-001
由控制面端口方法引用描述一次控制面操作请求携带操作类型目标渠道
参数与操作人``operation`` 为带子域前缀的自由字符串
``dashboard/overview`` / ``config/update`` / ``session/merge``
具体合法值由 ``operation_audit_meta`` 与各 handler ``operations()``
共同约束DTO 仅校验非空
字段
operation: 操作类型带子域前缀的自由字符串非空
operator: 操作人审计用
target_channel: 目标渠道类型可选
params: 操作参数可选
"""
operation: str
operator: Operator
target_channel: ChannelType | None = None
params: dict[str, Any] | None = None
def __post_init__(self) -> None:
"""校验必填字段非空。
``operation`` 必须非空在构造时即抛出 ``ValidationError``
adapter 不再做该校验INV-8
"""
if not self.operation:
raise ValidationError("operation", "must not be empty")
@dataclass(frozen=True)
class ControlResult:
"""控制结果CON-001
描述控制面操作的执行结果携带状态数据与错误详情用于结果汇报与
错误聚合集合字段使用 tuple 以保证不可变
字段
status: 执行状态success | failed | partial
data: 返回数据可选
errors: 错误详情列表默认空 tuple
"""
status: Literal["success", "failed", "partial"]
data: dict[str, Any] | None = None
errors: tuple[FailureDetail, ...] = ()
@dataclass(frozen=True)
class ControlPlaneResult:
"""控制面 handler 返回值H-7
控制面域 handler 的操作方法返回此类型化 DTO替代裸 ``dict[str, Any]``
``data`` 仍为 JSON 可序列化 dict ``dataclass_to_dict`` 等序列化器
生成保证外部 API 响应不变``status`` / ``meta`` 携带 HTTP 语义
与分页等元数据 ``ChannelControlService`` 与序列化层消费
当前版本 ``status`` / ``meta`` 为预留字段handler 暂只填充 ``data``
未来可用于分页 cursor/total 或创建类操作的 201 状态码
字段
data: handler 返回的业务数据 dict必填无数据场景 handler 返回 ``None``
status: HTTP 状态码语义默认 200创建类操作可设 201
meta: 元数据可选如分页 cursor / total
"""
data: dict[str, Any]
status: int = 200
meta: dict[str, Any] | None = None