"""COS 直传预签名的请求/响应契约(FR-SYS-03 / T-P1-27)。 放在 packages/storage 而非 apps/api/schemas:object_key 布局与场景语义属于 存储域,报告、形象等业务域复用直传时不应该反向依赖 API 层。 """ from __future__ import annotations from pydantic import BaseModel, Field class PresignRequest(BaseModel): """``POST /uploads/presign`` 请求体:客户端上传前的申报信息。 size_bytes 只是客户端自报值,仅用于服务端提前拒绝明显超限的请求; 真正的强制约束靠预签名 URL 签入的 Content-Type 与 COS 桶侧限制兜底。 """ scene: str = Field(min_length=1, max_length=64, description="上传场景,如 report_photo/avatar") content_type: str = Field(min_length=1, max_length=128, description="MIME 类型,如 image/jpeg") size_bytes: int = Field(ge=1, description="文件字节数") class PresignResponse(BaseModel): """签发结果:客户端用 PUT 直传 upload_url,body 与 Content-Type 必须与申报一致。""" upload_url: str = Field(description="带签名的 COS PUT URL,过期前有效") object_key: str = Field(description="COS 对象键(可审计布局:scene/user/日期/uuid)") expires_in: int = Field(description="URL 有效期(秒),与签发时配置一致") max_bytes: int = Field(description="服务端允许的最大字节数,客户端用于前置校验") content_type: str = Field(description="回显申报的 Content-Type,PUT 时必须携带同值")