|
@@ -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;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|