PMFlow/backend/app/api/recipes.py

166 lines
5.0 KiB
Python

"""食谱 API - 支持热量识别与营养分析"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
from sqlalchemy.ext.asyncio import AsyncSession
from ..core.database import get_db
from ..dependencies.auth import get_current_tenant_id
from ..schemas.recipe import (
RecipeCreate,
RecipeUpdate,
RecipeResponse,
RecipeImageUploadRequest,
RecipeAnalyzeCaloriesResponse,
)
from ..services.recipe import get_recipe_service
router = APIRouter(prefix="/recipes", tags=["食谱"])
@router.post("/", response_model=RecipeResponse, summary="创建食谱")
async def create_recipe(
recipe_data: RecipeCreate,
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_current_tenant_id),
):
"""创建新食谱"""
service = get_recipe_service()
recipe = await service.create_recipe(db, recipe_data, tenant_id)
return recipe
@router.post("/analyze", response_model=RecipeAnalyzeCaloriesResponse, summary="AI 热量分析")
async def analyze_food_calories(
recipe_data: RecipeCreate,
food_description: str,
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_current_tenant_id),
):
"""
创建食谱并进行 AI 热量分析
- food_description: 食物描述(可以是图片识别后的描述)
"""
service = get_recipe_service()
recipe, analysis = await service.create_recipe_with_ai_analysis(
db, recipe_data, food_description, tenant_id
)
if not analysis["success"]:
return RecipeAnalyzeCaloriesResponse(
success=False,
data=None,
message=analysis["message"],
)
return RecipeAnalyzeCaloriesResponse(
success=True,
data=recipe,
message=analysis["message"],
)
@router.get("/", response_model=list[RecipeResponse], summary="获取食谱列表")
async def list_recipes(
skip: int = 0,
limit: int = 20,
category: Optional[str] = None,
cuisine: Optional[str] = None,
status: Optional[str] = None,
is_favorite: Optional[bool] = None,
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_current_tenant_id),
):
"""获取食谱列表,支持分类筛选"""
service = get_recipe_service()
recipes = await service.list_recipes(
db,
tenant_id,
skip=skip,
limit=limit,
category=category,
cuisine=cuisine,
status=status,
is_favorite=is_favorite,
)
return recipes
@router.get("/{recipe_id}", response_model=RecipeResponse, summary="获取单个食谱")
async def get_recipe(
recipe_id: int,
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_current_tenant_id),
):
"""获取指定食谱详情"""
service = get_recipe_service()
recipe = await service.get_recipe(db, recipe_id, tenant_id)
if not recipe:
raise HTTPException(status_code=404, detail="食谱不存在")
return recipe
@router.put("/{recipe_id}", response_model=RecipeResponse, summary="更新食谱")
async def update_recipe(
recipe_id: int,
recipe_data: RecipeUpdate,
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_current_tenant_id),
):
"""更新食谱信息"""
service = get_recipe_service()
recipe = await service.update_recipe(db, recipe_id, recipe_data, tenant_id)
if not recipe:
raise HTTPException(status_code=404, detail="食谱不存在")
return recipe
@router.delete("/{recipe_id}", summary="删除食谱")
async def delete_recipe(
recipe_id: int,
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_current_tenant_id),
):
"""删除食谱"""
service = get_recipe_service()
success = await service.delete_recipe(db, recipe_id, tenant_id)
if not success:
raise HTTPException(status_code=404, detail="食谱不存在")
return {"message": "删除成功"}
@router.post("/{recipe_id}/favorite", response_model=RecipeResponse, summary="收藏/取消收藏食谱")
async def toggle_favorite(
recipe_id: int,
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_current_tenant_id),
):
"""收藏或取消收藏食谱"""
service = get_recipe_service()
recipe = await service.get_recipe(db, recipe_id, tenant_id)
if not recipe:
raise HTTPException(status_code=404, detail="食谱不存在")
recipe.is_favorite = not recipe.is_favorite
await db.commit()
await db.refresh(recipe)
return recipe
@router.post("/upload-image", response_model=dict, summary="上传图片(示例)")
async def upload_recipe_image(
image_base64: str,
filename: Optional[str] = None,
db: AsyncSession = Depends(get_db),
tenant_id: int = Depends(get_current_tenant_id),
):
"""
上传食谱图片
注意:这是示例实现,实际项目中需要集成对象存储服务(如 S3、OSS、COS 等)
"""
service = get_recipe_service()
image_url = await service.upload_image(image_base64, filename)
return {"image_url": image_url}