Browse Source

fix(json): 消除 AI 建议与 structuredAnalysis 解析 WARN 日志

1. WuxingSourcingAdviceVO:补 AI 五行生克链字段(upstream/restrainer 各 5 项 + action),
   加 @JsonIgnoreProperties(ignoreUnknown=true) 兜底未知字段,避免 AI 输出扩展
   导致 Jackson 反序列化整体失败(FiveDimensionSelfCheckService.parseAdvices)。

2. EnergyService/EmiReportService.parseStructuredAnalysis:structured_analysis
   实际存储的是 DAN 解析 items 平铺数组 [{code,name,value,category},...]
   (DanReportUploadService.confirmUpload 写入),并非嵌套对象 {extra:{...}}。
   原 JSON.parseObject(Map.class) 遇 [ 开头抛 fastjson illegal input WARN。
   现对 [ 开头(平铺数组)静默返回 null,调用方据此降级(自驱力/自我概念回 0,
   与"初期回 0 后续由自检问卷填充"语义一致);嵌套对象形态仍按原逻辑解析。
Xiaogang Liao 1 tuần trước cách đây
mục cha
commit
8ee3dfc7c5

+ 34 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/WuxingSourcingAdviceVO.java

@@ -1,10 +1,17 @@
 package com.etotem.cfc.dto;
 
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 import java.util.List;
 
+/**
+ * 五行溯源建议 VO。
+ * 兼容 AI 返回的字段:生我者(upstream 系)、克我者(restrainer 系)与行动建议(action)。
+ * 未知字段忽略,避免 AI 输出新增字段导致整体解析失败。
+ */
 @Data
+@JsonIgnoreProperties(ignoreUnknown = true)
 public class WuxingSourcingAdviceVO {
 
     private String dimension;
@@ -30,4 +37,31 @@ public class WuxingSourcingAdviceVO {
     /** 是否使用降级静态建议 */
     @JsonProperty("fallbackUsed")
     private Boolean fallbackUsed;
+
+    // ===== AI 五行生克链字段(prompt 输出,VO 同步适配) =====
+
+    /** 生我者(上游)维度 */
+    private String upstreamDimension;
+    /** 生我者(上游)名称 */
+    private String upstreamName;
+    /** 生我者(上游)元素 */
+    private String upstreamElement;
+    /** 生我者(上游)颜色 */
+    private String upstreamColor;
+    /** 生我者(上游)理由 */
+    private String upstreamReason;
+
+    /** 克我者(制约)维度 */
+    private String restrainerDimension;
+    /** 克我者(制约)名称 */
+    private String restrainerName;
+    /** 克我者(制约)元素 */
+    private String restrainerElement;
+    /** 克我者(制约)颜色 */
+    private String restrainerColor;
+    /** 克我者(制约)理由 */
+    private String restrainerReason;
+
+    /** 具体行动建议 */
+    private String action;
 }

+ 6 - 1
cfc-backend/src/main/java/com/etotem/cfc/service/EmiReportService.java

@@ -167,7 +167,12 @@ public class EmiReportService {
 
     private static Map<String, Object> parseStructuredAnalysis(String json) {
         if (json == null || json.isEmpty()) return null;
-        try { return JSON.parseObject(json, Map.class); }
+        String trimmed = json.trim();
+        if (trimmed.startsWith("[")) {
+            // 平铺 items 数组(无 extra 嵌套):非异常,静默返回 null
+            return null;
+        }
+        try { return JSON.parseObject(trimmed, Map.class); }
         catch (Exception e) { return null; }
     }
 }

+ 13 - 2
cfc-backend/src/main/java/com/etotem/cfc/service/EnergyService.java

@@ -1324,12 +1324,23 @@ public class EnergyService {
         return null;
     }
 
-    /** 解析 structuredAnalysis JSON(使用 fastjson) */
+    /**
+     * 解析 structuredAnalysis JSON(使用 fastjson)。
+     * 注意:structured_analysis 实际存储的是 DAN 解析 items 平铺数组
+     * [{code,name,value,category},...](见 DanReportUploadService#confirmUpload)。
+     * 本方法只消费嵌套对象形态(extra.selfDriving/selfConcept 等),
+     * 数组形态无这些数据,静默返回 null 由调用方降级(自驱力/自我概念回 0)。
+     */
     @SuppressWarnings("unchecked")
     private Map<String, Object> parseStructuredAnalysis(String json) {
         if (json == null || json.isEmpty()) return null;
+        String trimmed = json.trim();
+        if (trimmed.startsWith("[")) {
+            // 平铺 items 数组(无 extra 嵌套):非异常,静默返回 null
+            return null;
+        }
         try {
-            return JSON.parseObject(json, Map.class);
+            return JSON.parseObject(trimmed, Map.class);
         } catch (Exception e) {
             log.warn("解析 structuredAnalysis 失败: {}", e.getMessage());
             return null;