|
@@ -15,6 +15,7 @@ import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
@@ -50,14 +51,29 @@ public class FoodRecommendService {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // 从 foods 表读取最新报告关联的食材推荐(正常确认流程写入 foods 表)
|
|
|
|
|
|
|
+ // 优先从 foods 表读取(正常确认流程写入 foods 表);
|
|
|
|
|
+ // 若 foods 表无数据(审核路径只写 report_food_suitability),回退到 report_food_suitability
|
|
|
List<Food> reportFoods = foodService.listByReportId(report.getId());
|
|
List<Food> reportFoods = foodService.listByReportId(report.getId());
|
|
|
-
|
|
|
|
|
if (reportFoods.isEmpty()) {
|
|
if (reportFoods.isEmpty()) {
|
|
|
- foodRecommendIndexMapper.delete(new LambdaQueryWrapper<FoodRecommendIndex>()
|
|
|
|
|
- .eq(FoodRecommendIndex::getUserId, userId));
|
|
|
|
|
- log.info("报告{}无食材推荐数据,已清除用户{}的推荐指数", report.getId(), userId);
|
|
|
|
|
- return;
|
|
|
|
|
|
|
+ List<ReportFoodSuitability> fallback = reportFoodSuitabilityMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<ReportFoodSuitability>()
|
|
|
|
|
+ .eq(ReportFoodSuitability::getReportId, report.getId()));
|
|
|
|
|
+ if (fallback.isEmpty()) {
|
|
|
|
|
+ foodRecommendIndexMapper.delete(new LambdaQueryWrapper<FoodRecommendIndex>()
|
|
|
|
|
+ .eq(FoodRecommendIndex::getUserId, userId));
|
|
|
|
|
+ log.info("报告{}无食材推荐数据,已清除用户{}的推荐指数", report.getId(), userId);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 回退:将 report_food_suitability 映射为 Food 列表后继续处理
|
|
|
|
|
+ reportFoods = new ArrayList<>();
|
|
|
|
|
+ for (ReportFoodSuitability rfs : fallback) {
|
|
|
|
|
+ Food f = new Food();
|
|
|
|
|
+ f.setId(rfs.getFoodId() != null ? rfs.getFoodId() : 0L);
|
|
|
|
|
+ f.setName(rfs.getFoodName());
|
|
|
|
|
+ f.setCategory(rfs.getCategory());
|
|
|
|
|
+ f.setScore(rfs.getScore());
|
|
|
|
|
+ reportFoods.add(f);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
List<FoodRecommendIndex> existingIndices = foodRecommendIndexMapper.selectList(
|
|
List<FoodRecommendIndex> existingIndices = foodRecommendIndexMapper.selectList(
|
|
@@ -139,6 +155,72 @@ public class FoodRecommendService {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取用户"当前食材"推荐列表(含分类与营养信息,供饮食推荐页展示)。
|
|
|
|
|
+ * 数据来自 food_recommend_idx(有新报告时 recalculateForUser 有则更新/无则添加),
|
|
|
|
|
+ * join foods 表补充 category/protein 等字段。
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> getCurrentFoods(Long userId) {
|
|
|
|
|
+ List<FoodRecommendIndex> indices = getIndexByUser(userId);
|
|
|
|
|
+ if (indices.isEmpty()) return new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ Set<Long> foodIds = new HashSet<>();
|
|
|
|
|
+ for (FoodRecommendIndex idx : indices) {
|
|
|
|
|
+ if (idx.getFoodId() != null) foodIds.add(idx.getFoodId());
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<Long, Food> foodMap = new HashMap<>();
|
|
|
|
|
+ if (!foodIds.isEmpty()) {
|
|
|
|
|
+ List<Food> foods = foodService.listByIds(foodIds);
|
|
|
|
|
+ for (Food f : foods) {
|
|
|
|
|
+ if (f != null) foodMap.put(f.getId(), f);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+ for (FoodRecommendIndex idx : indices) {
|
|
|
|
|
+ Food food = foodMap.get(idx.getFoodId());
|
|
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
+ m.put("name", food != null && food.getName() != null ? food.getName() : idx.getFoodName());
|
|
|
|
|
+ m.put("foodName", m.get("name"));
|
|
|
|
|
+ m.put("score", idx.getIndexScore());
|
|
|
|
|
+ m.put("category", food != null ? food.getCategory() : "");
|
|
|
|
|
+ if (food != null) {
|
|
|
|
|
+ m.put("protein", food.getProtein());
|
|
|
|
|
+ m.put("fat", food.getFat());
|
|
|
|
|
+ m.put("carbs", food.getCarbs());
|
|
|
|
|
+ m.put("fiber", food.getFiber());
|
|
|
|
|
+ m.put("energyKj", food.getEnergyKj());
|
|
|
|
|
+ }
|
|
|
|
|
+ result.add(m);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 将当前食材数据转换为 Food 实体列表,供 /api/health/foods 无 reportId 时使用 */
|
|
|
|
|
+ public List<Food> getCurrentFoodsAsFoodList(Long userId) {
|
|
|
|
|
+ List<Map<String, Object>> current = getCurrentFoods(userId);
|
|
|
|
|
+ List<Food> result = new ArrayList<>();
|
|
|
|
|
+ for (Map<String, Object> m : current) {
|
|
|
|
|
+ Food food = new Food();
|
|
|
|
|
+ food.setName((String) m.get("name"));
|
|
|
|
|
+ food.setCategory((String) m.get("category"));
|
|
|
|
|
+ Integer score = (Integer) m.get("score");
|
|
|
|
|
+ if (score != null) food.setScore(score);
|
|
|
|
|
+ BigDecimal protein = (BigDecimal) m.get("protein");
|
|
|
|
|
+ if (protein != null) food.setProtein(protein);
|
|
|
|
|
+ BigDecimal fat = (BigDecimal) m.get("fat");
|
|
|
|
|
+ if (fat != null) food.setFat(fat);
|
|
|
|
|
+ BigDecimal carbs = (BigDecimal) m.get("carbs");
|
|
|
|
|
+ if (carbs != null) food.setCarbs(carbs);
|
|
|
|
|
+ BigDecimal fiber = (BigDecimal) m.get("fiber");
|
|
|
|
|
+ if (fiber != null) food.setFiber(fiber);
|
|
|
|
|
+ BigDecimal energyKj = (BigDecimal) m.get("energyKj");
|
|
|
|
|
+ if (energyKj != null) food.setEnergyKj(energyKj);
|
|
|
|
|
+ result.add(food);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public List<FoodRecommendIdxLog> getIndexHistory(Long userId, Long foodId) {
|
|
public List<FoodRecommendIdxLog> getIndexHistory(Long userId, Long foodId) {
|
|
|
return foodRecommendIdxLogMapper.selectList(
|
|
return foodRecommendIdxLogMapper.selectList(
|
|
|
new LambdaQueryWrapper<FoodRecommendIdxLog>()
|
|
new LambdaQueryWrapper<FoodRecommendIdxLog>()
|