Просмотр исходного кода

新增 getDeconstructResultMini 精简版解构接口

- 复用 getDeconstructResult 拉解构, 把原始 result 提取成 topic + highValuePoints 后返回
- 提取规则与 sync_decode_to_redis.py 一致, 阈值 0.8
- 灵感点全部纳入; 关键点仅 类型=='实质'; 目的点全部纳入; essences 为空的点丢弃
刘立冬 1 день назад
Родитель
Сommit
34718bcef1

+ 8 - 0
core/src/main/java/com/tzld/videoVector/service/VideoSearchService.java

@@ -26,6 +26,14 @@ public interface VideoSearchService {
      */
     JSONObject getDeconstructResult(GetDeconstructParam param);
 
+    /**
+     * 查询解构结果(精简版): result 替换为 topic + highValuePoints
+     * 提取规则与 sync_decode_to_redis.py 一致, 阈值 0.8
+     * @param param 查询参数
+     * @return 解构结果(成功时去掉 result, 顶层加 topic + highValuePoints)
+     */
+    JSONObject getDeconstructResultMini(GetDeconstructParam param);
+
     /**
      * 匹配TopN视频
      * @param param 匹配参数

+ 199 - 0
core/src/main/java/com/tzld/videoVector/service/impl/VideoSearchServiceImpl.java

@@ -1,5 +1,6 @@
 package com.tzld.videoVector.service.impl;
 
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.tzld.videoVector.api.VideoApiService;
 import com.tzld.videoVector.common.constant.VectorConstants;
@@ -222,6 +223,204 @@ public class VideoSearchServiceImpl implements VideoSearchService {
         return null;
     }
 
+    /**
+     * 精简版: 复用 getDeconstructResult, 把 result 字段(原始 JSON)提取成 topic + highValuePoints,
+     * 然后从返回里去掉 result 字段, 顶层放 topic + highValuePoints.
+     * 提取规则与 sync_decode_to_redis.py 一致, 实质阈值 0.8.
+     */
+    @Override
+    public JSONObject getDeconstructResultMini(GetDeconstructParam param) {
+        JSONObject full = getDeconstructResult(param);
+        if (full == null) {
+            return null;
+        }
+
+        // 默认顶层放空数组, 失败/未完成时也好让前端渲染
+        full.put("topic", "");
+        full.put("highValuePoints", new JSONArray());
+
+        Object resultObj = full.get("result");
+        if (resultObj instanceof JSONObject) {
+            try {
+                JSONObject extracted = parseRawDeconstruct((JSONObject) resultObj);
+                full.put("topic", extracted.getString("topic"));
+                full.put("highValuePoints", extracted.getJSONArray("highValuePoints"));
+            } catch (Exception e) {
+                log.error("getDeconstructResultMini parse failed, taskId={}, err={}",
+                        param.getTaskId(), e.getMessage(), e);
+            }
+        }
+        // 不论解析成功与否都丢掉原始 result, 控制返回体积
+        full.remove("result");
+        return full;
+    }
+
+    /**
+     * 解析外部解构 API 返回的原始 JSON, 抽取 topic + highValuePoints
+     * 规则:
+     *  - 灵感点: 全部纳入(分词都视为实质); 仅保留 contribution >= 0.8 的词
+     *  - 关键点: 仅当元数据 类型=='实质' 才纳入; 同样过滤 contribution >= 0.8
+     *  - 目的点: 全部纳入; 同样过滤 contribution >= 0.8
+     *  - 任一点 essences 为空 -> 不纳入
+     */
+    private JSONObject parseRawDeconstruct(JSONObject d) {
+        final double THRESHOLD = 0.8;
+
+        // ① 选题
+        String topic = "";
+        JSONObject finalTopic = d.getJSONObject("最终选题");
+        if (finalTopic != null) {
+            String t = finalTopic.getString("选题");
+            if (t != null) topic = t;
+        }
+
+        // ② 词 -> 贡献度
+        Map<String, Double> contribMap = new HashMap<>();
+        JSONArray contribArr = d.getJSONArray("contribution_results");
+        if (contribArr != null) {
+            for (int i = 0; i < contribArr.size(); i++) {
+                JSONObject row = contribArr.getJSONObject(i);
+                if (row == null) continue;
+                String word = row.getString("词");
+                if (word == null) continue;
+                Double score = row.getDouble("贡献度");
+                contribMap.put(word, score == null ? 0.0 : score);
+            }
+        }
+
+        // ③ 灵感点元数据 -> id
+        Map<String, String> inspirationIdMap = new HashMap<>();
+        JSONObject inspirationFinal = d.getJSONObject("inspiration_final_result");
+        if (inspirationFinal != null) {
+            JSONArray list = inspirationFinal.getJSONArray("最终灵感点列表");
+            if (list != null) {
+                for (int i = 0; i < list.size(); i++) {
+                    JSONObject row = list.getJSONObject(i);
+                    if (row == null) continue;
+                    String name = row.getString("灵感点");
+                    if (name != null) inspirationIdMap.put(name, row.getString("id"));
+                }
+            }
+        }
+
+        // ④ 关键点元数据 -> (类型, id)
+        Map<String, String[]> keypointMetaMap = new HashMap<>();
+        JSONObject keypointFinal = d.getJSONObject("keypoint_final");
+        if (keypointFinal != null) {
+            JSONArray list = keypointFinal.getJSONArray("最终关键点列表");
+            if (list != null) {
+                for (int i = 0; i < list.size(); i++) {
+                    JSONObject row = list.getJSONObject(i);
+                    if (row == null) continue;
+                    String name = row.getString("关键点");
+                    if (name != null) {
+                        keypointMetaMap.put(name,
+                                new String[]{row.getString("类型"), row.getString("关键点ID")});
+                    }
+                }
+            }
+        }
+
+        // ⑤ 目的点元数据 -> id
+        Map<String, String> purposeIdMap = new HashMap<>();
+        JSONObject purposeFinal = d.getJSONObject("purpose_final_result");
+        if (purposeFinal != null) {
+            JSONArray list = purposeFinal.getJSONArray("最终目的点列表");
+            if (list != null) {
+                for (int i = 0; i < list.size(); i++) {
+                    JSONObject row = list.getJSONObject(i);
+                    if (row == null) continue;
+                    String name = row.getString("目的点");
+                    if (name != null) purposeIdMap.put(name, row.getString("id"));
+                }
+            }
+        }
+
+        JSONArray highValue = new JSONArray();
+
+        // ⑥ 灵感点
+        JSONArray inspirations = d.getJSONArray("灵感点");
+        if (inspirations != null) {
+            for (int i = 0; i < inspirations.size(); i++) {
+                JSONObject ip = inspirations.getJSONObject(i);
+                if (ip == null) continue;
+                String name = ip.getString("点");
+                if (name == null || name.isEmpty()) continue;
+                JSONArray essences = collectEssences(ip.getJSONArray("分词结果"), contribMap, THRESHOLD);
+                if (essences.isEmpty()) continue;
+                JSONObject point = new JSONObject();
+                point.put("id", inspirationIdMap.getOrDefault(name, ""));
+                point.put("type", "灵感点");
+                point.put("name", name);
+                point.put("essences", essences);
+                highValue.add(point);
+            }
+        }
+
+        // ⑦ 关键点 (仅 类型=='实质')
+        JSONArray keypoints = d.getJSONArray("关键点");
+        if (keypoints != null) {
+            for (int i = 0; i < keypoints.size(); i++) {
+                JSONObject kp = keypoints.getJSONObject(i);
+                if (kp == null) continue;
+                String name = kp.getString("点");
+                if (name == null || name.isEmpty()) continue;
+                String[] meta = keypointMetaMap.get(name);
+                if (meta == null || !"实质".equals(meta[0])) continue;
+                JSONArray essences = collectEssences(kp.getJSONArray("分词结果"), contribMap, THRESHOLD);
+                if (essences.isEmpty()) continue;
+                JSONObject point = new JSONObject();
+                point.put("id", meta[1] == null ? "" : meta[1]);
+                point.put("type", "关键点");
+                point.put("name", name);
+                point.put("essences", essences);
+                highValue.add(point);
+            }
+        }
+
+        // ⑧ 目的点
+        JSONArray purposes = d.getJSONArray("目的点");
+        if (purposes != null) {
+            for (int i = 0; i < purposes.size(); i++) {
+                JSONObject pp = purposes.getJSONObject(i);
+                if (pp == null) continue;
+                String name = pp.getString("点");
+                if (name == null || name.isEmpty()) continue;
+                JSONArray essences = collectEssences(pp.getJSONArray("分词结果"), contribMap, THRESHOLD);
+                if (essences.isEmpty()) continue;
+                JSONObject point = new JSONObject();
+                point.put("id", purposeIdMap.getOrDefault(name, ""));
+                point.put("type", "目的点");
+                point.put("name", name);
+                point.put("essences", essences);
+                highValue.add(point);
+            }
+        }
+
+        JSONObject out = new JSONObject();
+        out.put("topic", topic);
+        out.put("highValuePoints", highValue);
+        return out;
+    }
+
+    private JSONArray collectEssences(JSONArray words, Map<String, Double> contribMap, double threshold) {
+        JSONArray essences = new JSONArray();
+        if (words == null) return essences;
+        for (int i = 0; i < words.size(); i++) {
+            JSONObject w = words.getJSONObject(i);
+            if (w == null) continue;
+            String word = w.getString("词");
+            if (word == null) continue;
+            Double score = contribMap.get(word);
+            if (score == null || score < threshold) continue;
+            JSONObject e = new JSONObject();
+            e.put("word", word);
+            e.put("score", score);
+            essences.add(e);
+        }
+        return essences;
+    }
+
     /**
      * 根据taskId查询解构内容
      */

+ 5 - 0
server/src/main/java/com/tzld/videoVector/controller/VideoSearchController.java

@@ -35,6 +35,11 @@ public class VideoSearchController {
         return CommonResponse.success(videoSearchService.getDeconstructResultByChannelContentId(channelContentId));
     }
 
+    @PostMapping("/getDeconstructResultMini")
+    public CommonResponse<JSONObject> getDeconstructResultMini(@RequestBody GetDeconstructParam param) {
+        return CommonResponse.success(videoSearchService.getDeconstructResultMini(param));
+    }
+
     @PostMapping("/matchTopNVideo")
     public CommonResponse<List<VideoMatchResult>> matchTopNVideo(@RequestBody MatchTopNVideoParam param) {
         return CommonResponse.success(videoSearchService.matchTopNVideo(param));