Преглед изворни кода

长文增加表现-review1

luojunhui пре 2 дана
родитељ
комит
df9c9565ed

+ 0 - 9
core/src/main/java/com/tzld/videoVector/dao/mapper/pgVector/ext/ArticleQualityMapperExt.java

@@ -5,18 +5,9 @@ import org.apache.ibatis.annotations.Param;
 
 
 import java.util.List;
 import java.util.List;
 
 
-/**
- * article_quality 自定义 Mapper
- */
 public interface ArticleQualityMapperExt {
 public interface ArticleQualityMapperExt {
 
 
-    /**
-     * 批量 upsert(ON CONFLICT DO UPDATE)
-     */
     int batchUpsert(@Param("list") List<ArticleQuality> list);
     int batchUpsert(@Param("list") List<ArticleQuality> list);
 
 
-    /**
-     * 按 contentId 批量查询质量分(取最新 dt)
-     */
     List<ArticleQuality> selectByContentIds(@Param("contentIds") List<String> contentIds);
     List<ArticleQuality> selectByContentIds(@Param("contentIds") List<String> contentIds);
 }
 }

+ 35 - 58
core/src/main/java/com/tzld/videoVector/job/ArticleQualitySyncJob.java

@@ -21,11 +21,6 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
-/**
- * 文章质量评分同步 Job
- * 从 ODPS loghubods.article_title_his_cache 拉取发布表现数据,
- * 解析 his_publish_article_list JSON,聚合计算综合质量分,写入 pgVector article_quality 表。
- */
 @Component
 @Component
 public class ArticleQualitySyncJob {
 public class ArticleQualitySyncJob {
 
 
@@ -37,18 +32,6 @@ public class ArticleQualitySyncJob {
     @Resource
     @Resource
     private ArticleQualityMapperExt articleQualityMapperExt;
     private ArticleQualityMapperExt articleQualityMapperExt;
 
 
-    /**
-     * 同步文章质量评分
-     *
-     * 可选参数(逗号分隔):
-     * wRead=0.4      — 阅读维度权重,默认 0.4
-     * wOpen=0.3      — 打开率维度权重,默认 0.3
-     * wFission=0.3   — 裂变率维度权重,默认 0.3
-     * confidenceThreshold=3 — 置信度发文次数阈值,默认 3
-     * dryRun=true    — 仅打印不写库
-     * dt=20260607    — ODPS 分区日期,默认昨天
-     * maxRows=10000  — 最多读取行数,默认不限(全量)
-     */
     @XxlJob("articleQualityJob")
     @XxlJob("articleQualityJob")
     public ReturnT<String> articleQualityJob(String param) {
     public ReturnT<String> articleQualityJob(String param) {
         log.info("===== articleQualityJob 开始, param: {} =====", param);
         log.info("===== articleQualityJob 开始, param: {} =====", param);
@@ -61,11 +44,20 @@ public class ArticleQualitySyncJob {
         String odpsDt = parseParamString(param, "dt", LocalDate.now().minusDays(1).format(DT_FMT));
         String odpsDt = parseParamString(param, "dt", LocalDate.now().minusDays(1).format(DT_FMT));
         int maxRows = (int) parseParamDouble(param, "maxRows", 0);
         int maxRows = (int) parseParamDouble(param, "maxRows", 0);
 
 
+        if (!odpsDt.matches("\\d{8}")) {
+            log.error("odpsDt 格式非法,期望 yyyyMMdd: {}", odpsDt);
+            return ReturnT.FAIL;
+        }
+        if (maxRows < 0) {
+            log.error("maxRows 不能为负数: {}", maxRows);
+            return ReturnT.FAIL;
+        }
+
         String dt = LocalDate.now().format(DT_FMT);
         String dt = LocalDate.now().format(DT_FMT);
         log.info("权重: r={} o={} f={}, 置信度阈值: {}, ODPS分区dt={}, 写入dt={}",
         log.info("权重: r={} o={} f={}, 置信度阈值: {}, ODPS分区dt={}, 写入dt={}",
                 wRead, wOpen, wFission, confidenceThreshold, odpsDt, dt);
                 wRead, wOpen, wFission, confidenceThreshold, odpsDt, dt);
 
 
-        // Step 0: 探针——确认分区有数据
+        // 确认分区有数据
         String probeSql = "SELECT COUNT(*) AS cnt FROM loghubods.article_title_his_cache WHERE dt = '" + odpsDt + "' AND type = '9'";
         String probeSql = "SELECT COUNT(*) AS cnt FROM loghubods.article_title_his_cache WHERE dt = '" + odpsDt + "' AND type = '9'";
         log.info("探针 SQL: {}", probeSql);
         log.info("探针 SQL: {}", probeSql);
         try {
         try {
@@ -83,7 +75,7 @@ public class ArticleQualitySyncJob {
             return ReturnT.FAIL;
             return ReturnT.FAIL;
         }
         }
 
 
-        // Step 1: 从 ODPS 一次流式读取(OdpsUtil 底层已是流式,不会 OOM)
+        // 从 ODPS 流式读取
         List<ArticleQuality> rawList = new ArrayList<>();
         List<ArticleQuality> rawList = new ArrayList<>();
         long[] totalRows = {0};
         long[] totalRows = {0};
         long[] parseFailCount = {0};
         long[] parseFailCount = {0};
@@ -112,12 +104,11 @@ public class ArticleQualitySyncJob {
                 if (contentId == null || contentId.isEmpty()) return;
                 if (contentId == null || contentId.isEmpty()) return;
                 if (hisPublishStr == null || hisPublishStr.isEmpty()) return;
                 if (hisPublishStr == null || hisPublishStr.isEmpty()) return;
 
 
-                // 采样前 3 条原始数据
                 if (totalRows[0] < 3) {
                 if (totalRows[0] < 3) {
                     String preview = hisPublishStr.length() > 500
                     String preview = hisPublishStr.length() > 500
                             ? hisPublishStr.substring(0, 500) + "..."
                             ? hisPublishStr.substring(0, 500) + "..."
                             : hisPublishStr;
                             : hisPublishStr;
-                    System.out.println("[采样" + totalRows[0] + "] contentId=" + contentId + ", json=" + preview);
+                    log.info("[采样{}] contentId={}, json={}", totalRows[0], contentId, preview);
                 }
                 }
 
 
                 ArticleQuality aq = aggregateFromHisPublishList(contentId, hisPublishStr);
                 ArticleQuality aq = aggregateFromHisPublishList(contentId, hisPublishStr);
@@ -125,14 +116,13 @@ public class ArticleQualitySyncJob {
                     synchronized (rawList) { rawList.add(aq); }
                     synchronized (rawList) { rawList.add(aq); }
                     validCount[0]++;
                     validCount[0]++;
                 } else {
                 } else {
-                    // 区分解析失败 vs 空数据
                     if (isJsonParseFail(hisPublishStr)) {
                     if (isJsonParseFail(hisPublishStr)) {
                         parseFailCount[0]++;
                         parseFailCount[0]++;
                         if (parseFailCount[0] <= 5) {
                         if (parseFailCount[0] <= 5) {
                             String preview = hisPublishStr.length() > 300
                             String preview = hisPublishStr.length() > 300
                                     ? hisPublishStr.substring(0, 300) + "..."
                                     ? hisPublishStr.substring(0, 300) + "..."
                                     : hisPublishStr;
                                     : hisPublishStr;
-                            System.out.println("[解析失败#" + parseFailCount[0] + "] contentId=" + contentId + ", json=" + preview);
+                            log.info("[解析失败#{}] contentId={}, json={}", parseFailCount[0], contentId, preview);
                         }
                         }
                     } else {
                     } else {
                         emptyDataCount[0]++;
                         emptyDataCount[0]++;
@@ -140,7 +130,7 @@ public class ArticleQualitySyncJob {
                 }
                 }
                 totalRows[0]++;
                 totalRows[0]++;
                 if (totalRows[0] % 10000 == 0) {
                 if (totalRows[0] % 10000 == 0) {
-                    System.out.println("[进度] " + totalRows[0] + " 行, 有效=" + validCount[0] + ", 解析失败=" + parseFailCount[0] + ", 无数据=" + emptyDataCount[0]);
+                    log.info("[进度] {} 行, 有效={}, 解析失败={}, 无数据={}", totalRows[0], validCount[0], parseFailCount[0], emptyDataCount[0]);
                 }
                 }
             });
             });
         } catch (Exception e) {
         } catch (Exception e) {
@@ -148,13 +138,13 @@ public class ArticleQualitySyncJob {
             return ReturnT.FAIL;
             return ReturnT.FAIL;
         }
         }
 
 
-        System.out.println("[完成] 总行数=" + totalRows[0] + ", 有效=" + validCount[0] + ", 解析失败=" + parseFailCount[0] + ", 无数据=" + emptyDataCount[0]);
+        log.info("[完成] 总行数={}, 有效={}, 解析失败={}, 无数据={}", totalRows[0], validCount[0], parseFailCount[0], emptyDataCount[0]);
         if (rawList.isEmpty()) {
         if (rawList.isEmpty()) {
             log.warn("无有效文章表现数据");
             log.warn("无有效文章表现数据");
             return ReturnT.FAIL;
             return ReturnT.FAIL;
         }
         }
 
 
-        // Step 2: 计算质量分
+        // 计算质量分
         ArticleQualityCalculator.calculateAll(rawList, wRead, wOpen, wFission, confidenceThreshold);
         ArticleQualityCalculator.calculateAll(rawList, wRead, wOpen, wFission, confidenceThreshold);
 
 
         if (dryRun) {
         if (dryRun) {
@@ -163,44 +153,38 @@ public class ArticleQualitySyncJob {
             return ReturnT.SUCCESS;
             return ReturnT.SUCCESS;
         }
         }
 
 
-        // Step 2.5: 按 contentId 去重(ODPS 同 contentId 可能多行)
+        // 按 contentId 去重(ODPS 同 contentId 可能多行)
         Map<String, ArticleQuality> deduped = new LinkedHashMap<>();
         Map<String, ArticleQuality> deduped = new LinkedHashMap<>();
         for (ArticleQuality aq : rawList) {
         for (ArticleQuality aq : rawList) {
             deduped.putIfAbsent(aq.getContentId(), aq);
             deduped.putIfAbsent(aq.getContentId(), aq);
         }
         }
         List<ArticleQuality> list = new ArrayList<>(deduped.values());
         List<ArticleQuality> list = new ArrayList<>(deduped.values());
-        System.out.println("[去重] " + rawList.size() + " → " + list.size() + " 条");
+        log.info("[去重] {} → {} 条", rawList.size(), list.size());
 
 
-        // Step 3: 分批写入
-        System.out.println("[写入DB] 开始, 共 " + list.size() + " 条, dt=" + dt);
-        // 采样第一条数据
+        // 分批写入
+        log.info("[写入DB] 开始, 共 {} 条, dt={}", list.size(), dt);
         if (!list.isEmpty()) {
         if (!list.isEmpty()) {
             ArticleQuality sample = list.get(0);
             ArticleQuality sample = list.get(0);
-            System.out.println("[写入DB 采样] contentId=" + sample.getContentId()
-                    + ", qualityScore=" + round2(sample.getQualityScore())
-                    + ", dt=" + dt);
+            log.info("[写入DB 采样] contentId={}, qualityScore={}, dt={}",
+                    sample.getContentId(), round2(sample.getQualityScore()), dt);
         }
         }
         int totalUpserted = 0;
         int totalUpserted = 0;
         for (int i = 0; i < list.size(); i += DB_BATCH_SIZE) {
         for (int i = 0; i < list.size(); i += DB_BATCH_SIZE) {
             int end = Math.min(i + DB_BATCH_SIZE, list.size());
             int end = Math.min(i + DB_BATCH_SIZE, list.size());
             List<ArticleQuality> batch = list.subList(i, end);
             List<ArticleQuality> batch = list.subList(i, end);
-            // 设置 dt
             for (ArticleQuality aq : batch) {
             for (ArticleQuality aq : batch) {
                 aq.setDt(dt);
                 aq.setDt(dt);
             }
             }
             int n = articleQualityMapperExt.batchUpsert(batch);
             int n = articleQualityMapperExt.batchUpsert(batch);
             totalUpserted += n;
             totalUpserted += n;
             if ((i / DB_BATCH_SIZE) % 50 == 0) {
             if ((i / DB_BATCH_SIZE) % 50 == 0) {
-                System.out.println("[写入DB 进度] " + end + "/" + rawList.size() + ", 本批" + n + "条, 累计" + totalUpserted + "条");
+                log.info("[写入DB 进度] {}/{}, 本批{}条, 累计{}条", end, list.size(), n, totalUpserted);
             }
             }
         }
         }
-        System.out.println("[写入DB 完成] upserted=" + totalUpserted);
+        log.info("[写入DB 完成] upserted={}", totalUpserted);
         return ReturnT.SUCCESS;
         return ReturnT.SUCCESS;
     }
     }
 
 
-    /**
-     * 解析 his_publish_article_list 并聚合为单条 ArticleQuality
-     */
     private ArticleQuality aggregateFromHisPublishList(String contentId, String hisPublishListJson) {
     private ArticleQuality aggregateFromHisPublishList(String contentId, String hisPublishListJson) {
         JSONArray publishList;
         JSONArray publishList;
         try {
         try {
@@ -232,14 +216,13 @@ public class ArticleQualitySyncJob {
                 totalAvgRead += avgView;
                 totalAvgRead += avgView;
             }
             }
 
 
-            // 取最新发文(itemIndex 最大)的粉丝量
+            // 取最新发文的粉丝量
             int itemIndex = pub.getIntValue("itemIndex");
             int itemIndex = pub.getIntValue("itemIndex");
             if (itemIndex > maxItemIndex) {
             if (itemIndex > maxItemIndex) {
                 maxItemIndex = itemIndex;
                 maxItemIndex = itemIndex;
                 totalFans = pub.getLongValue("fans");
                 totalFans = pub.getLongValue("fans");
             }
             }
 
 
-            // 首层和裂变
             JSONArray fissionList = pub.getJSONArray("articleDetailInfoList");
             JSONArray fissionList = pub.getJSONArray("articleDetailInfoList");
             if (fissionList != null) {
             if (fissionList != null) {
                 double pubFirstLevel = 0;
                 double pubFirstLevel = 0;
@@ -281,8 +264,6 @@ public class ArticleQualitySyncJob {
         }
         }
     }
     }
 
 
-    // ===== 辅助方法 =====
-
     private static double parseParamDouble(String param, String key, double defaultValue) {
     private static double parseParamDouble(String param, String key, double defaultValue) {
         if (param == null || param.isEmpty()) return defaultValue;
         if (param == null || param.isEmpty()) return defaultValue;
         for (String part : param.split(",")) {
         for (String part : param.split(",")) {
@@ -323,25 +304,21 @@ public class ArticleQualitySyncJob {
                 a.getQualityScore() == null ? 0 : a.getQualityScore()));
                 a.getQualityScore() == null ? 0 : a.getQualityScore()));
 
 
         int show = Math.min(5, sorted.size());
         int show = Math.min(5, sorted.size());
-        System.out.println("===== Top " + show + " 高质量文章 =====");
+        log.info("===== Top {} 高质量文章 =====", show);
         for (int i = 0; i < show; i++) {
         for (int i = 0; i < show; i++) {
             ArticleQuality aq = sorted.get(i);
             ArticleQuality aq = sorted.get(i);
-            System.out.println("[" + (i + 1) + "] contentId=" + aq.getContentId()
-                    + ", qualityScore=" + round2(aq.getQualityScore())
-                    + ", readScore=" + round2(aq.getReadScore())
-                    + ", openScore=" + round2(aq.getOpenScore())
-                    + ", fissionScore=" + round2(aq.getFissionScore())
-                    + ", conf=" + round2(aq.getConfidence()));
+            log.info("[{}] contentId={}, qualityScore={}, readScore={}, openScore={}, fissionScore={}, conf={}",
+                    i + 1, aq.getContentId(), round2(aq.getQualityScore()),
+                    round2(aq.getReadScore()), round2(aq.getOpenScore()),
+                    round2(aq.getFissionScore()), round2(aq.getConfidence()));
         }
         }
-        System.out.println("===== Bottom " + show + " 低质量文章 =====");
+        log.info("===== Bottom {} 低质量文章 =====", show);
         for (int i = sorted.size() - 1; i >= Math.max(0, sorted.size() - show); i--) {
         for (int i = sorted.size() - 1; i >= Math.max(0, sorted.size() - show); i--) {
             ArticleQuality aq = sorted.get(i);
             ArticleQuality aq = sorted.get(i);
-            System.out.println("[" + (sorted.size() - i) + "] contentId=" + aq.getContentId()
-                    + ", qualityScore=" + round2(aq.getQualityScore())
-                    + ", readScore=" + round2(aq.getReadScore())
-                    + ", openScore=" + round2(aq.getOpenScore())
-                    + ", fissionScore=" + round2(aq.getFissionScore())
-                    + ", conf=" + round2(aq.getConfidence()));
+            log.info("[{}] contentId={}, qualityScore={}, readScore={}, openScore={}, fissionScore={}, conf={}",
+                    sorted.size() - i, aq.getContentId(), round2(aq.getQualityScore()),
+                    round2(aq.getReadScore()), round2(aq.getOpenScore()),
+                    round2(aq.getFissionScore()), round2(aq.getConfidence()));
         }
         }
     }
     }
 
 

+ 9 - 0
core/src/main/java/com/tzld/videoVector/model/param/recall/RankingSpec.java

@@ -43,4 +43,13 @@ public class RankingSpec {
 
 
     /** 素材质量缺失策略:"group" | "shrink",默认 "group" */
     /** 素材质量缺失策略:"group" | "shrink",默认 "group" */
     private String materialMissingStrategy;
     private String materialMissingStrategy;
+
+    /** 文章质量子维度权重——阅读,默认 0.4 */
+    private Double wRead;
+
+    /** 文章质量子维度权重——打开率,默认 0.3 */
+    private Double wOpen;
+
+    /** 文章质量子维度权重——裂变率,默认 0.3 */
+    private Double wFission;
 }
 }

+ 3 - 40
core/src/main/java/com/tzld/videoVector/model/po/pgVector/ArticleQuality.java

@@ -1,17 +1,15 @@
 package com.tzld.videoVector.model.po.pgVector;
 package com.tzld.videoVector.model.po.pgVector;
 
 
+import lombok.Data;
+
 import java.util.Date;
 import java.util.Date;
 
 
-/**
- * 文章质量评分表(对应 pgVector 库 article_quality)
- * 数据来源:ODPS loghubods.article_title_his_cache.his_publish_article_list
- */
+@Data
 public class ArticleQuality {
 public class ArticleQuality {
 
 
     private Long id;
     private Long id;
     private String contentId;
     private String contentId;
 
 
-    // 原始聚合指标
     private Long totalRead;
     private Long totalRead;
     private Double avgRead;
     private Double avgRead;
     private Long totalFans;
     private Long totalFans;
@@ -19,49 +17,14 @@ public class ArticleQuality {
     private Double openRate;
     private Double openRate;
     private Double fissionRate;
     private Double fissionRate;
 
 
-    // 分维度得分(百分位归一化)
     private Double readScore;
     private Double readScore;
     private Double openScore;
     private Double openScore;
     private Double fissionScore;
     private Double fissionScore;
 
 
-    // 综合评分
     private Double qualityScore;
     private Double qualityScore;
     private Double confidence;
     private Double confidence;
 
 
     private String dt;
     private String dt;
     private Date createTime;
     private Date createTime;
     private Date updateTime;
     private Date updateTime;
-
-    public Long getId() { return id; }
-    public void setId(Long id) { this.id = id; }
-    public String getContentId() { return contentId; }
-    public void setContentId(String contentId) { this.contentId = contentId; }
-    public Long getTotalRead() { return totalRead; }
-    public void setTotalRead(Long totalRead) { this.totalRead = totalRead; }
-    public Double getAvgRead() { return avgRead; }
-    public void setAvgRead(Double avgRead) { this.avgRead = avgRead; }
-    public Long getTotalFans() { return totalFans; }
-    public void setTotalFans(Long totalFans) { this.totalFans = totalFans; }
-    public Integer getPublishCount() { return publishCount; }
-    public void setPublishCount(Integer publishCount) { this.publishCount = publishCount; }
-    public Double getOpenRate() { return openRate; }
-    public void setOpenRate(Double openRate) { this.openRate = openRate; }
-    public Double getFissionRate() { return fissionRate; }
-    public void setFissionRate(Double fissionRate) { this.fissionRate = fissionRate; }
-    public Double getReadScore() { return readScore; }
-    public void setReadScore(Double readScore) { this.readScore = readScore; }
-    public Double getOpenScore() { return openScore; }
-    public void setOpenScore(Double openScore) { this.openScore = openScore; }
-    public Double getFissionScore() { return fissionScore; }
-    public void setFissionScore(Double fissionScore) { this.fissionScore = fissionScore; }
-    public Double getQualityScore() { return qualityScore; }
-    public void setQualityScore(Double qualityScore) { this.qualityScore = qualityScore; }
-    public Double getConfidence() { return confidence; }
-    public void setConfidence(Double confidence) { this.confidence = confidence; }
-    public String getDt() { return dt; }
-    public void setDt(String dt) { this.dt = dt; }
-    public Date getCreateTime() { return createTime; }
-    public void setCreateTime(Date createTime) { this.createTime = createTime; }
-    public Date getUpdateTime() { return updateTime; }
-    public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
 }
 }

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

@@ -27,21 +27,21 @@ public class RecallSignalsVO {
     @Data
     @Data
     public static class QualitySignal {
     public static class QualitySignal {
         private boolean hasData;
         private boolean hasData;
-        private Double ctr;   // conversionEfficiencyScore(素材)
-        private Double viral; // viralScore(素材)
-        private Double roi;   // revenueScore(素材)
-
-        // 文章质量维度分(ARTICLE 模态专用)
-        private Double readScore;    // read_score
-        private Double openScore;    // open_score
-        private Double fissionScore; // fission_score
-
-        // 文章原始指标(ARTICLE 模态专用)
-        private Long totalRead;      // 总阅读
-        private Double avgRead;      // 阅读均值
-        private Double openRate;     // 总打开率
-        private Double fissionRate;  // 总裂变率
-        private Integer publishCount; // 发文次数
+        private Double ctr;
+        private Double viral;
+        private Double roi;
+
+        private Double readScore;
+        private Double openScore;
+        private Double fissionScore;
+
+        private Double confidence;
+
+        private Long totalRead;
+        private Double avgRead;
+        private Double openRate;
+        private Double fissionRate;
+        private Integer publishCount;
     }
     }
 
 
     @Data
     @Data

+ 6 - 1
core/src/main/java/com/tzld/videoVector/service/rank/RankServiceImpl.java

@@ -113,9 +113,11 @@ public class RankServiceImpl implements RankService {
                     && qs.getReadScore() != null && qs.getOpenScore() != null && qs.getFissionScore() != null) {
                     && qs.getReadScore() != null && qs.getOpenScore() != null && qs.getFissionScore() != null) {
                 double qualTotalW = params.getWRead() + params.getWOpen() + params.getWFission();
                 double qualTotalW = params.getWRead() + params.getWOpen() + params.getWFission();
                 if (qualTotalW <= 0) qualTotalW = 1;
                 if (qualTotalW <= 0) qualTotalW = 1;
-                double qualityScore = (params.getWRead() * qs.getReadScore()
+                double rawScore = (params.getWRead() * qs.getReadScore()
                         + params.getWOpen() * qs.getOpenScore()
                         + params.getWOpen() * qs.getOpenScore()
                         + params.getWFission() * qs.getFissionScore()) / qualTotalW;
                         + params.getWFission() * qs.getFissionScore()) / qualTotalW;
+                double confidence = qs.getConfidence() != null ? qs.getConfidence() : 1.0;
+                double qualityScore = confidence * rawScore + (1 - confidence) * 0.5;
                 double composite = params.getAlpha() * codeBoost * simNorm + (1 - params.getAlpha()) * qualityScore;
                 double composite = params.getAlpha() * codeBoost * simNorm + (1 - params.getAlpha()) * qualityScore;
                 return ScoreBreakdown.of(composite, simNorm, 0, codeBoost, lowerBound, passesThreshold);
                 return ScoreBreakdown.of(composite, simNorm, 0, codeBoost, lowerBound, passesThreshold);
             }
             }
@@ -195,6 +197,9 @@ public class RankServiceImpl implements RankService {
         adaptiveParams.setWCtr(baseParams.getWCtr());
         adaptiveParams.setWCtr(baseParams.getWCtr());
         adaptiveParams.setWViral(baseParams.getWViral());
         adaptiveParams.setWViral(baseParams.getWViral());
         adaptiveParams.setWRoi(baseParams.getWRoi());
         adaptiveParams.setWRoi(baseParams.getWRoi());
+        adaptiveParams.setWRead(baseParams.getWRead());
+        adaptiveParams.setWOpen(baseParams.getWOpen());
+        adaptiveParams.setWFission(baseParams.getWFission());
         adaptiveParams.setMaterialMissingStrategy(baseParams.getMaterialMissingStrategy());
         adaptiveParams.setMaterialMissingStrategy(baseParams.getMaterialMissingStrategy());
 
 
         // 逐条打分 + 回填 rankScore
         // 逐条打分 + 回填 rankScore

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

@@ -1140,6 +1140,7 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
                 qs.setReadScore(aq.getReadScore());
                 qs.setReadScore(aq.getReadScore());
                 qs.setOpenScore(aq.getOpenScore());
                 qs.setOpenScore(aq.getOpenScore());
                 qs.setFissionScore(aq.getFissionScore());
                 qs.setFissionScore(aq.getFissionScore());
+                qs.setConfidence(aq.getConfidence());
                 qs.setTotalRead(aq.getTotalRead());
                 qs.setTotalRead(aq.getTotalRead());
                 qs.setAvgRead(aq.getAvgRead());
                 qs.setAvgRead(aq.getAvgRead());
                 qs.setOpenRate(aq.getOpenRate());
                 qs.setOpenRate(aq.getOpenRate());
@@ -1705,6 +1706,9 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
         if (spec.getWViral() != null) p.setWViral(spec.getWViral());
         if (spec.getWViral() != null) p.setWViral(spec.getWViral());
         if (spec.getWRoi() != null) p.setWRoi(spec.getWRoi());
         if (spec.getWRoi() != null) p.setWRoi(spec.getWRoi());
         if (spec.getMaterialMissingStrategy() != null) p.setMaterialMissingStrategy(spec.getMaterialMissingStrategy());
         if (spec.getMaterialMissingStrategy() != null) p.setMaterialMissingStrategy(spec.getMaterialMissingStrategy());
+        if (spec.getWRead() != null) p.setWRead(spec.getWRead());
+        if (spec.getWOpen() != null) p.setWOpen(spec.getWOpen());
+        if (spec.getWFission() != null) p.setWFission(spec.getWFission());
         return p;
         return p;
     }
     }
 
 
@@ -2480,6 +2484,12 @@ public class VectorRecallTestServiceImpl implements VectorRecallTestService {
             qs.setReadScore(aq.getReadScore());
             qs.setReadScore(aq.getReadScore());
             qs.setOpenScore(aq.getOpenScore());
             qs.setOpenScore(aq.getOpenScore());
             qs.setFissionScore(aq.getFissionScore());
             qs.setFissionScore(aq.getFissionScore());
+            qs.setConfidence(aq.getConfidence());
+            qs.setTotalRead(aq.getTotalRead());
+            qs.setAvgRead(aq.getAvgRead());
+            qs.setOpenRate(aq.getOpenRate());
+            qs.setFissionRate(aq.getFissionRate());
+            qs.setPublishCount(aq.getPublishCount());
             vo.getSignals().setQuality(qs);
             vo.getSignals().setQuality(qs);
         }
         }
 
 

+ 1 - 18
core/src/main/java/com/tzld/videoVector/util/ArticleQualityCalculator.java

@@ -9,25 +9,12 @@ import java.util.Comparator;
 import java.util.List;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
-/**
- * 文章质量分计算工具
- * 基于文章发布表现数据(阅读、打开、裂变)计算综合质量分
- */
 public class ArticleQualityCalculator {
 public class ArticleQualityCalculator {
 
 
     private static final Logger log = LoggerFactory.getLogger(ArticleQualityCalculator.class);
     private static final Logger log = LoggerFactory.getLogger(ArticleQualityCalculator.class);
 
 
     private static final double DEFAULT_PRIOR = 0.5;
     private static final double DEFAULT_PRIOR = 0.5;
 
 
-    /**
-     * 批量计算质量分
-     *
-     * @param list                 原始聚合指标列表
-     * @param wRead                阅读维度权重
-     * @param wOpen                打开率维度权重
-     * @param wFission             裂变率维度权重
-     * @param confidenceThreshold  置信度发文次数阈值
-     */
     public static void calculateAll(List<ArticleQuality> list,
     public static void calculateAll(List<ArticleQuality> list,
                                     double wRead, double wOpen, double wFission,
                                     double wRead, double wOpen, double wFission,
                                     int confidenceThreshold) {
                                     int confidenceThreshold) {
@@ -42,7 +29,7 @@ public class ArticleQualityCalculator {
         log.info("开始计算文章质量分, 总数: {}, 权重: r={} o={} f={}, 置信度阈值: {}",
         log.info("开始计算文章质量分, 总数: {}, 权重: r={} o={} f={}, 置信度阈值: {}",
                 totalCount, wRead, wOpen, wFission, confidenceThreshold);
                 totalCount, wRead, wOpen, wFission, confidenceThreshold);
 
 
-        // Step 1: 提取原始维度值
+        // 提取原始维度值
         List<DimValues> dimValuesList = new ArrayList<>(totalCount);
         List<DimValues> dimValuesList = new ArrayList<>(totalCount);
         for (ArticleQuality aq : list) {
         for (ArticleQuality aq : list) {
             DimValues dv = new DimValues();
             DimValues dv = new DimValues();
@@ -53,12 +40,10 @@ public class ArticleQualityCalculator {
             dimValuesList.add(dv);
             dimValuesList.add(dv);
         }
         }
 
 
-        // Step 2: 各维度百分位排名
         computePercentileRanks(dimValuesList, dv -> dv.totalRead, (dv, r) -> dv.readPct = r);
         computePercentileRanks(dimValuesList, dv -> dv.totalRead, (dv, r) -> dv.readPct = r);
         computePercentileRanks(dimValuesList, dv -> dv.openRate, (dv, r) -> dv.openPct = r);
         computePercentileRanks(dimValuesList, dv -> dv.openRate, (dv, r) -> dv.openPct = r);
         computePercentileRanks(dimValuesList, dv -> dv.fissionRate, (dv, r) -> dv.fissionPct = r);
         computePercentileRanks(dimValuesList, dv -> dv.fissionRate, (dv, r) -> dv.fissionPct = r);
 
 
-        // Step 3: 加权计算综合分 + 置信度收缩
         int lowConfCount = 0;
         int lowConfCount = 0;
         int noDataCount = 0;
         int noDataCount = 0;
 
 
@@ -90,7 +75,6 @@ public class ArticleQualityCalculator {
                 totalCount, noDataCount, lowConfCount);
                 totalCount, noDataCount, lowConfCount);
     }
     }
 
 
-    // ===== 百分位排名计算(对标 MaterialQualityCalculator) =====
 
 
     @FunctionalInterface
     @FunctionalInterface
     private interface ValueExtractor {
     private interface ValueExtractor {
@@ -125,7 +109,6 @@ public class ArticleQualityCalculator {
         }
         }
     }
     }
 
 
-    // ===== 辅助方法 =====
 
 
     private static double nullToZero(Long v) { return v == null ? 0 : (double) v; }
     private static double nullToZero(Long v) { return v == null ? 0 : (double) v; }
     private static double nullToZero(Double v) { return v == null ? 0 : v; }
     private static double nullToZero(Double v) { return v == null ? 0 : v; }