Explorar o código

长文增加表现-review2
新增-days

luojunhui hai 1 día
pai
achega
983be25526

+ 3 - 0
core/src/main/java/com/tzld/videoVector/model/param/recall/BatchByTextParam.java

@@ -40,4 +40,7 @@ public class BatchByTextParam {
 
     /** 精排参数(从前端传入,覆盖后端默认值) */
     private RankingSpec ranking;
+
+    /** 指标数据日期维度(天),不传则使用 video.detail.metrics.days 配置 */
+    private Integer days;
 }

+ 3 - 0
core/src/main/java/com/tzld/videoVector/model/param/recall/MatchByTextParam.java

@@ -55,4 +55,7 @@ public class MatchByTextParam {
 
     /** 精排参数(从前端传入,覆盖后端默认值) */
     private RankingSpec ranking;
+
+    /** 指标数据日期维度(天),用于筛选 article_quality 的 dt 分区,不传则取最新分区 */
+    private Integer days;
 }

+ 1 - 0
core/src/main/java/com/tzld/videoVector/model/vo/recall/RecallSignalsVO.java

@@ -39,6 +39,7 @@ public class RecallSignalsVO {
 
         private Long totalRead;
         private Double avgRead;
+        private Double readMultiplier;
         private Double openRate;
         private Double fissionRate;
         private Integer publishCount;

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

@@ -292,7 +292,7 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
             articleItems = Collections.emptyList();
         }
 
-        List<VideoMatchEnrichedVO> videoItems = enrichVideoMatches(videoMatches, configCode);
+        List<VideoMatchEnrichedVO> videoItems = enrichVideoMatches(videoMatches, configCode, param.getDays());
 
         // WP3 召回前筛选:模态 + 来源(精排前先筛,减少无效候选进入 ranker)
         int beforeV = videoItems.size(), beforeM = materialItems.size(), beforeA = articleItems.size();
@@ -462,7 +462,7 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                 .limit(enrichK).collect(Collectors.toList());
 
         // enrich
