本次提交包含多项代码优化与规范修正: 1. 文档与注释优化:修正注释术语、补充注解与FR编号 2. 代码格式调整:统一空格、换行与缩进规范 3. 类型与接口完善:补充__all__导出、修正返回类型注解 4. 错误处理增强:新增领域错误类与校验逻辑 5. 依赖与导入调整:修复路径引用、统一时区导入 6. 协议与契约更新:完善接口文档与一致性注解
135 lines
4.2 KiB
Python
135 lines
4.2 KiB
Python
"""媒体资源 DTO。
|
||
|
||
定义媒体上传/下载/元数据查询的数据传输对象,覆盖图片/文件/音频/视频
|
||
媒体资源的通用契约。所有渠道插件共享统一媒体 DTO,渠道插件实现
|
||
``InboundAdapter.downloadAttachment`` 或 ``OutboundAdapter.sendMessage``
|
||
时可使用这些 DTO 作为内部类型。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
from datetime import datetime
|
||
from enum import StrEnum
|
||
|
||
from yuxi.channels.contract.errors import ValidationError
|
||
|
||
|
||
class MediaType(StrEnum):
|
||
"""媒体类型枚举。"""
|
||
|
||
IMAGE = "image"
|
||
FILE = "file"
|
||
AUDIO = "audio"
|
||
VIDEO = "video"
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MediaUploadResult:
|
||
"""媒体上传结果。
|
||
|
||
字段:
|
||
media_id: 渠道侧媒体 ID(如飞书 image_key / file_key)。
|
||
url: 可访问的媒体 URL(部分渠道返回,可为 None)。
|
||
expires_at: 媒体资源过期时间(UTC),不过期时为 None。
|
||
"""
|
||
|
||
media_id: str
|
||
url: str | None = None
|
||
expires_at: datetime | None = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验 media_id 非空。
|
||
|
||
``media_id`` 必须非空,在构造时即抛出 ``ValidationError``,避免
|
||
空媒体 ID 导致后续上传结果无法定位(INV-8)。
|
||
"""
|
||
if not self.media_id:
|
||
raise ValidationError("media_id", "must not be empty")
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MediaDownloadResult:
|
||
"""媒体下载结果。
|
||
|
||
字段:
|
||
content: 媒体二进制内容。
|
||
filename: 文件名(含扩展名),渠道未返回时为 None。
|
||
mime_type: MIME 类型(如 image/png),渠道未返回时为 None。
|
||
size: 内容字节数。
|
||
"""
|
||
|
||
content: bytes
|
||
size: int
|
||
filename: str | None = None
|
||
mime_type: str | None = None
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class MediaMetadata:
|
||
"""媒体元数据。
|
||
|
||
字段:
|
||
media_id: 渠道侧媒体 ID。
|
||
media_type: 媒体类型。
|
||
size: 内容字节数。
|
||
mime_type: MIME 类型,渠道未返回时为 None。
|
||
created_at: 媒体创建时间(UTC),渠道未返回时为 None。
|
||
"""
|
||
|
||
media_id: str
|
||
media_type: MediaType
|
||
size: int
|
||
mime_type: str | None = None
|
||
created_at: datetime | None = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验 media_id 非空与 size 非负。
|
||
|
||
``media_id`` 必须非空,``size`` 必须非负,在构造时即抛出
|
||
``ValidationError``,避免空媒体 ID 或负字节数导致元数据失效(INV-8)。
|
||
"""
|
||
if not self.media_id:
|
||
raise ValidationError("media_id", "must not be empty")
|
||
if self.size < 0:
|
||
raise ValidationError("size", "must not be negative")
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class AttachmentUploadResult:
|
||
"""附件上传结果(MSG-ATTACH-UPLOAD)。
|
||
|
||
描述通过插件 ``AttachmentUploadAdapter`` 适配器上传附件后的返回结果,包括
|
||
渠道侧附件 ID、可访问 URL、MIME 类型与字节数,由
|
||
``MessageManagementPort.uploadAttachment`` 引用(FR-12)。
|
||
|
||
字段:
|
||
attachment_id: 渠道侧附件 ID。
|
||
url: 可访问的附件 URL。
|
||
mime_type: MIME 类型(如 image/png)。
|
||
size_bytes: 内容字节数。
|
||
expires_at: 附件资源过期时间(UTC),不过期时为 None。
|
||
"""
|
||
|
||
attachment_id: str
|
||
url: str
|
||
mime_type: str
|
||
size_bytes: int
|
||
expires_at: datetime | None = None
|
||
|
||
def __post_init__(self) -> None:
|
||
"""校验 attachment_id / url / mime_type 非空与 size_bytes 为正整数。
|
||
|
||
必填字符串字段必须非空,``size_bytes`` 必须为正整数,在构造时即
|
||
抛出 ``ValidationError``,避免空附件 ID / URL 或非正字节数导致上传
|
||
结果失效(INV-8)。
|
||
"""
|
||
if not self.attachment_id:
|
||
raise ValidationError("attachment_id", "must not be empty")
|
||
if not self.url:
|
||
raise ValidationError("url", "must not be empty")
|
||
if not self.mime_type:
|
||
raise ValidationError("mime_type", "must not be empty")
|
||
if self.size_bytes <= 0:
|
||
raise ValidationError("size_bytes", "must be a positive integer")
|