2025-07-14 17:46:20 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
数据查询API路由
|
|
|
|
|
|
支持景区、产品、风格、受众等基础数据的查询
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
|
from typing import List, Dict, Any, Optional
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
|
|
|
|
|
|
from api.services.database_service import DatabaseService
|
|
|
|
|
|
from api.dependencies import get_database_service
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter(
|
|
|
|
|
|
prefix="/data",
|
|
|
|
|
|
tags=["data"],
|
|
|
|
|
|
responses={404: {"description": "Not found"}},
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ScenicSpotResponse(BaseModel):
|
|
|
|
|
|
"""景区响应模型"""
|
|
|
|
|
|
id: int = Field(..., description="景区ID")
|
|
|
|
|
|
name: str = Field(..., description="景区名称")
|
|
|
|
|
|
address: Optional[str] = Field(None, description="地址")
|
|
|
|
|
|
description: Optional[str] = Field(None, description="描述")
|
|
|
|
|
|
advantage: Optional[str] = Field(None, description="优势")
|
|
|
|
|
|
highlight: Optional[str] = Field(None, description="亮点")
|
|
|
|
|
|
isPublic: bool = Field(..., description="是否公开")
|
|
|
|
|
|
userId: int = Field(..., description="用户ID")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ProductResponse(BaseModel):
|
|
|
|
|
|
"""产品响应模型"""
|
|
|
|
|
|
id: int = Field(..., description="产品ID")
|
2025-07-18 19:32:55 +08:00
|
|
|
|
productName: str = Field(..., description="产品名称")
|
2025-07-14 17:46:20 +08:00
|
|
|
|
originPrice: Optional[float] = Field(None, description="原价")
|
|
|
|
|
|
realPrice: Optional[float] = Field(None, description="实际价格")
|
|
|
|
|
|
packageInfo: Optional[str] = Field(None, description="套票信息")
|
2025-07-18 19:32:55 +08:00
|
|
|
|
detailedDescription: Optional[str] = Field(None, description="产品描述")
|
|
|
|
|
|
keyAdvantages: Optional[str] = Field(None, description="产品优势")
|
|
|
|
|
|
highlights: Optional[str] = Field(None, description="产品亮点")
|
|
|
|
|
|
usageRules: Optional[str] = Field(None, description="使用规则详细说明")
|
|
|
|
|
|
surcharge: Optional[str] = Field(None, description="加价说明")
|
|
|
|
|
|
reservation: Optional[str] = Field(None, description="预约规则")
|
|
|
|
|
|
refund: Optional[str] = Field(None, description="退改政策")
|
|
|
|
|
|
discounts: Optional[str] = Field(None, description="优惠内容")
|
2025-07-14 17:46:20 +08:00
|
|
|
|
isPublic: bool = Field(..., description="是否公开")
|
|
|
|
|
|
userId: int = Field(..., description="用户ID")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class StyleResponse(BaseModel):
|
|
|
|
|
|
"""风格响应模型"""
|
|
|
|
|
|
id: int = Field(..., description="风格ID")
|
|
|
|
|
|
styleName: str = Field(..., description="风格名称")
|
|
|
|
|
|
description: Optional[str] = Field(None, description="描述")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AudienceResponse(BaseModel):
|
|
|
|
|
|
"""受众响应模型"""
|
|
|
|
|
|
id: int = Field(..., description="受众ID")
|
|
|
|
|
|
audienceName: str = Field(..., description="受众名称")
|
|
|
|
|
|
description: Optional[str] = Field(None, description="描述")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/scenic-spots", response_model=List[ScenicSpotResponse], summary="获取景区列表")
|
|
|
|
|
|
async def get_scenic_spots(
|
|
|
|
|
|
user_id: Optional[int] = Query(None, description="用户ID,如果提供则只返回该用户的景区"),
|
|
|
|
|
|
is_public: Optional[bool] = Query(None, description="是否公开,如果提供则过滤公开/私有景区"),
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取景区列表
|
|
|
|
|
|
|
|
|
|
|
|
- **user_id**: 用户ID,如果提供则只返回该用户的景区
|
|
|
|
|
|
- **is_public**: 是否公开,如果提供则过滤公开/私有景区
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
spots = db_service.list_all_scenic_spots(user_id=user_id, is_public=is_public)
|
|
|
|
|
|
return [ScenicSpotResponse(**spot) for spot in spots]
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取景区列表失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取景区列表失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/scenic-spots/{spot_id}", response_model=ScenicSpotResponse, summary="根据ID获取景区信息")
|
|
|
|
|
|
async def get_scenic_spot_by_id(
|
|
|
|
|
|
spot_id: int,
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据ID获取景区信息
|
|
|
|
|
|
|
|
|
|
|
|
- **spot_id**: 景区ID
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
spot = db_service.get_scenic_spot_by_id(spot_id)
|
|
|
|
|
|
if not spot:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=f"未找到ID为{spot_id}的景区")
|
|
|
|
|
|
|
|
|
|
|
|
return ScenicSpotResponse(**spot)
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取景区信息失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取景区信息失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/products", response_model=List[ProductResponse], summary="获取产品列表")
|
|
|
|
|
|
async def get_products(
|
|
|
|
|
|
user_id: Optional[int] = Query(None, description="用户ID,如果提供则只返回该用户的产品"),
|
|
|
|
|
|
is_public: Optional[bool] = Query(None, description="是否公开,如果提供则过滤公开/私有产品"),
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取产品列表
|
|
|
|
|
|
|
|
|
|
|
|
- **user_id**: 用户ID,如果提供则只返回该用户的产品
|
|
|
|
|
|
- **is_public**: 是否公开,如果提供则过滤公开/私有产品
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
products = db_service.list_all_products(user_id=user_id, is_public=is_public)
|
|
|
|
|
|
return [ProductResponse(**product) for product in products]
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取产品列表失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取产品列表失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/products/{product_id}", response_model=ProductResponse, summary="根据ID获取产品信息")
|
|
|
|
|
|
async def get_product_by_id(
|
|
|
|
|
|
product_id: int,
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据ID获取产品信息
|
|
|
|
|
|
|
|
|
|
|
|
- **product_id**: 产品ID
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
product = db_service.get_product_by_id(product_id)
|
|
|
|
|
|
if not product:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=f"未找到ID为{product_id}的产品")
|
|
|
|
|
|
|
|
|
|
|
|
return ProductResponse(**product)
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取产品信息失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取产品信息失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/styles", response_model=List[StyleResponse], summary="获取风格列表")
|
|
|
|
|
|
async def get_styles(
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取所有风格列表
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
styles = db_service.list_all_styles()
|
|
|
|
|
|
return [StyleResponse(**style) for style in styles]
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取风格列表失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取风格列表失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/styles/{style_id}", response_model=StyleResponse, summary="根据ID获取风格信息")
|
|
|
|
|
|
async def get_style_by_id(
|
|
|
|
|
|
style_id: int,
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据ID获取风格信息
|
|
|
|
|
|
|
|
|
|
|
|
- **style_id**: 风格ID
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
style = db_service.get_style_by_id(style_id)
|
|
|
|
|
|
if not style:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=f"未找到ID为{style_id}的风格")
|
|
|
|
|
|
|
|
|
|
|
|
return StyleResponse(**style)
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取风格信息失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取风格信息失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/audiences", response_model=List[AudienceResponse], summary="获取受众列表")
|
|
|
|
|
|
async def get_audiences(
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
获取所有受众列表
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
audiences = db_service.list_all_audiences()
|
|
|
|
|
|
return [AudienceResponse(**audience) for audience in audiences]
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取受众列表失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取受众列表失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/audiences/{audience_id}", response_model=AudienceResponse, summary="根据ID获取受众信息")
|
|
|
|
|
|
async def get_audience_by_id(
|
|
|
|
|
|
audience_id: int,
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
根据ID获取受众信息
|
|
|
|
|
|
|
|
|
|
|
|
- **audience_id**: 受众ID
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
audience = db_service.get_audience_by_id(audience_id)
|
|
|
|
|
|
if not audience:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail=f"未找到ID为{audience_id}的受众")
|
|
|
|
|
|
|
|
|
|
|
|
return AudienceResponse(**audience)
|
|
|
|
|
|
except HTTPException:
|
|
|
|
|
|
raise
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"获取受众信息失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"获取受众信息失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/scenic-spots/batch", response_model=List[ScenicSpotResponse], summary="批量获取景区信息")
|
|
|
|
|
|
async def get_scenic_spots_batch(
|
|
|
|
|
|
spot_ids: List[int],
|
|
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
批量获取景区信息
|
|
|
|
|
|
|
|
|
|
|
|
- **spot_ids**: 景区ID列表
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
|
|
|
|
|
spots = db_service.get_scenic_spots_by_ids(spot_ids)
|
|
|
|
|
|
return [ScenicSpotResponse(**spot) for spot in spots]
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"批量获取景区信息失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"批量获取景区信息失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/products/batch", response_model=List[ProductResponse], summary="批量获取产品信息")
|
|
|
|
|
|
async def get_products_batch(
|
2025-07-15 10:59:36 +08:00
|
|
|
|
productIds: List[int],
|
2025-07-14 17:46:20 +08:00
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
批量获取产品信息
|
|
|
|
|
|
|
2025-07-15 10:59:36 +08:00
|
|
|
|
- **productIds**: 产品ID列表
|
2025-07-14 17:46:20 +08:00
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
2025-07-15 10:59:36 +08:00
|
|
|
|
products = db_service.get_products_by_ids(productIds)
|
2025-07-14 17:46:20 +08:00
|
|
|
|
return [ProductResponse(**product) for product in products]
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"批量获取产品信息失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"批量获取产品信息失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/styles/batch", response_model=List[StyleResponse], summary="批量获取风格信息")
|
|
|
|
|
|
async def get_styles_batch(
|
2025-07-15 10:59:36 +08:00
|
|
|
|
styleIds: List[int],
|
2025-07-14 17:46:20 +08:00
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
批量获取风格信息
|
|
|
|
|
|
|
2025-07-15 10:59:36 +08:00
|
|
|
|
- **styleIds**: 风格ID列表
|
2025-07-14 17:46:20 +08:00
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
2025-07-15 10:59:36 +08:00
|
|
|
|
styles = db_service.get_styles_by_ids(styleIds)
|
2025-07-14 17:46:20 +08:00
|
|
|
|
return [StyleResponse(**style) for style in styles]
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"批量获取风格信息失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"批量获取风格信息失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/audiences/batch", response_model=List[AudienceResponse], summary="批量获取受众信息")
|
|
|
|
|
|
async def get_audiences_batch(
|
2025-07-15 10:59:36 +08:00
|
|
|
|
audienceIds: List[int],
|
2025-07-14 17:46:20 +08:00
|
|
|
|
db_service: DatabaseService = Depends(get_database_service)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""
|
|
|
|
|
|
批量获取受众信息
|
|
|
|
|
|
|
2025-07-15 10:59:36 +08:00
|
|
|
|
- **audienceIds**: 受众ID列表
|
2025-07-14 17:46:20 +08:00
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
if not db_service.is_available():
|
|
|
|
|
|
raise HTTPException(status_code=503, detail="数据库服务不可用")
|
|
|
|
|
|
|
2025-07-15 10:59:36 +08:00
|
|
|
|
audiences = db_service.get_audiences_by_ids(audienceIds)
|
2025-07-14 17:46:20 +08:00
|
|
|
|
return [AudienceResponse(**audience) for audience in audiences]
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"批量获取受众信息失败: {e}", exc_info=True)
|
|
|
|
|
|
raise HTTPException(status_code=500, detail=f"批量获取受众信息失败: {str(e)}")
|