240 lines
6.2 KiB
Python
240 lines
6.2 KiB
Python
"""
|
|
Pydantic schemas for products API.
|
|
"""
|
|
from typing import Optional, List
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from ninja import Schema, FilterSchema
|
|
from ninja.orm import create_schema
|
|
|
|
|
|
class CategoryOut(Schema):
|
|
"""Category output schema."""
|
|
id: int
|
|
name: str
|
|
slug: str
|
|
description: Optional[str] = None
|
|
icon: Optional[str] = None
|
|
parent_id: Optional[int] = None
|
|
sort_order: int
|
|
created_at: datetime
|
|
|
|
|
|
class CategoryIn(Schema):
|
|
"""Category input schema."""
|
|
name: str
|
|
slug: str
|
|
description: Optional[str] = None
|
|
icon: Optional[str] = None
|
|
parent_id: Optional[int] = None
|
|
sort_order: int = 0
|
|
|
|
|
|
class WebsiteOut(Schema):
|
|
"""Website output schema."""
|
|
id: int
|
|
name: str
|
|
url: str
|
|
logo: Optional[str] = None
|
|
description: Optional[str] = None
|
|
category_id: int
|
|
rating: Decimal
|
|
is_verified: bool
|
|
sort_order: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class WebsiteIn(Schema):
|
|
"""Website input schema."""
|
|
name: str
|
|
url: str
|
|
logo: Optional[str] = None
|
|
description: Optional[str] = None
|
|
category_id: int
|
|
rating: Decimal = Decimal("0")
|
|
is_verified: bool = False
|
|
sort_order: int = 0
|
|
|
|
|
|
class ProductPriceOut(Schema):
|
|
"""Product price output schema."""
|
|
id: int
|
|
product_id: int
|
|
website_id: int
|
|
website_name: Optional[str] = None
|
|
website_logo: Optional[str] = None
|
|
price: Decimal
|
|
original_price: Optional[Decimal] = None
|
|
currency: str
|
|
url: str
|
|
in_stock: bool
|
|
last_checked: datetime
|
|
|
|
|
|
class ProductPriceHistoryOut(Schema):
|
|
"""Product price history output schema."""
|
|
id: int
|
|
product_id: int
|
|
website_id: int
|
|
price: Decimal
|
|
recorded_at: datetime
|
|
|
|
|
|
class PriceHistoryStatsOut(Schema):
|
|
"""Price history statistics output schema."""
|
|
product_id: int
|
|
historical_lowest: Optional[Decimal] = None
|
|
historical_lowest_date: Optional[datetime] = None
|
|
historical_highest: Optional[Decimal] = None
|
|
historical_highest_date: Optional[datetime] = None
|
|
average_price: Optional[Decimal] = None
|
|
current_lowest: Optional[Decimal] = None
|
|
is_historical_lowest: bool = False
|
|
discount_from_highest: Optional[Decimal] = None # 距最高价降幅百分比
|
|
distance_to_lowest: Optional[Decimal] = None # 距历史最低差额
|
|
price_trend: str = "stable" # up, down, stable
|
|
history: List["ProductPriceHistoryOut"] = []
|
|
|
|
|
|
class EnhancedProductPriceOut(ProductPriceOut):
|
|
"""Enhanced product price with history stats."""
|
|
historical_lowest: Optional[Decimal] = None
|
|
is_at_historical_lowest: bool = False
|
|
discount_percent: Optional[Decimal] = None # 降价百分比
|
|
discount_amount: Optional[Decimal] = None # 降价金额
|
|
|
|
|
|
class ProductOut(Schema):
|
|
"""Product output schema."""
|
|
id: int
|
|
name: str
|
|
description: Optional[str] = None
|
|
image: Optional[str] = None
|
|
images: List[str] = []
|
|
category_id: int
|
|
status: str = "approved"
|
|
submitted_by_id: Optional[int] = None
|
|
reject_reason: Optional[str] = None
|
|
reviewed_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ProductWithPricesOut(ProductOut):
|
|
"""Product with prices output schema."""
|
|
prices: List[ProductPriceOut] = []
|
|
lowest_price: Optional[Decimal] = None
|
|
highest_price: Optional[Decimal] = None
|
|
|
|
|
|
class ComparisonTagOut(Schema):
|
|
"""Comparison tag output schema."""
|
|
id: int
|
|
name: str
|
|
slug: str
|
|
description: Optional[str] = None
|
|
cover_image: Optional[str] = None
|
|
icon: Optional[str] = None
|
|
sort_order: int
|
|
is_active: bool
|
|
product_count: int = 0
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ComparisonTagItemOut(Schema):
|
|
"""Comparison tag item output schema."""
|
|
product: ProductOut
|
|
prices: List["EnhancedProductPriceOut"] = []
|
|
lowest_price: Optional[Decimal] = None
|
|
highest_price: Optional[Decimal] = None
|
|
platform_count: int = 0
|
|
# 增强字段
|
|
historical_lowest: Optional[Decimal] = None
|
|
historical_lowest_date: Optional[datetime] = None
|
|
is_at_historical_lowest: bool = False
|
|
discount_from_highest_percent: Optional[Decimal] = None
|
|
is_recommended: bool = False # 推荐标签
|
|
recommendation_reason: Optional[str] = None # 推荐理由
|
|
|
|
|
|
class ComparisonTagDetailOut(Schema):
|
|
"""Comparison tag detail output schema."""
|
|
tag: ComparisonTagOut
|
|
items: List[ComparisonTagItemOut] = []
|
|
|
|
|
|
class UploadImageOut(Schema):
|
|
"""Image upload output."""
|
|
url: str
|
|
|
|
|
|
class ProductIn(Schema):
|
|
"""Product input schema."""
|
|
name: str
|
|
description: Optional[str] = None
|
|
image: Optional[str] = None
|
|
images: Optional[List[str]] = None
|
|
category_id: int
|
|
|
|
|
|
class MyProductOut(Schema):
|
|
"""User's product output schema."""
|
|
id: int
|
|
name: str
|
|
description: Optional[str] = None
|
|
image: Optional[str] = None
|
|
images: List[str] = []
|
|
category_id: int
|
|
status: str
|
|
reject_reason: Optional[str] = None
|
|
reviewed_at: Optional[datetime] = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ProductPriceIn(Schema):
|
|
"""Product price input schema."""
|
|
product_id: int
|
|
website_id: int
|
|
price: Decimal
|
|
original_price: Optional[Decimal] = None
|
|
currency: str = "CNY"
|
|
url: str
|
|
in_stock: bool = True
|
|
|
|
|
|
class ImportResultOut(Schema):
|
|
"""CSV import result."""
|
|
created_categories: int = 0
|
|
created_websites: int = 0
|
|
created_products: int = 0
|
|
created_prices: int = 0
|
|
updated_prices: int = 0
|
|
errors: List[str] = []
|
|
|
|
|
|
class ProductFilter(FilterSchema):
|
|
"""Product filter schema."""
|
|
category_id: Optional[int] = None
|
|
search: Optional[str] = None
|
|
min_price: Optional[Decimal] = None
|
|
max_price: Optional[Decimal] = None
|
|
sort_by: Optional[str] = None
|
|
|
|
|
|
class ProductSearchFilter(FilterSchema):
|
|
"""Product search filter schema."""
|
|
category_id: Optional[int] = None
|
|
user_id: Optional[str] = None # 按用户ID筛选该用户提交的商品
|
|
min_price: Optional[Decimal] = None
|
|
max_price: Optional[Decimal] = None
|
|
sort_by: Optional[str] = None
|
|
|
|
|
|
class WebsiteFilter(FilterSchema):
|
|
"""Website filter schema."""
|
|
category_id: Optional[int] = None
|
|
is_verified: Optional[bool] = None
|