from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy import or_, select from sqlalchemy.orm import Session, selectinload from .shared import site_config from ..database import get_db from ..models import Destination, DestinationAlias, Lead, Product from ..schemas import LeadCreateIn, ProductQuery, ProductStatus from ..serializers import destination_dict, public_product_dict router = APIRouter() @router.get("/health") def health(): return {"ok": True, "service": "miniapp-api"} @router.get("/api/public/site-config") def get_site_config(db: Session = Depends(get_db)): return site_config(db, active_only=True) @router.get("/api/public/products") def list_products( keyword: str | None = None, destinationId: str | None = None, status_value: ProductStatus = Query(default="published", alias="status"), take: int = Query(default=48, ge=1, le=100), db: Session = Depends(get_db), ): query = ProductQuery(keyword=keyword, destinationId=destinationId, status=status_value, take=take) stmt = ( select(Product) .options(selectinload(Product.destination).selectinload(Destination.aliases), selectinload(Product.images)) .where(Product.status == query.status) .order_by(Product.sortWeight.asc(), Product.createdAt.asc()) .limit(query.take) ) if query.destinationId: stmt = stmt.where(Product.destinationId == query.destinationId) if query.keyword: pattern = f"%{query.keyword}%" stmt = ( stmt.outerjoin(Product.destination) .outerjoin(Destination.aliases) .where( or_( Product.title.ilike(pattern), Product.subtitle.ilike(pattern), Product.tags.any(query.keyword), Destination.name.ilike(pattern), DestinationAlias.alias.ilike(pattern), ) ) .distinct() ) products = db.scalars(stmt).unique().all() return {"items": [public_product_dict(product) for product in products]} @router.get("/api/public/products/{product_id}") def get_product(product_id: str, db: Session = Depends(get_db)): stmt = select(Product).options(selectinload(Product.destination), selectinload(Product.images)) if product_id.isdigit(): stmt = stmt.where(Product.sourceId == int(product_id)) else: stmt = stmt.where(Product.id == product_id) product = db.scalars(stmt).first() if not product: raise HTTPException(status_code=404, detail="线路不存在") return public_product_dict(product) @router.get("/api/public/destinations") def list_destinations(db: Session = Depends(get_db)): destinations = db.scalars( select(Destination) .options(selectinload(Destination.aliases)) .where(Destination.isActive.is_(True)) .order_by(Destination.sortOrder.asc()) ).all() return {"items": [destination_dict(destination) for destination in destinations]} @router.post("/api/public/leads", status_code=status.HTTP_201_CREATED) def create_lead(body: LeadCreateIn, db: Session = Depends(get_db)): lead = Lead(**body.model_dump(exclude_none=True), status="new") db.add(lead) db.commit() db.refresh(lead) return {"id": lead.id, "status": lead.status}