-        List<VideoMatchEnrichedVO> videoItems = enrichVideoMatches(topVideo, configCodes.get(0));
+        List<VideoMatchEnrichedVO> videoItems = enrichVideoMatches(topVideo, configCodes.get(0), param.getDays());
         for (int i = 0; i < videoItems.size() && i < topVideo.size(); i++) {
             String cc = topVideo.get(i).getConfigCode();
             if (cc != null) videoItems.get(i).setConfigCode(cc);
@@ -802,7 +802,8 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
     /**
      * 视频召回结果 enrich
      */
-    private List<VideoMatchEnrichedVO> enrichVideoMatches(List<VideoMatchResult> matches, String requestConfigCode) {
+    private List<VideoMatchEnrichedVO> enrichVideoMatches(List<VideoMatchResult> matches, String requestConfigCode,
+                                                           Integer days) {
         if (CollectionUtils.isEmpty(matches)) {
             return Collections.emptyList();
         }
@@ -816,7 +817,7 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                 : videoApiService.getVideoDetail(videoIds);
 
         // 从 Redis 加载视频详情(含 ROV 等指标数据)用于精排打分
-        Map<Long, Map<String, Object>> redisDetails = loadRedisVideoDetails(matches);
+        Map<Long, Map<String, Object>> redisDetails = loadRedisVideoDetails(matches, days);
 
         List<VideoMatchEnrichedVO> items = new ArrayList<>(matches.size());
         for (VideoMatchResult m : matches) {
@@ -858,16 +859,18 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
      * 从 Redis 批量加载视频详情(key: video:detail:{metricsDays}d:{videoId})
      * 用于提取 ROV 等指标数据做精排打分。
      */
-    private Map<Long, Map<String, Object>> loadRedisVideoDetails(List<VideoMatchResult> matches) {
+    private Map<Long, Map<String, Object>> loadRedisVideoDetails(List<VideoMatchResult> matches, Integer days) {
         Map<Long, Map<String, Object>> result = new HashMap<>();
         if (CollectionUtils.isEmpty(matches)) return result;
 
+        int effectiveDays = days != null ? days : metricsDays;
+
         List<Long> orderedIds = new ArrayList<>();
         List<String> keys = new ArrayList<>();
         for (VideoMatchResult m : matches) {
             if (m != null && m.getVideoId() != null) {
                 orderedIds.add(m.getVideoId());
-                keys.add(VectorConstants.VIDEO_DETAIL_DAYS_KEY_PREFIX + metricsDays + "d:" + m.getVideoId());
+                keys.add(VectorConstants.VIDEO_DETAIL_DAYS_KEY_PREFIX + effectiveDays + "d:" + m.getVideoId());
             }
         }
         if (keys.isEmpty()) return result;
@@ -1143,6 +1146,9 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                 qs.setConfidence(aq.getConfidence());
                 qs.setTotalRead(aq.getTotalRead());
                 qs.setAvgRead(aq.getAvgRead());
+                if (aq.getAvgRead() != null && aq.getAvgRead() > 0) {
+                    qs.setReadMultiplier((double) aq.getTotalRead() / aq.getAvgRead());
+                }
                 qs.setOpenRate(aq.getOpenRate());
                 qs.setFissionRate(aq.getFissionRate());
                 qs.setPublishCount(aq.getPublishCount());
@@ -1876,7 +1882,7 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
         }
         String configCode = StringUtils.hasText(param.getConfigCode())
                 ? param.getConfigCode() : VectorConstants.DEFAULT_CONFIG_CODE;
-        List<VideoMatchEnrichedVO> videoItems = enrichVideoMatches(rawMatches, configCode);
+        List<VideoMatchEnrichedVO> videoItems = enrichVideoMatches(rawMatches, configCode, null);
         // displayK 截断
         if (videoItems.size() > displayK) {
             videoItems = limitEnrichedItemsByScore(videoItems, displayK);
@@ -1952,7 +1958,7 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                     if (!deduped.isEmpty()) {
                         List<VideoMatchResult> videoResults = toVideoMatchResults(deduped, cc);
                         populateVideoMatchResultDetails(videoResults);
-                        allResults.addAll(enrichVideoMatches(videoResults, cc));
+                        allResults.addAll(enrichVideoMatches(videoResults, cc, null));
                     }
                 } catch (Exception e) {
                     log.error("matchByMaterialId 视频搜索失败 configCode={}: {}", cc, e.getMessage(), e);
@@ -2345,7 +2351,7 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                     if (!deduped.isEmpty()) {
                         List<VideoMatchResult> videoResults = toVideoMatchResults(deduped, cc);
                         populateVideoMatchResultDetails(videoResults);
-                        allResults.addAll(enrichVideoMatches(videoResults, cc));
+                        allResults.addAll(enrichVideoMatches(videoResults, cc, null));
                     }
                 } catch (Exception e) {
                     log.error("matchByArticleId 视频搜索失败 configCode={}: {}", cc, e.getMessage(), e);

+ 8 - 2
core/src/main/java/com/tzld/videoVector/util/ArticleQualityCalculator.java

@@ -34,13 +34,17 @@ public class ArticleQualityCalculator {
         for (ArticleQuality aq : list) {
             DimValues dv = new DimValues();
             dv.totalRead = nullToZero(aq.getTotalRead());
+            double avgRead = nullToZero(aq.getAvgRead());
+            // 阅读均值倍数 = 总阅读 / 总阅读均值(avgRead = 单次发文平均阅读累加)
+            dv.readMultiplier = avgRead > 0 ? dv.totalRead / avgRead : 0;
             dv.openRate = nullToZero(aq.getOpenRate());
             dv.fissionRate = nullToZero(aq.getFissionRate());
             dv.publishCount = aq.getPublishCount() != null ? aq.getPublishCount() : 0;
             dimValuesList.add(dv);
         }
 
-        computePercentileRanks(dimValuesList, dv -> dv.totalRead, (dv, r) -> dv.readPct = r);
+        // readScore 基于阅读均值倍数(总阅读 / 总阅读均值),而非 totalRead
+        computePercentileRanks(dimValuesList, dv -> dv.readMultiplier, (dv, r) -> dv.readPct = r);
         computePercentileRanks(dimValuesList, dv -> dv.openRate, (dv, r) -> dv.openPct = r);
         computePercentileRanks(dimValuesList, dv -> dv.fissionRate, (dv, r) -> dv.fissionPct = r);
 
@@ -61,7 +65,8 @@ public class ArticleQualityCalculator {
                 lowConfCount++;
             }
 
-            double rawScore = (wRead * dv.readPct + wOpen * dv.openPct + wFission * dv.fissionPct) / qualTotalW;
+            double rawScore = (wRead * dv.readPct + wOpen * dv.openPct
+                    + wFission * dv.fissionPct) / qualTotalW;
             double qualityScore = confidence * rawScore + (1 - confidence) * DEFAULT_PRIOR;
 
             aq.setReadScore(round2(dv.readPct));
@@ -120,6 +125,7 @@ public class ArticleQualityCalculator {
 
     private static class DimValues {
         double totalRead;
+        double readMultiplier;
         double openRate;
         double fissionRate;
         int publishCount;