136 lines
4.1 KiB
Python
136 lines
4.1 KiB
Python
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("/<int:recipe_id>")
|
|
@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("/<int:recipe_id>")
|
|
@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("/<int:recipe_id>")
|
|
@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}, "食谱导入完成")
|