|
@@ -0,0 +1,147 @@
|
|
|
|
|
+package com.etotem.cfc.service;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.etotem.cfc.entity.FiveDimensionSelfCheck;
|
|
|
|
|
+import com.etotem.cfc.mapper.FiveDimensionSelfCheckMapper;
|
|
|
|
|
+import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+
|
|
|
|
|
+@Service
|
|
|
|
|
+public class SelfCheckAnalysisService {
|
|
|
|
|
+
|
|
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(SelfCheckAnalysisService.class);
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private AiGateway aiGateway;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private FiveDimensionSelfCheckMapper selfCheckMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成自检建议(AI 优先,失败返回 adviceJson=null + fallbackUsed=true)
|
|
|
|
|
+ */
|
|
|
|
|
+ public Map<String, Object> generateAdvice(Long userId, Map<String, Integer> scoreMap, List<Integer> questionIds) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ List<Map<String, Object>> recentHistory = getRecentHistory(userId, 3);
|
|
|
|
|
+ Map<String, Object> scoresWithMeta = new LinkedHashMap<>();
|
|
|
|
|
+ for (Map.Entry<String, Integer> entry : scoreMap.entrySet()) {
|
|
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
+ m.put("dimension", entry.getKey());
|
|
|
|
|
+ m.put("dimensionName", getDimensionName(entry.getKey()));
|
|
|
|
|
+ m.put("score", entry.getValue());
|
|
|
|
|
+ scoresWithMeta.put(entry.getKey(), m);
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, Object> inputs = new LinkedHashMap<>();
|
|
|
|
|
+ inputs.put("scores", scoresWithMeta);
|
|
|
|
|
+ inputs.put("questionIds", questionIds);
|
|
|
|
|
+ inputs.put("userId", userId);
|
|
|
|
|
+ inputs.put("recentHistory", recentHistory);
|
|
|
|
|
+ Map<String, Object> result = aiGateway.generateSelfCheckAdvice(inputs);
|
|
|
|
|
+ if (result == null) {
|
|
|
|
|
+ log.info("AI 自检建议生成失败,fallback");
|
|
|
|
|
+ Map<String, Object> fallback = new LinkedHashMap<>();
|
|
|
|
|
+ fallback.put("adviceJson", null);
|
|
|
|
|
+ fallback.put("fallbackUsed", true);
|
|
|
|
|
+ return fallback;
|
|
|
|
|
+ }
|
|
|
|
|
+ String adviceJson = (String) result.get("advice_json");
|
|
|
|
|
+ Boolean fallbackUsed = (Boolean) result.getOrDefault("fallback_used", false);
|
|
|
|
|
+ Map<String, Object> advResult = new LinkedHashMap<>();
|
|
|
|
|
+ advResult.put("adviceJson", adviceJson);
|
|
|
|
|
+ advResult.put("fallbackUsed", fallbackUsed);
|
|
|
|
|
+ return advResult;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("自检建议生成异常: {}", e.getMessage());
|
|
|
|
|
+ Map<String, Object> fallback = new LinkedHashMap<>();
|
|
|
|
|
+ fallback.put("adviceJson", null);
|
|
|
|
|
+ fallback.put("fallbackUsed", true);
|
|
|
|
|
+ return fallback;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成趋势分析(AI 优先,失败返回空 insight)
|
|
|
|
|
+ */
|
|
|
|
|
+ public Map<String, Object> generateTrend(Long userId) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ List<Map<String, Object>> history = getRecentHistory(userId, 3);
|
|
|
|
|
+ Map<String, Object> inputs = new LinkedHashMap<>();
|
|
|
|
|
+ inputs.put("history", history);
|
|
|
|
|
+ inputs.put("userId", userId);
|
|
|
|
|
+ Map<String, Object> result = aiGateway.generateSelfCheckTrend(inputs);
|
|
|
|
|
+ if (result == null) {
|
|
|
|
|
+ Map<String, Object> empty = new LinkedHashMap<>();
|
|
|
|
|
+ empty.put("aiInsight", "");
|
|
|
|
|
+ empty.put("trendSummary", "");
|
|
|
|
|
+ return empty;
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<String, Object> trendResult = new LinkedHashMap<>();
|
|
|
|
|
+ trendResult.put("aiInsight", result.getOrDefault("aiInsight", ""));
|
|
|
|
|
+ trendResult.put("trendSummary", result.getOrDefault("trendSummary", ""));
|
|
|
|
|
+ return trendResult;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("趋势分析异常: {}", e.getMessage());
|
|
|
|
|
+ Map<String, Object> empty = new LinkedHashMap<>();
|
|
|
|
|
+ empty.put("aiInsight", "");
|
|
|
|
|
+ empty.put("trendSummary", "");
|
|
|
|
|
+ return empty;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 获取最近 N 次自检历史(供 AI 和前端使用) */
|
|
|
|
|
+ public List<Map<String, Object>> getRecentHistory(Long userId, int limit) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ List<FiveDimensionSelfCheck> records = selfCheckMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<FiveDimensionSelfCheck>()
|
|
|
|
|
+ .eq(FiveDimensionSelfCheck::getUserId, userId)
|
|
|
|
|
+ .orderByDesc(FiveDimensionSelfCheck::getCreatedAt)
|
|
|
|
|
+ .last("LIMIT " + limit)
|
|
|
|
|
+ );
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+ for (FiveDimensionSelfCheck r : records) {
|
|
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
+ m.put("createdAt", r.getCreatedAt() != null ? r.getCreatedAt().toString() : "");
|
|
|
|
|
+ m.put("totalScore", r.getTotalScore());
|
|
|
|
|
+ if (r.getScoresJson() != null) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ Map<String, Integer> scores = objectMapper.readValue(r.getScoresJson(),
|
|
|
|
|
+ new TypeReference<Map<String, Integer>>() {});
|
|
|
|
|
+ List<Map<String, Object>> dims = new ArrayList<>();
|
|
|
|
|
+ for (Map.Entry<String, Integer> e : scores.entrySet()) {
|
|
|
|
|
+ Map<String, Object> d = new LinkedHashMap<>();
|
|
|
|
|
+ d.put("dimension", e.getKey());
|
|
|
|
|
+ d.put("name", getDimensionName(e.getKey()));
|
|
|
|
|
+ d.put("score", e.getValue());
|
|
|
|
|
+ dims.add(d);
|
|
|
|
|
+ }
|
|
|
|
|
+ m.put("dimensions", dims);
|
|
|
|
|
+ } catch (Exception ignored) {}
|
|
|
|
|
+ }
|
|
|
|
|
+ result.add(m);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.warn("获取自检历史失败: {}", e.getMessage());
|
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String getDimensionName(String dim) {
|
|
|
|
|
+ switch (dim) {
|
|
|
|
|
+ case "body": return "身";
|
|
|
|
|
+ case "wisdom": return "智";
|
|
|
|
|
+ case "wealth": return "富";
|
|
|
|
|
+ case "action": return "行";
|
|
|
|
|
+ case "mind": return "心";
|
|
|
|
|
+ default: return dim;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|