import json from pathlib import Path from flask import Blueprint, current_app, request from flask_jwt_extended import jwt_required from app.extensions import db from app.models import Recipe from app.utils.auth import admin_required from app.utils.response import fail, ok recipe_bp = Blueprint("recipe", __name__) def _recipe_payload(recipe: Recipe, payload: dict) -> None: recipe.name = payload.get("name", recipe.name) recipe.category = payload.get("category", recipe.category or "轻食") recipe.description = payload.get("description", recipe.description or "") recipe.calories = float(payload.get("calories", recipe.calories or 0)) recipe.protein = float(payload.get("protein", recipe.protein or 0)) recipe.fat = float(payload.get("fat", recipe.fat or 0)) recipe.carbs = float(payload.get("carbs", recipe.carbs or 0)) recipe.fiber = float(payload.get("fiber", recipe.fiber or 0)) recipe.tags = payload.get("tags", recipe.tags or []) recipe.difficulty = payload.get("difficulty", recipe.difficulty or "easy") @recipe_bp.get("") @jwt_required(optional=True) def list_recipes(): keyword = (request.args.get("keyword") or "").strip() category = (request.args.get("category") or "").strip() page = max(int(request.args.get("page", 1) or 1), 1) page_size = min(max(int(request.args.get("page_size", 10) or 10), 1), 50) query = Recipe.query if keyword: query = query.filter(Recipe.name.like(f"%{keyword}%")) if category: query = query.filter(Recipe.category == category) pagination = query.order_by(Recipe.id.desc()).paginate(page=page, per_page=page_size, error_out=False) data = { "items": [item.to_dict() for item in pagination.items], "total": pagination.total, "page": page, "page_size": page_size, } return ok(data) @recipe_bp.get("/") @jwt_required(optional=True) def get_recipe(recipe_id: int): recipe = Recipe.query.get(recipe_id) if not recipe: return fail("食谱不存在", 404) return ok(recipe.to_dict()) @recipe_bp.post("") @admin_required def create_recipe(): payload = request.get_json(silent=True) or {} if not payload.get("name"): return fail("缺少食谱名称", 400) recipe = Recipe() _recipe_payload(recipe, payload) db.session.add(recipe) db.session.commit() return ok(recipe.to_dict(), "食谱创建成功") @recipe_bp.put("/") @admin_required def update_recipe(recipe_id: int): recipe = Recipe.query.get(recipe_id) if not recipe: return fail("食谱不存在", 404) payload = request.get_json(silent=True) or {} _recipe_payload(recipe, payload) db.session.commit() return ok(recipe.to_dict(), "食谱更新成功") @recipe_bp.delete("/") @admin_required def delete_recipe(recipe_id: int): recipe = Recipe.query.get(recipe_id) if not recipe: return fail("食谱不存在", 404) db.session.delete(recipe) db.session.commit() return ok({}, "食谱删除成功") @recipe_bp.post("/import") @admin_required def import_recipes(): payload = request.get_json(silent=True) or {} rows = payload.get("items") if not isinstance(rows, list): seed_file = Path(current_app.root_path).parents[0] / "seed" / "recipes_seed.json" if seed_file.exists(): with seed_file.open("r", encoding="utf-8-sig") as file: rows = json.load(file) else: return fail("导入数据为空,且未找到默认种子文件", 400) created_count = 0 updated_count = 0 for item in rows: name = (item.get("name") or "").strip() if not name: continue recipe = Recipe.query.filter_by(name=name).first() if recipe: _recipe_payload(recipe, item) updated_count += 1 else: recipe = Recipe() _recipe_payload(recipe, item) db.session.add(recipe) created_count += 1 db.session.commit() return ok({"created": created_count, "updated": updated_count}, "食谱导入完成")