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

召回结果嵌入视频解构(videoDetail.deconstruct)

刘立冬 13 часов назад
Родитель
Сommit
4c1898254b

+ 3 - 0
core/src/main/java/com/tzld/videoVector/common/constant/VectorConstants.java

@@ -24,6 +24,9 @@ public interface VectorConstants {
     /** 视频基础信息 Redis 过期时间:2天(秒) */
     long VIDEO_DETAIL_EXPIRE_SECONDS = 2L * 24 * 60 * 60;
 
+    /** 视频解构 Redis Key 前缀,格式: recall:vid_decode:{videoId},由 script/sync_decode_to_redis.py 写入 */
+    String VID_DECODE_KEY_PREFIX = "recall:vid_decode:";
+
     // ========================== 批处理参数 ==========================
 
     /** 每页查询数量 */

+ 5 - 1
core/src/main/java/com/tzld/videoVector/model/vo/VideoMatchResult.java

@@ -20,7 +20,11 @@ public class VideoMatchResult {
     /** 余弦相似度分值 */
     private Double score;
 
-    /** 视频基础信息(从大数据表同步至Redis,字段待确定后补充) */
+    /**
+     * 视频基础信息(从大数据表同步至Redis)
+     * 内嵌 deconstruct 子对象 = { topic, 灵感点, 灵感点-实质, 关键点, 关键点-实质, 目的点, 目的点-实质 }
+     * 来源 Redis recall:vid_decode:{vid}, 由 enrichDeconstruct 填入
+     */
     private Map<String, Object> videoDetail;
 
     public VideoMatchResult() {

+ 5 - 1
core/src/main/java/com/tzld/videoVector/model/vo/recall/VideoMatchEnrichedVO.java

@@ -57,6 +57,10 @@ public class VideoMatchEnrichedVO {
     /** ROV,占位 "--" */
     private String rov;
 
-    /** 视频详情 */
+    /**
+     * 视频详情(扁平 KV)
+     * 内嵌 deconstruct 子对象 = { topic, 灵感点, 灵感点-实质, 关键点, 关键点-实质, 目的点, 目的点-实质 }
+     * 来源 Redis recall:vid_decode:{vid}
+     */
     private Map<String, Object> videoDetail;
 }

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

@@ -479,6 +479,8 @@ public class VideoSearchServiceImpl implements VideoSearchService {
 
         // 从 Redis 获取视频基础信息并填充到结果中
         enrichVideoDetail(result);
+        // 从 Redis 获取视频解构(选题 + 高价值实质点)并填充
+        enrichDeconstruct(result);
 
         return result;
     }
@@ -501,6 +503,8 @@ public class VideoSearchServiceImpl implements VideoSearchService {
         }
         // 填充视频详情
         enrichVideoDetail(result);
+        // 填充视频解构
+        enrichDeconstruct(result);
         return result;
     }
 
@@ -814,4 +818,116 @@ public class VideoSearchServiceImpl implements VideoSearchService {
             log.error("批量获取视频详情失败: {}", e.getMessage(), e);
         }
     }
+
+    /**
+     * 从 Redis 批量获取视频解构,以扁平 key 形式塞进 videoDetail.deconstruct 子对象
+     *
+     * 输出结构 (videoDetail.deconstruct):
+     *   {
+     *     "topic": "...",
+     *     "灵感点": ["name1", ...],
+     *     "灵感点-实质": [{"word":"...","score":0.9}, ...],
+     *     "关键点": [...], "关键点-实质": [...],
+     *     "目的点": [...], "目的点-实质": [...]
+     *   }
+     *
+     * 数据来源:script/sync_decode_to_redis.py 同步,Key: recall:vid_decode:{videoId}
+     */
+    private void enrichDeconstruct(List<VideoMatchResult> results) {
+        if (results == null || results.isEmpty()) {
+            return;
+        }
+
+        try {
+            List<String> keys = results.stream()
+                    .map(r -> VectorConstants.VID_DECODE_KEY_PREFIX + r.getVideoId())
+                    .collect(Collectors.toList());
+
+            List<String> values = redisUtils.mGet(keys);
+
+            for (int i = 0; i < results.size(); i++) {
+                String json = values.get(i);
+                if (json == null) {
+                    continue;
+                }
+                try {
+                    Map<String, Object> deconstructFlat = buildFlatDeconstruct(json);
+                    if (deconstructFlat == null || deconstructFlat.isEmpty()) {
+                        continue;
+                    }
+                    VideoMatchResult r = results.get(i);
+                    Map<String, Object> detail = r.getVideoDetail();
+                    if (detail == null) {
+                        detail = new HashMap<>();
+                        r.setVideoDetail(detail);
+                    }
+                    detail.put("deconstruct", deconstructFlat);
+                } catch (Exception e) {
+                    log.error("解析视频解构失败,videoId={}: {}", results.get(i).getVideoId(), e.getMessage());
+                }
+            }
+        } catch (Exception e) {
+            log.error("批量获取视频解构失败: {}", e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 把 Redis 原始解构 JSON 转成扁平结构 (topic + 3 组 类型/类型-实质 key)
+     */
+    private Map<String, Object> buildFlatDeconstruct(String json) {
+        JSONObject src = JSONObject.parseObject(json);
+        if (src == null) {
+            return null;
+        }
+        Map<String, Object> out = new LinkedHashMap<>();
+        String topic = src.getString("topic");
+        if (topic != null) {
+            out.put("topic", topic);
+        }
+
+        // 按 type 分桶: name 列表 + essences flatten
+        Map<String, List<String>> nameByType = new LinkedHashMap<>();
+        Map<String, List<Map<String, Object>>> essenceByType = new LinkedHashMap<>();
+        for (String t : new String[]{"灵感点", "关键点", "目的点"}) {
+            nameByType.put(t, new ArrayList<>());
+            essenceByType.put(t, new ArrayList<>());
+        }
+
+        com.alibaba.fastjson.JSONArray points = src.getJSONArray("highValuePoints");
+        if (points != null) {
+            for (int j = 0; j < points.size(); j++) {
+                JSONObject p = points.getJSONObject(j);
+                if (p == null) {
+                    continue;
+                }
+                String type = p.getString("type");
+                if (type == null || !nameByType.containsKey(type)) {
+                    continue;
+                }
+                String name = p.getString("name");
+                if (name != null && !name.isEmpty()) {
+                    nameByType.get(type).add(name);
+                }
+                com.alibaba.fastjson.JSONArray essences = p.getJSONArray("essences");
+                if (essences != null) {
+                    for (int k = 0; k < essences.size(); k++) {
+                        JSONObject e = essences.getJSONObject(k);
+                        if (e == null) {
+                            continue;
+                        }
+                        Map<String, Object> item = new LinkedHashMap<>();
+                        item.put("word", e.getString("word"));
+                        item.put("score", e.getDouble("score"));
+                        essenceByType.get(type).add(item);
+                    }
+                }
+            }
+        }
+
+        for (String t : new String[]{"灵感点", "关键点", "目的点"}) {
+            out.put(t, nameByType.get(t));
+            out.put(t + "-实质", essenceByType.get(t));
+        }
+        return out;
+    }
 }

+ 1 - 1
core/src/main/java/com/tzld/videoVector/service/recall/impl/VectorRecallTestServiceImpl.java

@@ -317,7 +317,7 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                     break;
             }
 
-            // 填充 videoDetail
+            // 填充 videoDetail (已由 VideoSearchServiceImpl 嵌入 deconstruct 子对象)
             vo.setVideoDetail(m.videoDetail);
 
             items.add(vo);