from sqlalchemy import Column, String, Integer, Float, DateTime, JSON, ForeignKey from sqlalchemy.dialects.mysql import DECIMAL from sqlalchemy.sql import func, select from sqlalchemy.orm import relationship from pydantic import BaseModel, Field from typing import Optional, List from datetime import datetime from .database import Base # 商家图片表 class MerchantImageDB(Base): __tablename__ = "merchant_images" id = Column(Integer, primary_key=True, autoincrement=True) merchant_id = Column(Integer, ForeignKey("merchants.id", ondelete="CASCADE"), index=True) image_url = Column(String(500), nullable=False) sort = Column(Integer, nullable=False, default=0) create_time = Column(DateTime(timezone=True), server_default=func.now()) class Config: unique_together = [("merchant_id", "sort")] # 数据库模型 class MerchantDB(Base): __tablename__ = "merchants" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(100), nullable=False) business_hours = Column(String(100), nullable=False) # 营业时间,如 "09:00-22:00" address = Column(String(200), nullable=False) longitude = Column(DECIMAL(9, 6), nullable=False) # 经度,精确到小数点后6位 latitude = Column(DECIMAL(9, 6), nullable=False) # 纬度,精确到小数点后6位 phone = Column(String(20), nullable=False) create_time = Column(DateTime(timezone=True), server_default=func.now()) update_time = Column(DateTime(timezone=True), onupdate=func.now()) category_id = Column(Integer, ForeignKey("merchant_categories.id"), nullable=True) # 关联图片 images = relationship("MerchantImageDB", order_by="MerchantImageDB.sort", cascade="all, delete-orphan") # Pydantic 模型 class MerchantImage(BaseModel): image_url: str = Field(..., max_length=500) sort: int = Field(..., ge=0) # 排序序号,从0开始 class Config: from_attributes = True class MerchantCreate(BaseModel): name: str = Field(..., max_length=100) business_hours: str = Field(..., max_length=100) address: str = Field(..., max_length=200) longitude: float = Field(..., ge=-180, le=180, description="经度") latitude: float = Field(..., ge=-90, le=90, description="纬度") phone: str = Field(..., max_length=20, pattern=r'^\d+$') images: List[MerchantImage] = [] category_id: Optional[int] = None class MerchantUpdate(BaseModel): name: Optional[str] = Field(None, max_length=100) business_hours: Optional[str] = Field(None, max_length=100) address: Optional[str] = Field(None, max_length=200) longitude: Optional[float] = Field(None, ge=-180, le=180, description="经度") latitude: Optional[float] = Field(None, ge=-90, le=90, description="纬度") phone: Optional[str] = Field(None, max_length=20, pattern=r'^\d+$') images: Optional[List[MerchantImage]] = None category_id: Optional[int] = None class MerchantInfo(BaseModel): id: int name: str business_hours: str address: str longitude: float latitude: float phone: str images: List[MerchantImage] create_time: datetime update_time: Optional[datetime] distance: Optional[int] = None # 距离(米) category_id: Optional[int] = None category_name: Optional[str] = None # 用于关联查询显示分类名称 class Config: from_attributes = True