from typing import Optional, List, Dict from enum import Enum from pydantic import BaseModel, field_validator from datetime import datetime from app.core.version import validate_version_code # 平台枚举,与前端一致(安卓分平板/手机,苹果含 iPadOS,鸿蒙分电脑/平板/手机) class DistributionPlatform(str, Enum): ANDROID = "android" ANDROID_PHONE = "android_phone" # 安卓手机 ANDROID_TABLET = "android_tablet" # 安卓平板 IOS = "ios" IPADOS = "ipados" # iPadOS WINDOWS = "windows" MACOS = "macos" HARMONYOS_PC = "harmonyos_pc" # 鸿蒙电脑 HARMONYOS_PAD = "harmonyos_pad" # 鸿蒙平板 HARMONYOS_PHONE = "harmonyos_phone" # 鸿蒙手机 # Distribution Schemas class ClientDistributionBase(BaseModel): name: str description: Optional[str] = None icon_url: Optional[str] = None class ClientDistributionCreate(ClientDistributionBase): pass class ClientDistributionUpdate(BaseModel): name: Optional[str] = None description: Optional[str] = None icon_url: Optional[str] = None class ClientDistributionResponse(ClientDistributionBase): id: int owner_id: int created_at: datetime updated_at: Optional[datetime] = None icon_object_key: Optional[str] = None # 原始 object_key,编辑时回传用 share_id: str = "" # 由 id+created_at 生成的固定无规律分享标识,用于 /d/{share_id} class Config: from_attributes = True class ClientDistributionList(BaseModel): total: int items: List[ClientDistributionResponse] # Version Schemas class ClientVersionBase(BaseModel): version_code: str version_name: Optional[str] = None release_notes: Optional[str] = None platform: Optional[str] = None @field_validator("version_code") @classmethod def version_code_format(cls, v: str) -> str: if not v or not v.strip(): raise ValueError("版本号不能为空") validate_version_code(v) return v.strip() @field_validator("platform") @classmethod def platform_enum(cls, v: Optional[str]) -> Optional[str]: if v is None or v == "": return None allowed = {p.value for p in DistributionPlatform} if v.strip().lower() not in allowed: raise ValueError(f"平台必须是以下之一: {', '.join(sorted(allowed))}") return v.strip().lower() class ClientVersionCreate(ClientVersionBase): object_key: str file_size: Optional[int] = None class ClientVersionUpdate(BaseModel): version_name: Optional[str] = None release_notes: Optional[str] = None platform: Optional[str] = None @field_validator("platform") @classmethod def platform_enum(cls, v: Optional[str]) -> Optional[str]: if v is None or v == "": return None allowed = {p.value for p in DistributionPlatform} if v.strip().lower() not in allowed: raise ValueError(f"平台必须是以下之一: {', '.join(sorted(allowed))}") return v.strip().lower() class ClientVersionResponse(ClientVersionBase): id: int distribution_id: int object_key: str file_size: Optional[int] = None created_at: datetime class Config: from_attributes = True class ClientVersionList(BaseModel): total: int items: List[ClientVersionResponse] # Open API - Public download page class LatestVersionInfo(BaseModel): id: int version_code: str version_name: Optional[str] = None release_notes: Optional[str] = None file_size: Optional[int] = None platform: Optional[str] = None created_at: datetime download_url: str # Pre-signed URL class DistributionPublicResponse(BaseModel): id: int name: str description: Optional[str] = None icon_url: Optional[str] = None latest_version: Optional[LatestVersionInfo] = None # 按平台分组的最新版本,用于分享页 Tab 切换;key 为 platform 枚举值 latest_versions_by_platform: Optional[Dict[str, LatestVersionInfo]] = None