from pydantic import BaseModel, Field from datetime import datetime from typing import Optional, List # 商品基础信息 class ProductBase(BaseModel): category_id: int = Field(default=0, description="分类 ID") product_number: str = Field(default="", description="产品货号,11 位 UUID", max_length=11) model: str = Field(default="", description="产品型号,业务输入", max_length=50) name: str = Field(default="", description="产品名称", max_length=200) brand: str = Field(default="", description="品牌名称", max_length=100) price: float = Field(default=0.0, description="价格") original_price: float = Field(default=0.0, description="原价") currency: str = Field(default="USD", description="货币符号", max_length=10) stock: int = Field(default=0, description="库存") min_order: int = Field(default=1, description="最小起订量") unit: str = Field(default="pcs", description="计量单位", max_length=20) status: int = Field(default=1, description="状态:0-下架,1-上架") is_new: int = Field(default=0, description="是否新品:0-否,1-是") is_hot: int = Field(default=0, description="是否热销:0-否,1-是") is_best_seller: int = Field(default=0, description="是否畅销:0-否,1-是") sort_order: int = Field(default=0, description="排序顺序") # 商品详情字段 description: str = Field(default="", description="功能描述") usage: str = Field(default="", description="适用范围") material: str = Field(default="", description="材质", max_length=200) parameters: str = Field(default="", description="其他参数说明") # SEO 字段 seo_title_tag: str = Field(default="", description="SEO 页面标题", max_length=200) seo_meta_keywords: str = Field(default="", description="SEO 元关键词", max_length=500) # 产地字段 country_of_origin: str = Field(default="", description="原产国", max_length=100) made_in_label: str = Field(default="", description="Made in 标签", max_length=100) company_code: str = Field(default="", description="公司编码", max_length=50) class ProductCreate(ProductBase): pass class ProductUpdate(BaseModel): category_id: Optional[int] = None product_number: Optional[str] = None model: Optional[str] = None name: Optional[str] = None brand: Optional[str] = None price: Optional[float] = None original_price: Optional[float] = None currency: Optional[str] = None stock: Optional[int] = None min_order: Optional[int] = None unit: Optional[str] = None status: Optional[int] = None is_new: Optional[int] = None is_hot: Optional[int] = None is_best_seller: Optional[int] = None sort_order: Optional[int] = None description: Optional[str] = None usage: Optional[str] = None material: Optional[str] = None parameters: Optional[str] = None seo_title_tag: Optional[str] = None seo_meta_keywords: Optional[str] = None country_of_origin: Optional[str] = None made_in_label: Optional[str] = None class ProductResponse(ProductBase): id: int created_at: datetime updated_at: Optional[datetime] = None class Config: from_attributes = True # 商品媒体 class ProductMediaBase(BaseModel): product_number: str = Field(default="", description="产品货号", max_length=11) media_type: str = Field(default="image", description="媒体类型:image-图片,video-视频", max_length=20) r2_url: str = Field(default="", description="媒体 URL(R2 原图 URL)", max_length=500) thumbnail_r2_url: str = Field(default="", description="缩略图 URL(R2 缩略图 URL)", max_length=500) media_id: str = Field(default="", description="素材ID", max_length=32) media_alt: str = Field(default="", description="ALT 标签/描述", max_length=200) is_main: int = Field(default=0, description="是否主图:0-否,1-是") sort_order: int = Field(default=0, description="排序顺序") company_code: str = Field(default="", description="公司编码", max_length=50) class ProductMediaCreate(ProductMediaBase): pass class ProductMediaUpdate(BaseModel): media_type: Optional[str] = None r2_url: Optional[str] = None thumbnail_r2_url: Optional[str] = None media_id: Optional[str] = None media_alt: Optional[str] = None is_main: Optional[int] = None sort_order: Optional[int] = None class ProductMediaResponse(ProductMediaBase): id: int created_at: datetime updated_at: datetime class Config: from_attributes = True # 商品认证 class ProductCertificationBase(BaseModel): product_number: str = Field(default="", description="产品货号", max_length=11) certification_type: str = Field(default="", description="认证类型", max_length=50) r2_url: str = Field(default="", description="认证文件 URL(R2 URL)", max_length=500) thumbnail_r2_url: str = Field(default="", description="认证缩略图路径(R2 URL)", max_length=500) media_id: str = Field(default="", description="素材ID", max_length=32) company_code: str = Field(default="", description="公司编码", max_length=50) class ProductCertificationCreate(ProductCertificationBase): pass class ProductCertificationUpdate(BaseModel): certification_type: Optional[str] = None r2_url: Optional[str] = None thumbnail_r2_url: Optional[str] = None media_id: Optional[str] = None class ProductCertificationResponse(ProductCertificationBase): id: int created_at: datetime updated_at: datetime class Config: from_attributes = True # 商品包装 class ProductPackagingBase(BaseModel): product_number: str = Field(default="", description="产品货号", max_length=11) model: str = Field(default="", description="型号", max_length=50) length: float = Field(default=0.0, description="长度 (cm)") width: float = Field(default=0.0, description="宽度 (cm)") height: float = Field(default=0.0, description="高度 (cm)") volume: float = Field(default=0.0, description="体积 (cm³)") weight: float = Field(default=0.0, description="重量 (g)") company_code: str = Field(default="", description="公司编码", max_length=50) class ProductPackagingCreate(ProductPackagingBase): pass class ProductPackagingUpdate(BaseModel): model: Optional[str] = None length: Optional[float] = None width: Optional[float] = None height: Optional[float] = None volume: Optional[float] = None weight: Optional[float] = None class ProductPackagingResponse(ProductPackagingBase): id: int created_at: datetime updated_at: datetime class Config: from_attributes = True # 商品详情(包含所有关联数据) class ProductDetailResponse(ProductResponse): media: List[ProductMediaResponse] = [] certifications: List[ProductCertificationResponse] = [] packaging: List[ProductPackagingResponse] = [] class Config: from_attributes = True # 公司 R2 配置 class CompanyR2ConfigBase(BaseModel): company_code: str = Field(..., description="公司编码") r2_account_id: str = Field(..., description="R2 账户 ID") r2_access_key_id: str = Field(..., description="R2 访问密钥 ID") r2_secret_access_key: str = Field(..., description="R2 密钥") r2_bucket_name: str = Field(..., description="R2 桶名称") r2_public_url: str = Field(..., description="R2 公共访问 URL") r2_enabled: int = Field(..., description="是否启用 R2:0-禁用,1-启用") class CompanyR2ConfigCreate(CompanyR2ConfigBase): pass class CompanyR2ConfigUpdate(BaseModel): r2_account_id: Optional[str] = None r2_access_key_id: Optional[str] = None r2_secret_access_key: Optional[str] = None r2_bucket_name: Optional[str] = None r2_public_url: Optional[str] = None r2_enabled: Optional[int] = None class CompanyR2ConfigResponse(CompanyR2ConfigBase): id: int created_at: datetime updated_at: Optional[datetime] = None company_name: Optional[str] = None class Config: from_attributes = True