Bladeren bron

feat(service): add FoodRecommendService sourcing index from report scores

Xiaogang Liao 2 maanden geleden
bovenliggende
commit
44331ab3f2
1 gewijzigde bestanden met toevoegingen van 168 en 0 verwijderingen
  1. 168 0
      cfc-backend/src/main/java/com/etotem/cfc/service/FoodRecommendService.java

+ 168 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/FoodRecommendService.java

@@ -0,0 +1,168 @@
+package com.etotem.cfc.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.etotem.cfc.entity.FoodRecommendIndex;
+import com.etotem.cfc.entity.FoodRecommendIdxLog;
+import com.etotem.cfc.entity.FamilyMember;
+import com.etotem.cfc.entity.ReportFoodSuitability;
+import com.etotem.cfc.entity.HealthReport;
+import com.etotem.cfc.mapper.FoodRecommendIndexMapper;
+import com.etotem.cfc.mapper.FoodRecommendIdxLogMapper;
+import com.etotem.cfc.mapper.ReportFoodSuitabilityMapper;
+import com.etotem.cfc.mapper.FamilyMemberMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class FoodRecommendService {
+
+    @Resource
+    private FoodRecommendIndexMapper foodRecommendIndexMapper;
+
+    @Resource
+    private FoodRecommendIdxLogMapper foodRecommendIdxLogMapper;
+
+    @Resource
+    private ReportFoodSuitabilityMapper reportFoodSuitabilityMapper;
+
+    @Resource
+    private FamilyMemberMapper familyMemberMapper;
+
+    @Resource
+    private HealthReportService healthReportService;
+
+    @Transactional
+    public void recalculateForUser(Long userId) {
+        HealthReport report = healthReportService.getLatestReport(userId);
+        if (report == null) {
+            foodRecommendIndexMapper.delete(new LambdaQueryWrapper<FoodRecommendIndex>()
+                    .eq(FoodRecommendIndex::getUserId, userId));
+            log.info("用户{}无最新健康报告,已清除推荐指数", userId);
+            return;
+        }
+
+        List<ReportFoodSuitability> suitList = reportFoodSuitabilityMapper.selectList(
+                new LambdaQueryWrapper<ReportFoodSuitability>()
+                        .eq(ReportFoodSuitability::getReportId, report.getId())
+        );
+
+        if (suitList.isEmpty()) {
+            foodRecommendIndexMapper.delete(new LambdaQueryWrapper<FoodRecommendIndex>()
+                    .eq(FoodRecommendIndex::getUserId, userId));
+            log.info("报告{}无食材适宜度数据,已清除用户{}的推荐指数", report.getId(), userId);
+            return;
+        }
+
+        List<FoodRecommendIndex> existingIndices = foodRecommendIndexMapper.selectList(
+                new LambdaQueryWrapper<FoodRecommendIndex>()
+                        .eq(FoodRecommendIndex::getUserId, userId)
+        );
+
+        Map<Long, FoodRecommendIndex> existingMap = existingIndices.stream()
+                .collect(Collectors.toMap(FoodRecommendIndex::getFoodId, idx -> idx));
+
+        Date now = new Date();
+        Long reportId = report.getId();
+
+        for (ReportFoodSuitability suit : suitList) {
+            Long foodId = suit.getFoodId();
+            Integer newIndex = suit.getScore();
+            FoodRecommendIndex existing = existingMap.get(foodId);
+
+            if (existing != null) {
+                if (!Objects.equals(existing.getIndexScore(), newIndex)) {
+                    Integer oldIndex = existing.getIndexScore();
+                    existing.setIndexScore(newIndex);
+                    existing.setHealthReportId(reportId);
+                    existing.setCalculatedAt(now);
+                    foodRecommendIndexMapper.updateById(existing);
+
+                    FoodRecommendIdxLog logEntry = new FoodRecommendIdxLog();
+                    logEntry.setUserId(userId);
+                    logEntry.setFoodId(foodId);
+                    logEntry.setFoodName(suit.getFoodName());
+                    logEntry.setPreviousIndex(oldIndex);
+                    logEntry.setNewIndex(newIndex);
+                    logEntry.setHealthReportId(reportId);
+                    logEntry.setChangeReason("健康报告更新");
+                    logEntry.setChangedAt(now);
+                    foodRecommendIdxLogMapper.insert(logEntry);
+
+                    log.info("用户{}食材{}推荐指数变更: {} -> {}", userId, foodId, oldIndex, newIndex);
+                }
+            } else {
+                FoodRecommendIndex index = new FoodRecommendIndex();
+                index.setUserId(userId);
+                index.setFoodId(foodId);
+                index.setFoodName(suit.getFoodName());
+                index.setIndexScore(newIndex);
+                index.setHealthReportId(reportId);
+                index.setCalculatedAt(now);
+                index.setCreatedAt(now);
+                foodRecommendIndexMapper.insert(index);
+            }
+            existingMap.remove(foodId);
+        }
+
+        for (Map.Entry<Long, FoodRecommendIndex> entry : existingMap.entrySet()) {
+            FoodRecommendIndex removed = entry.getValue();
+            foodRecommendIndexMapper.deleteById(removed.getId());
+
+            FoodRecommendIdxLog logEntry = new FoodRecommendIdxLog();
+            logEntry.setUserId(userId);
+            logEntry.setFoodId(removed.getFoodId());
+            logEntry.setFoodName(removed.getFoodName());
+            logEntry.setPreviousIndex(removed.getIndexScore());
+            logEntry.setNewIndex(null);
+            logEntry.setHealthReportId(reportId);
+            logEntry.setChangeReason("已从最新健康报告中移除");
+            logEntry.setChangedAt(now);
+            foodRecommendIdxLogMapper.insert(logEntry);
+
+            log.info("用户{}食材{}已从最新报告中移除", userId, removed.getFoodId());
+        }
+    }
+
+    public List<FoodRecommendIndex> getIndexByUser(Long userId) {
+        return foodRecommendIndexMapper.selectList(
+                new LambdaQueryWrapper<FoodRecommendIndex>()
+                        .eq(FoodRecommendIndex::getUserId, userId)
+        );
+    }
+
+    public List<FoodRecommendIdxLog> getIndexHistory(Long userId, Long foodId) {
+        return foodRecommendIdxLogMapper.selectList(
+                new LambdaQueryWrapper<FoodRecommendIdxLog>()
+                        .eq(FoodRecommendIdxLog::getUserId, userId)
+                        .eq(FoodRecommendIdxLog::getFoodId, foodId)
+                        .orderByDesc(FoodRecommendIdxLog::getChangedAt)
+        );
+    }
+
+    public Map<Long, List<FoodRecommendIndex>> getByFamily(Long familyId) {
+        List<FamilyMember> members = familyMemberMapper.selectList(
+                new LambdaQueryWrapper<FamilyMember>()
+                        .eq(FamilyMember::getFamilyId, familyId)
+        );
+
+        Map<Long, List<FoodRecommendIndex>> result = new HashMap<>();
+        for (FamilyMember member : members) {
+            Long memberUserId = member.getUserId();
+            if (memberUserId != null) {
+                List<FoodRecommendIndex> indices = foodRecommendIndexMapper.selectList(
+                        new LambdaQueryWrapper<FoodRecommendIndex>()
+                                .eq(FoodRecommendIndex::getUserId, memberUserId)
+                );
+                if (!indices.isEmpty()) {
+                    result.put(memberUserId, indices);
+                }
+            }
+        }
+        return result;
+    }
+}