from typing import Optional, List import enum from sqlalchemy import Column, Integer, String, DECIMAL, DateTime, Enum, ForeignKey from sqlalchemy.sql import func from sqlalchemy.dialects.mysql import JSON from pydantic import BaseModel, Field, model_validator from .database import Base from sqlalchemy.orm import relationship, backref from app.core.imageprocessor import process_image, ImageFormat from app.core import utils class CommunityStatus(str, enum.Enum): UNOPEN = "UNOPEN" # 未运营 OPENING = "OPENING" # 已运营 # 数据库模型 class CommunityDB(Base): __tablename__ = "communities" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(100), nullable=False) address = Column(String(200), nullable=False) longitude = Column(DECIMAL(9,6), nullable=False) # 经度,精确到小数点后6位 latitude = Column(DECIMAL(9,6), nullable=False) # 纬度,精确到小数点后6位 status = Column(Enum(CommunityStatus), nullable=False, default=CommunityStatus.UNOPEN) qy_group_qrcode = Column(String(200), nullable=True) # 企业微信群二维码地址 webot_webhook = Column(String(200), nullable=True) # 企业微信机器人webhook # 小区定价 base_price = Column(DECIMAL(10,2), nullable=False, default=3.0) # 基础费用 extra_package_price = Column(DECIMAL(10,2), nullable=False, default=1.0) # 额外包裹费用 extra_package_threshold = Column(Integer, nullable=False, default=2) # 额外收费阈值 more_station_price = Column(DECIMAL(10,2), nullable=False, default=2.0) # 多驿站加价 # 服务时间 weekdays = Column(JSON, nullable=True) # 服务星期几,如[1,2,3,4,5]表示周一到周五 create_time = Column(DateTime(timezone=True), server_default=func.now()) update_time = Column(DateTime(timezone=True), onupdate=func.now()) admin_id = Column(Integer, ForeignKey("users.userid"), nullable=True) # 关联管理员 - 明确指定外键 admin = relationship("UserDB", foreign_keys=[admin_id], backref=backref("managed_communities", lazy="dynamic")) # 通过映射表关联的社区分润 community_profit_sharing = relationship("CommunityProfitSharing", backref="community", uselist=False) # 关联社区集合 community_set_mappings = relationship("CommunitySetMapping", backref="community") @property def optimized_qy_group_qrcode(self): if self.qy_group_qrcode: return process_image(self.qy_group_qrcode).thumbnail(800, 800).format(ImageFormat.WEBP).build() return None # Pydantic 模型 class CommunityCreate(BaseModel): name: str = Field(..., max_length=100) address: str = Field(..., max_length=200) longitude: float = Field(..., ge=-180, le=180) latitude: float = Field(..., ge=-90, le=90) status: CommunityStatus = Field(default=CommunityStatus.UNOPEN) qy_group_qrcode: Optional[str] = Field(None, max_length=200) webot_webhook: Optional[str] = Field(None, max_length=200) weekdays: Optional[List[int]] = Field(None, description="服务星期几,如[1,2,3,4,5]表示周一到周五") class CommunityUpdate(BaseModel): name: Optional[str] = Field(None, max_length=100) address: Optional[str] = Field(None, max_length=200) longitude: Optional[float] = Field(None, ge=-180, le=180) latitude: Optional[float] = Field(None, ge=-90, le=90) status: Optional[CommunityStatus] = None qy_group_qrcode: Optional[str] = Field(None, max_length=200) webot_webhook: Optional[str] = Field(None, max_length=200) base_price: Optional[float] = Field(None) extra_package_price: Optional[float] = Field(None) extra_package_threshold: Optional[int] = Field(None) more_station_price: Optional[float] = Field(None) weekdays: Optional[List[int]] = Field(None, description="服务星期几,如[1,2,3,4,5]表示周一到周五") admin_id: Optional[int] = Field(None) class CommunityInfo(BaseModel): id: int name: str address: str latitude: float longitude: float admin_id: Optional[int] = None status: CommunityStatus qy_group_qrcode: Optional[str] = None webot_webhook: Optional[str] = None base_price: Optional[float] = None extra_package_price: Optional[float] = None extra_package_threshold: Optional[int] = None more_station_price: Optional[float] = None weekdays: Optional[List[int]] = None optimized_qy_group_qrcode: Optional[str] = None distance: Optional[float] = None # 距离,单位:米 # 添加分润信息字段 profit_sharing: Optional[dict] = None # 添加管理员信息字段 admin: Optional[object] = None class Config: from_attributes = True @model_validator(mode='after') def set_admin(self): if hasattr(self, 'admin') and getattr(self, 'admin') is not None: admin = getattr(self, 'admin') # 检查admin是否已经是字典类型 if isinstance(admin, dict): return self # 处理UserDB对象 try: self.admin = { "id": admin.userid, "nickname": admin.nickname if hasattr(admin, 'nickname') else None, "phone": utils.CommonUtils.desensitize_phone(admin.phone) if hasattr(admin, 'phone') and admin.phone else None, "avatar": admin.avatar if hasattr(admin, 'avatar') else None } except Exception as e: # 如果处理失败,设置为None self.admin = None return self @model_validator(mode='after') def set_profit_sharing(self): # 从ORM对象中获取分润信息 if hasattr(self, 'community_profit_sharing') and getattr(self, 'community_profit_sharing') is not None: profit_sharing = getattr(self, 'community_profit_sharing') self.profit_sharing = { "id": profit_sharing.id, "platform_rate": profit_sharing.platform_rate, "partner_rate": profit_sharing.partner_rate, "admin_rate": profit_sharing.admin_rate, "delivery_rate": profit_sharing.delivery_rate } return self