from __future__ import annotations from typing import Any from pydantic import BaseModel, Field, field_validator from app.enums.private_portrait import PrivatePortraitAssetType from app.schemas.common import NaiveDatetimeOptional class PrivatePortraitConfigOut(BaseModel): enabled: bool image_limit: int used_image_count: int remaining_image_count: int class PrivatePortraitAdminConfigUpdate(BaseModel): private_portrait_image_limit: int = Field(..., ge=0, le=9999, description="0 表示关闭真人素材模块;>0 表示启用并限制图片总量") class PrivatePortraitProjectCreate(BaseModel): name: str = Field(..., min_length=1, max_length=128) description: str | None = Field(None, max_length=2000) class PrivatePortraitProjectUpdate(BaseModel): name: str | None = Field(None, min_length=1, max_length=128) description: str | None = Field(None, max_length=2000) status: str | None = None class PrivatePortraitProjectOut(BaseModel): id: str user_id: str | None = None name: str name_slug: str | None = None remote_project_name: str | None = None description: str | None = None status: str asset_group_count: int = 0 asset_count: int = 0 active_asset_count: int = 0 last_used_at: NaiveDatetimeOptional = None created_at: NaiveDatetimeOptional = None updated_at: NaiveDatetimeOptional = None model_config = {"from_attributes": True} class PrivatePortraitProjectListOut(BaseModel): items: list[PrivatePortraitProjectOut] total: int page: int page_size: int class PrivatePortraitValidateSessionCreate(BaseModel): callback_redirect_url: str | None = Field(None, description="认证完成后前端要跳转的页面。为空时使用后端默认回调页。") class PrivatePortraitValidateSessionOut(BaseModel): id: str user_id: str | None = None project_id: str byted_token: str | None = None h5_link: str | None = None callback_url: str | None = None result_code: str | None = None algorithm_base_resp_code: str | None = None verify_type: str | None = None status: str remote_group_id: str | None = None remote_project_name: str | None = None expired_at: NaiveDatetimeOptional = None error_message: str | None = None created_at: NaiveDatetimeOptional = None updated_at: NaiveDatetimeOptional = None model_config = {"from_attributes": True} class PrivatePortraitAssetGroupOut(BaseModel): id: str user_id: str | None = None project_id: str remote_group_id: str remote_group_name: str | None = None remote_project_name: str group_type: str status: str remote_delete_status: str remote_deleted_at: NaiveDatetimeOptional = None remote_delete_error: str | None = None created_at: NaiveDatetimeOptional = None updated_at: NaiveDatetimeOptional = None model_config = {"from_attributes": True} class PrivatePortraitAssetCreate(BaseModel): url: str = Field(..., min_length=1, description="已上传到本系统且可公网访问的素材 URL") asset_type: str = Field(default=PrivatePortraitAssetType.IMAGE.value) name: str | None = Field(None, max_length=256) @field_validator("asset_type") @classmethod def validate_asset_type(cls, v: str) -> str: value = v or PrivatePortraitAssetType.IMAGE.value if value not in {item.value for item in PrivatePortraitAssetType}: raise ValueError("asset_type 仅支持 Image/Video/Audio") return value class PrivatePortraitAssetOut(BaseModel): id: str user_id: str | None = None project_id: str project_name: str | None = None group_id: str remote_group_id: str remote_asset_id: str | None = None remote_project_name: str | None = None asset_type: str name: str | None = None source_url: str preview_url: str | None = None remote_url: str | None = None remote_url_expired_at: NaiveDatetimeOptional = None status: str moderation: dict[str, Any] | None = None last_poll_at: NaiveDatetimeOptional = None next_poll_at: NaiveDatetimeOptional = None poll_count: int = 0 remote_delete_status: str remote_deleted_at: NaiveDatetimeOptional = None remote_delete_error: str | None = None error_message: str | None = None created_at: NaiveDatetimeOptional = None updated_at: NaiveDatetimeOptional = None model_config = {"from_attributes": True} class PrivatePortraitAssetListOut(BaseModel): items: list[PrivatePortraitAssetOut] total: int page: int page_size: int class PrivatePortraitSelectableAssetOut(BaseModel): id: str project_id: str project_name: str name: str | None = None asset_type: str preview_url: str | None = None status: str created_at: NaiveDatetimeOptional = None class PrivatePortraitSelectableAssetListOut(BaseModel): items: list[PrivatePortraitSelectableAssetOut] total: int page: int page_size: int class PrivatePortraitDeleteOut(BaseModel): success: bool = True remote_delete_status: str | None = None