import json from collections import defaultdict class NutritionService: def __init__(self, food_db_path, config_path): with open(food_db_path, "r", encoding="utf-8") as f: self.food_db = json.load(f) with open(config_path, "r", encoding="utf-8") as f: self.config = json.load(f) def build_report_data(self, front_json): meals = front_json.get("meals", []) merged_meals = self._merge_meals(meals) food_details, nutrition_total = self._calculate_nutrition(merged_meals) category_result = self._build_category_result_from_foods(food_details) suggestions = self._generate_suggestions(category_result) return { "user": front_json.get("user", {}), "date": front_json.get("date"), "foods": food_details, "category_result": category_result, "suggestions": suggestions, "nutrition": nutrition_total } def _merge_meals(self, meals): meal_sum = defaultdict(float) for item in meals: name = str(item.get("name", "")).strip() amount = item.get("amount", 0) if not name: continue try: amount = float(amount) except Exception: amount = 0 meal_sum[name] += amount return meal_sum def _calculate_nutrition(self, merged_meals): details = [] total_energy = 0.0 total_protein = 0.0 total_fat = 0.0 total_carb = 0.0 total_calcium = 0.0 for food_name, grams in merged_meals.items(): food_info = self.food_db.get(food_name) if not food_info: # 如果没匹配到食物库,可以先跳过 continue factor = grams / 100.0 energy = self._safe_float(food_info.get("energy")) * factor protein = self._safe_float(food_info.get("protein")) * factor fat = self._safe_float(food_info.get("fat")) * factor carbohydrate = self._safe_float(food_info.get("carbohydrate")) * factor calcium = self._safe_float(food_info.get("calcium")) * factor total_energy += energy total_protein += protein total_fat += fat total_carb += carbohydrate total_calcium += calcium details.append({ "name": food_name, "amount": round(grams, 1), "category": food_info.get("category", ""), "nutrition": { "energy": round(energy, 2), "protein": round(protein, 2), "fat": round(fat, 2), "carbohydrate": round(carbohydrate, 2), "calcium": round(calcium, 2) } }) nutrition_total = { "energy": { "value": round(total_energy, 2), "evaluation": self._eval_nutrition(total_energy, "energy") }, "protein": round(total_protein, 2), "fat": round(total_fat, 2), "carbohydrate": round(total_carb, 2), "calcium": { "value": round(total_calcium, 2), "evaluation": self._eval_nutrition(total_calcium, "calcium") } } return details, nutrition_total def _build_category_result_from_foods(self, food_details): # 1. 先把配置里的所有分类都初始化为 0 category_sum = { category: 0.0 for category in self.config["food_category_map"].keys() } # 2. 把实际食物摄入量累加进去 for item in food_details: category = item.get("category", "").strip() amount = self._safe_float(item.get("amount", 0)) if category in category_sum: category_sum[category] += amount # 3. 所有分类都生成评价结果 result = {} for cat, val in category_sum.items(): result[cat] = { "value": round(val, 1), "evaluation": self._evaluate_category(cat, val) } return result def _evaluate_category(self, category, value): rules = self.config["category_evaluation"].get( category, self.config["category_evaluation"].get("default", []) ) for r in rules: if r["min"] <= value <= r["max"]: return r["label"] return "-" def _generate_suggestions(self, category_result): rules = self.config["suggestion_rules"] res = [] for cat, info in category_result.items(): label = info["evaluation"] if label in rules and rules[label]: res.append(rules[label].replace("{category}", cat)) return res def _eval_nutrition(self, value, key): rules = self.config["nutrition_eval"][key] try: value = float(value) except Exception: return "-" for r in rules: if r["min"] <= value <= r["max"]: return r["label"] return "-" def _safe_float(self, value, default=0.0): try: return float(value) except Exception: return default