فهرست منبع

feat(diet): 换一批seed生效 + 食材搜索接口

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
E2E Test Bot 2 ماه پیش
والد
کامیت
ac6f052d38

+ 7 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/diet/DietIngredientController.java

@@ -48,4 +48,11 @@ public class DietIngredientController {
             @RequestBody Map<String, Object> request) {
         return Result.success(dietIngredientService.confirmIngredients(familyId, request));
     }
+
+    @PostMapping("/search")
+    public Result<Map> searchFoods(@RequestBody(required = false) Map<String, Object> request) {
+        String keyword = request != null && request.get("keyword") != null
+                ? request.get("keyword").toString() : "";
+        return Result.success(dietIngredientService.searchFoods(keyword));
+    }
 }

+ 19 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/BeijingNutritionService.java

@@ -559,6 +559,18 @@ public class BeijingNutritionService {
      * @return 推荐食材列表(Top 15,含推荐理由)
      */
     public List<IngredientRecommendation> generateIngredientList(Long familyId, java.time.LocalDate date) {
+        return generateIngredientList(familyId, date, 0);
+    }
+
+    /**
+     * 生成食材推荐列表(支持 seed 偏移,使"换一批"真正返回不同组合)
+     *
+     * @param familyId 家庭ID
+     * @param date     日期
+     * @param seed     偏移种子(0=默认截取Top 15,>0=旋转后截取)
+     * @return 推荐食材列表(Top 15,含推荐理由)
+     */
+    public List<IngredientRecommendation> generateIngredientList(Long familyId, java.time.LocalDate date, int seed) {
         List<IngredientRecommendation> result = new ArrayList<>();
         
         // 1. 查 meal_configs → participant_member_ids
@@ -711,7 +723,13 @@ public class BeijingNutritionService {
         
         // 6. 排序并返回 Top 15
         result.sort((a, b) -> Integer.compare(b.getScore(), a.getScore()));
-        return result.size() > 15 ? result.subList(0, 15) : result;
+        if (seed != 0 && result.size() > 1) {
+            int offset = Math.abs(seed) % result.size();
+            if (offset > 0) {
+                java.util.Collections.rotate(result, offset);
+            }
+        }
+        return result.size() > 15 ? new ArrayList<>(result.subList(0, 15)) : result;
     }
     
     private boolean isWeekend(java.time.LocalDate date) {

+ 39 - 4
cfc-backend/src/main/java/com/etotem/cfc/service/DietIngredientService.java

@@ -1,6 +1,8 @@
 package com.etotem.cfc.service;
 
 import com.etotem.cfc.dto.IngredientRecommendation;
+import com.etotem.cfc.entity.Food;
+import com.etotem.cfc.mapper.FoodMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -15,6 +17,9 @@ public class DietIngredientService {
     @Resource
     private BeijingNutritionService beijingNutritionService;
 
+    @Resource
+    private FoodMapper foodMapper;
+
     // 会话内食材选择(按 familyId 隔离)
     private final Map<Long, List<IngredientRecommendation>> selectedIngredients = new HashMap<>();
     private final Map<Long, Integer> refreshSeed = new HashMap<>();
@@ -33,11 +38,11 @@ public class DietIngredientService {
     public Map<String, Object> refreshIngredients(Long familyId) {
         Integer seed = refreshSeed.getOrDefault(familyId, 0) + 1;
         refreshSeed.put(familyId, seed);
-        
-        // 重新生成(通过随机种子变化)
-        List<IngredientRecommendation> suggestions = beijingNutritionService.generateIngredientList(familyId, LocalDate.now());
+
+        // 重新生成(通过 seed 偏移截取,真正换一批)
+        List<IngredientRecommendation> suggestions = beijingNutritionService.generateIngredientList(familyId, LocalDate.now(), seed);
         selectedIngredients.put(familyId, suggestions);
-        
+
         Map<String, Object> result = new HashMap<>();
         result.put("ingredients", suggestions);
         result.put("selectedCount", 0);
@@ -88,4 +93,34 @@ public class DietIngredientService {
         
         return result;
     }
+
+    /**
+     * 搜索食材(供"添加食材"弹窗使用)
+     *
+     * @param keyword 关键字(空=返回全部,最多50条)
+     * @return {foods:[{id,name,category}]}
+     */
+    public Map<String, Object> searchFoods(String keyword) {
+        List<Map<String, Object>> foods = new ArrayList<>();
+        List<Food> all = foodMapper.selectList(
+                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Food>()
+                        .eq(Food::getStatus, "active")
+                        .orderByAsc(Food::getSortOrder)
+        );
+        String kw = keyword == null ? "" : keyword.trim();
+        int count = 0;
+        for (Food f : all) {
+            if (kw.isEmpty() || (f.getName() != null && f.getName().contains(kw))) {
+                Map<String, Object> item = new HashMap<>();
+                item.put("id", f.getId());
+                item.put("name", f.getName());
+                item.put("category", f.getCategory());
+                foods.add(item);
+                if (++count >= 50) break;
+            }
+        }
+        Map<String, Object> result = new HashMap<>();
+        result.put("foods", foods);
+        return result;
+    }
 }