client_distribution.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. from typing import Optional, List, Dict
  2. from enum import Enum
  3. from pydantic import BaseModel, field_validator
  4. from datetime import datetime
  5. from app.core.version import validate_version_code
  6. # 平台枚举,与前端一致(安卓分平板/手机,苹果含 iPadOS,鸿蒙分电脑/平板/手机)
  7. class DistributionPlatform(str, Enum):
  8. ANDROID = "android"
  9. ANDROID_PHONE = "android_phone" # 安卓手机
  10. ANDROID_TABLET = "android_tablet" # 安卓平板
  11. IOS = "ios"
  12. IPADOS = "ipados" # iPadOS
  13. WINDOWS = "windows"
  14. MACOS = "macos"
  15. HARMONYOS_PC = "harmonyos_pc" # 鸿蒙电脑
  16. HARMONYOS_PAD = "harmonyos_pad" # 鸿蒙平板
  17. HARMONYOS_PHONE = "harmonyos_phone" # 鸿蒙手机
  18. # Distribution Schemas
  19. class ClientDistributionBase(BaseModel):
  20. name: str
  21. description: Optional[str] = None
  22. icon_url: Optional[str] = None
  23. class ClientDistributionCreate(ClientDistributionBase):
  24. pass
  25. class ClientDistributionUpdate(BaseModel):
  26. name: Optional[str] = None
  27. description: Optional[str] = None
  28. icon_url: Optional[str] = None
  29. class ClientDistributionResponse(ClientDistributionBase):
  30. id: int
  31. owner_id: int
  32. created_at: datetime
  33. updated_at: Optional[datetime] = None
  34. icon_object_key: Optional[str] = None # 原始 object_key,编辑时回传用
  35. share_id: str = "" # 由 id+created_at 生成的固定无规律分享标识,用于 /d/{share_id}
  36. class Config:
  37. from_attributes = True
  38. class ClientDistributionList(BaseModel):
  39. total: int
  40. items: List[ClientDistributionResponse]
  41. # Version Schemas
  42. class ClientVersionBase(BaseModel):
  43. version_code: str
  44. version_name: Optional[str] = None
  45. release_notes: Optional[str] = None
  46. platform: Optional[str] = None
  47. @field_validator("version_code")
  48. @classmethod
  49. def version_code_format(cls, v: str) -> str:
  50. if not v or not v.strip():
  51. raise ValueError("版本号不能为空")
  52. validate_version_code(v)
  53. return v.strip()
  54. @field_validator("platform")
  55. @classmethod
  56. def platform_enum(cls, v: Optional[str]) -> Optional[str]:
  57. if v is None or v == "":
  58. return None
  59. allowed = {p.value for p in DistributionPlatform}
  60. if v.strip().lower() not in allowed:
  61. raise ValueError(f"平台必须是以下之一: {', '.join(sorted(allowed))}")
  62. return v.strip().lower()
  63. class ClientVersionCreate(ClientVersionBase):
  64. object_key: str
  65. file_size: Optional[int] = None
  66. class ClientVersionUpdate(BaseModel):
  67. version_name: Optional[str] = None
  68. release_notes: Optional[str] = None
  69. platform: Optional[str] = None
  70. @field_validator("platform")
  71. @classmethod
  72. def platform_enum(cls, v: Optional[str]) -> Optional[str]:
  73. if v is None or v == "":
  74. return None
  75. allowed = {p.value for p in DistributionPlatform}
  76. if v.strip().lower() not in allowed:
  77. raise ValueError(f"平台必须是以下之一: {', '.join(sorted(allowed))}")
  78. return v.strip().lower()
  79. class ClientVersionResponse(ClientVersionBase):
  80. id: int
  81. distribution_id: int
  82. object_key: str
  83. file_size: Optional[int] = None
  84. created_at: datetime
  85. class Config:
  86. from_attributes = True
  87. class ClientVersionList(BaseModel):
  88. total: int
  89. items: List[ClientVersionResponse]
  90. # Open API - Public download page
  91. class LatestVersionInfo(BaseModel):
  92. id: int
  93. version_code: str
  94. version_name: Optional[str] = None
  95. release_notes: Optional[str] = None
  96. file_size: Optional[int] = None
  97. platform: Optional[str] = None
  98. created_at: datetime
  99. download_url: str # Pre-signed URL
  100. class DistributionPublicResponse(BaseModel):
  101. id: int
  102. name: str
  103. description: Optional[str] = None
  104. icon_url: Optional[str] = None
  105. latest_version: Optional[LatestVersionInfo] = None
  106. # 按平台分组的最新版本,用于分享页 Tab 切换;key 为 platform 枚举值
  107. latest_versions_by_platform: Optional[Dict[str, LatestVersionInfo]] = None