Files
ai_web/backend/apps/products/schemas.py
2026-02-04 15:25:04 +08:00

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