Browse Source

增加账号相关性得分 rankv4 rankv7修改

wangyunpeng 10 months ago
parent
commit
59175a923f
13 changed files with 396 additions and 56 deletions
  1. 1 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/Content.java
  2. 1 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/ContentHisPublishArticle.java
  3. 13 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/crawler/AccountCorrelationRepository.java
  4. 61 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/entity/crawler/AccountCorrelation.java
  5. 3 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/mapper/crawler/CrawlerBaseMapper.java
  6. 38 39
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV4Strategy.java
  7. 1 1
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV7Strategy.java
  8. 15 2
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recall/RecallService.java
  9. 2 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/ScoreService.java
  10. 40 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/strategy/HisFissionAvgReadRateCorrelationRateStrategy.java
  11. 148 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/strategy/ViewCountRateCorrelationStrategy.java
  12. 8 0
      long-article-recommend-service/src/main/resources/mapper/crawler/CrawlerBaseMapper.xml
  13. 65 14
      long-article-recommend-service/src/test/java/com/tzld/longarticle/recommend/server/RecommendTest.java

+ 1 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/Content.java

@@ -36,6 +36,7 @@ public class Content {
     private List<ContentHisPublishArticle> hisPublishArticleList;
     private Double t0FissionByFansMean;
     private Double t0FissionByReadAvgMean;
+    private Double t0FissionByReadAvgCorrelationMean;
     private Double t0FissionByFansSumAvg;
     private Double t0FissionByReadAvgSumAvg;
 

+ 1 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/ContentHisPublishArticle.java

@@ -29,4 +29,5 @@ public class ContentHisPublishArticle {
     private Integer t0FissionSum;
     private Double t0FissionByFans;
     private Double t0FissionByReadAvg;
+    private Double correlation;
 }

+ 13 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/crawler/AccountCorrelationRepository.java

@@ -0,0 +1,13 @@
+package com.tzld.longarticle.recommend.server.repository.crawler;
+
+import com.tzld.longarticle.recommend.server.repository.entity.crawler.AccountCorrelation;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface AccountCorrelationRepository extends JpaRepository<AccountCorrelation, AccountCorrelation.PK> {
+
+    List<AccountCorrelation> findByGhIdAndStatus(String ghId, Integer status);
+}

+ 61 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/entity/crawler/AccountCorrelation.java

@@ -0,0 +1,61 @@
+package com.tzld.longarticle.recommend.server.repository.entity.crawler;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.Table;
+import javax.persistence.Column;
+import java.io.Serializable;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "account_correlation")
+@IdClass(AccountCorrelation.PK.class)
+public class AccountCorrelation {
+
+    @Id
+    @Column(name = "date_str")
+    private String dateStr;
+
+    @Id
+    @Column(name = "gh_id")
+    private String ghId;
+
+    @Id
+    @Column(name = "rel_gh_id")
+    private String relGhId;
+
+    @Column(name = "account_name")
+    private String accountName;
+
+    @Column(name = "rel_account_name")
+    private String relAccountName;
+
+    @Column(name = "status")
+    private Integer status;
+
+    @Column(name = "correlation")
+    private Double correlation;
+
+    @Data
+    public static class PK implements Serializable {
+
+        @Column(name = "date_str")
+        private String dateStr;
+        @Column(name = "gh_id")
+        private String ghId;
+        @Column(name = "rel_gh_id")
+        private String relGhId;
+
+        public PK() {
+        }
+
+    }
+}
+

+ 3 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/mapper/crawler/CrawlerBaseMapper.java

@@ -1,5 +1,6 @@
 package com.tzld.longarticle.recommend.server.repository.mapper.crawler;
 
+import com.tzld.longarticle.recommend.server.repository.entity.crawler.AccountCorrelation;
 import com.tzld.longarticle.recommend.server.repository.entity.crawler.DatastatSortStrategy;
 
 import java.util.List;
@@ -10,4 +11,6 @@ public interface CrawlerBaseMapper {
 
     void batchInsertDatastatSortStrategy(List<DatastatSortStrategy> list);
 
+    void batchInsertAccountCorrelation(List<AccountCorrelation> list);
+
 }

+ 38 - 39
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV4Strategy.java

@@ -8,16 +8,16 @@ import com.tzld.longarticle.recommend.server.service.rank.RankItem;
 import com.tzld.longarticle.recommend.server.service.rank.RankParam;
 import com.tzld.longarticle.recommend.server.service.rank.RankResult;
 import com.tzld.longarticle.recommend.server.service.rank.RankStrategy;
+import com.tzld.longarticle.recommend.server.service.score.AccountIndexReplacePoolConfig;
 import com.tzld.longarticle.recommend.server.service.score.ScoreParam;
 import com.tzld.longarticle.recommend.server.service.score.ScoreResult;
 import com.tzld.longarticle.recommend.server.service.score.ScoreService;
 import com.tzld.longarticle.recommend.server.service.score.strategy.*;
 import com.tzld.longarticle.recommend.server.util.CommonCollectionUtils;
-import com.tzld.longarticle.recommend.server.util.JSONUtils;
 import com.tzld.longarticle.recommend.server.util.TitleSimilarCheckUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.RandomUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -39,27 +39,30 @@ public class RankV4Strategy implements RankStrategy {
 
         //log.info("RankParam {}", JSONUtils.toJson(param));
         ScoreResult scoreResult = scoreService.score(convertToScoreParam(param));
-        //log.info("ScoreResult {}", JSONUtils.toJson(scoreResult));
 
         Map<String, Map<String, Double>> scoreMap = scoreResult.getScoreMap();
+        String[] contentPools = accountContentPoolConfigService.getContentPools(param.getAccountName());
+        Map<Integer, AccountIndexReplacePoolConfig> indexReplacePoolConfigMap = accountContentPoolConfigService.getContentReplacePools(param.getAccountName());
 
         List<RankItem> items = CommonCollectionUtils.toList(param.getContents(), c -> {
             RankItem item = new RankItem();
             item.setContent(c);
             c.setScoreMap(scoreMap.get(c.getId()));
             item.setScoreMap(scoreMap.get(c.getId()));
-            String[] contentPools = accountContentPoolConfigService.getContentPools(param.getAccountName());
             double score;
             if (contentPools[0].equals(item.getContent().getContentPoolType())
                     || contentPools[1].equals(item.getContent().getContentPoolType())) {
                 score = item.getScore(SimilarityStrategy.class.getSimpleName())
                         + item.getScore(CategoryStrategy.class.getSimpleName())
-                        + item.getScore(ViewCountRateStrategy.class.getSimpleName())
                         + item.getScore(FlowCtlDecreaseStrategy.class.getSimpleName());
+                if (item.getScore(PublishTimesStrategy.class.getSimpleName()) >= 0) {
+                    score += item.getScore(ViewCountRateCorrelationStrategy.class.getSimpleName());
+                }
             } else {
                 score = item.getScore(SimilarityStrategy.class.getSimpleName())
                         + item.getScore(CategoryStrategy.class.getSimpleName())
                         + item.getScore(AccountPreDistributeStrategy.class.getSimpleName())
+                        + item.getScore(PublishTimesStrategy.class.getSimpleName())
                         + item.getScore(FlowCtlDecreaseStrategy.class.getSimpleName());
             }
             c.setScore(score);
@@ -69,11 +72,9 @@ public class RankV4Strategy implements RankStrategy {
 
         // 1 排序
         Collections.sort(items, (o1, o2) -> -Double.compare(o1.getScore(), o2.getScore()));
-        log.info("SortResult {}", JSONUtils.toJson(items));
         // 2 相似去重
         List<Content> contents = CommonCollectionUtils.toList(items, RankItem::getContent);
 //        contents = deduplication(contents);
-        log.info("Deduplication {}", JSONUtils.toJson(contents));
 
         // 3 文章按照内容池分组
         Map<String, List<Content>> contentMap = new HashMap<>();
@@ -81,52 +82,49 @@ public class RankV4Strategy implements RankStrategy {
             List<Content> data = contentMap.computeIfAbsent(c.getContentPoolType(), k -> new ArrayList<>());
             data.add(c);
         }
-        log.info("ContentMap {}", JSONUtils.toJson(contentMap));
         // 4 选文章
         List<Content> result = new ArrayList<>();
-        String[] contentPools = accountContentPoolConfigService.getContentPools(param.getAccountName());
         String[] publishPool = Arrays.copyOf(contentPools, contentPools.length);
 
-        // 头、次
-        if (StringUtils.equals(contentPools[0], contentPools[1])) {
-            List<Content> pool = contentMap.get(contentPools[0]);
-            Integer level = accountContentPoolConfigService.getLevelByContentPool(contentPools[0]);
-            if (level == 1) {
-                if (CollectionUtils.isNotEmpty(pool)) {
-                    result.add(pool.get(0));
-                    if (pool.size() > 2) {
-                        result.add(pool.get(2));
-                    }
-                } else {
-                    return new RankResult(result);
-                }
-            } else if (level == 2) {
-                if (CollectionUtils.isNotEmpty(pool)) {
-                    result.add(pool.get(0));
-                    if (pool.size() > 1) {
-                        result.add(pool.get(1));
+        // 头
+        List<Content> pool1 = contentMap.get(contentPools[0]);
+        if (CollectionUtils.isNotEmpty(pool1)) {
+            result.add(pool1.get(0));
+        } else {
+            return new RankResult(result);
+        }
+        // 次
+        List<Content> pool2 = contentMap.get(contentPools[1]);
+        if (CollectionUtils.isNotEmpty(pool2)) {
+            int i = RandomUtils.nextInt(0, Math.min(pool2.size(), 5));
+            int j = RandomUtils.nextInt(0, Math.min(pool2.size(), 5));
+            result.add(pool2.get(i));
+            // 替补 头条内容不足使用次条内容
+            if (result.size() == 1 && pool2.size() > 1) {
+                while (i == j && pool2.size() > 1) {
+                    j = RandomUtils.nextInt(0, Math.min(pool2.size(), 5));
+                    if (i != j) {
+                        publishPool[0] = contentPools[1];
+                        result.add(pool2.get(1));
+                        break;
                     }
-                } else {
-                    return new RankResult(result);
                 }
             }
         } else {
-            // 配置错误 兜底
-            List<Content> pool1 = contentMap.get(contentPools[0]);
-            List<Content> pool2 = contentMap.get(contentPools[1]);
-            if (CollectionUtils.isNotEmpty(pool1)) {
-                result.add(pool1.get(0));
-                if (CollectionUtils.isNotEmpty(pool2)) {
-                    result.add(pool2.get(0));
+            // 替补 根据设置替补内容池查找内容尽心替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(2);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool2Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool2Replace)) {
+                    publishPool[1] = replacePoolConfig.getContentPool();
+                    result.add(pool2Replace.get(0));
                 }
-            } else {
-                return new RankResult(result);
             }
         }
 
         // 3-8
         List<Content> pool = contentMap.get(contentPools[2]);
-        if (CollectionUtils.isNotEmpty(pool)) {
+        if (CollectionUtils.isNotEmpty(pool) && param.getSize() > result.size()) {
             result.addAll(pool.subList(0, Math.min(pool.size(), param.getSize() - result.size())));
         }
 
@@ -141,6 +139,7 @@ public class RankV4Strategy implements RankStrategy {
         scoreParam.setAccountName(param.getAccountName());
         scoreParam.setContents(param.getContents());
         scoreParam.setStrategy(param.getStrategy());
+        scoreParam.setScene(param.getScene());
         return scoreParam;
     }
 

+ 1 - 1
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV7Strategy.java

@@ -51,7 +51,7 @@ public class RankV7Strategy implements RankStrategy {
             item.setScoreMap(scoreMap.get(c.getId()));
             double score;
             if (contentPools[0].equals(item.getContent().getContentPoolType())) {
-                score = item.getScore(HisFissionFansRateRateStrategy.class.getSimpleName());
+                score = item.getScore(HisFissionAvgReadRateCorrelationRateStrategy.class.getSimpleName());
             } else if (contentPools[1].equals(item.getContent().getContentPoolType())) {
                 score = item.getScore(SimilarityStrategy.class.getSimpleName())
                         + item.getScore(CategoryStrategy.class.getSimpleName())

+ 15 - 2
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recall/RecallService.java

@@ -7,10 +7,12 @@ import com.tzld.longarticle.recommend.server.model.ContentHisPublishArticle;
 import com.tzld.longarticle.recommend.server.remote.AIGCRemoteService;
 import com.tzld.longarticle.recommend.server.repository.aigc.CrawlerMetaArticleRepository;
 import com.tzld.longarticle.recommend.server.repository.crawler.AccountAvgInfoRepository;
+import com.tzld.longarticle.recommend.server.repository.crawler.AccountCorrelationRepository;
 import com.tzld.longarticle.recommend.server.repository.crawler.ArticleDetailInfoRepository;
 import com.tzld.longarticle.recommend.server.repository.crawler.ArticleRepository;
 import com.tzld.longarticle.recommend.server.repository.entity.aigc.CrawlerMetaArticle;
 import com.tzld.longarticle.recommend.server.repository.entity.crawler.AccountAvgInfo;
+import com.tzld.longarticle.recommend.server.repository.entity.crawler.AccountCorrelation;
 import com.tzld.longarticle.recommend.server.repository.entity.crawler.Article;
 import com.tzld.longarticle.recommend.server.repository.entity.crawler.ArticleDetailInfo;
 import com.tzld.longarticle.recommend.server.repository.mapper.crawler.CrawlerBaseMapper;
@@ -58,6 +60,8 @@ public class RecallService implements ApplicationContextAware {
     AccountIndexAvgViewCountService accountIndexAvgViewCountService;
     @Autowired
     CrawlerBaseMapper crawlerBaseMapper;
+    @Autowired
+    AccountCorrelationRepository accountCorrelationRepository;
 
     private final Map<String, RecallStrategy> strategyMap = new HashMap<>();
     private ApplicationContext applicationContext;
@@ -129,7 +133,7 @@ public class RecallService implements ApplicationContextAware {
             return content;
         }
         // 标题历史均值
-        setTitleAvgViewCount(content);
+        setTitleAvgViewCount(content, param.getGhId());
         // category 查询
         setContentCategory(content);
         return content;
@@ -189,8 +193,12 @@ public class RecallService implements ApplicationContextAware {
         }
     }
 
-    public void setTitleAvgViewCount(List<Content> contentList) {
+    public void setTitleAvgViewCount(List<Content> contentList, String ghId) {
         long start = System.currentTimeMillis();
+        // 获取账号相关性
+        List<AccountCorrelation> accountCorrelationList = accountCorrelationRepository.findByGhIdAndStatus(ghId, 1);
+        Map<String, Double> accountCorrelationMap = accountCorrelationList.stream().collect(
+                Collectors.toMap(AccountCorrelation::getRelGhId, AccountCorrelation::getCorrelation));
 
         Set<String> titleList = contentList.stream().map(Content::getTitle).collect(Collectors.toSet());
 //        Set<String> crawlerTitleList = contentList.stream().map(Content::getCrawlerTitle).collect(Collectors.toSet());
@@ -290,6 +298,7 @@ public class RecallService implements ApplicationContextAware {
                         article.setFirstViewCountRate((firstArticle.getShowViewCount() * 1.0) / firstIndexAvgInfo.getReadAvg());
                     }
                 }
+                article.setCorrelation(Optional.ofNullable(accountCorrelationMap.get(article.getGhId())).orElse(0.0));
                 content.getHisPublishArticleList().add(article);
             }
             // 设置头条阅读均值
@@ -308,6 +317,7 @@ public class RecallService implements ApplicationContextAware {
         int avgReadCountSum = 0;
         Double t0FissionByFansSum = 0.0;
         Double t0FissionByReadAvgSum = 0.0;
+        Double t0FissionByReadAvgCorrelationSum = 0.0;
         for (ContentHisPublishArticle article : content.getHisPublishArticleList()) {
             if (article.getItemIndex() != 1 || !article.isInnerAccount()
                     || CollectionUtils.isEmpty(article.getArticleDetailInfoList())) {
@@ -323,6 +333,7 @@ public class RecallService implements ApplicationContextAware {
             if (sumFission0 == 0) {
                 continue;
             }
+            Double correlation = (article.getCorrelation() + 1) / 2;
             article.setT0FissionSum(sumFission0);
             if (article.getFans() > 0) {
                 article.setT0FissionByFans(sumFission0 * 1.0 / article.getFans());
@@ -333,6 +344,7 @@ public class RecallService implements ApplicationContextAware {
                 article.setT0FissionByReadAvg(sumFission0 * 1.0 / (article.getAvgViewCount() + 500));
                 avgReadCountSum += article.getAvgViewCount();
                 t0FissionByReadAvgSum += article.getT0FissionByReadAvg();
+                t0FissionByReadAvgCorrelationSum += article.getT0FissionByReadAvg() * correlation;
             }
             fissionSum += sumFission0;
             firstLevelSize++;
@@ -340,6 +352,7 @@ public class RecallService implements ApplicationContextAware {
         if (firstLevelSize > 0) {
             content.setT0FissionByFansMean(t0FissionByFansSum / firstLevelSize);
             content.setT0FissionByReadAvgMean(t0FissionByReadAvgSum / firstLevelSize);
+            content.setT0FissionByReadAvgCorrelationMean(t0FissionByReadAvgCorrelationSum / firstLevelSize);
             if (fansSum > 0) {
                 content.setT0FissionByFansSumAvg(fissionSum * 1.0 / fansSum);
             }

+ 2 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/ScoreService.java

@@ -101,10 +101,12 @@ public class ScoreService implements ApplicationContextAware {
             strategies.add(strategyMap.get(AccountPreDistributeStrategy.class.getSimpleName()));
             strategies.add(strategyMap.get(FlowCtlDecreaseStrategy.class.getSimpleName()));
             strategies.add(strategyMap.get(ViewCountRateStrategy.class.getSimpleName()));
+            strategies.add(strategyMap.get(ViewCountRateCorrelationStrategy.class.getSimpleName()));
             strategies.add(strategyMap.get(PublishTimesStrategy.class.getSimpleName()));
             strategies.add(strategyMap.get(HisFissionFansRateRateStrategy.class.getSimpleName()));
             strategies.add(strategyMap.get(HisFissionFansSumRateStrategy.class.getSimpleName()));
             strategies.add(strategyMap.get(HisFissionAvgReadRateRateStrategy.class.getSimpleName()));
+            strategies.add(strategyMap.get(HisFissionAvgReadRateCorrelationRateStrategy.class.getSimpleName()));
             strategies.add(strategyMap.get(HisFissionAvgReadSumRateStrategy.class.getSimpleName()));
         }
 

+ 40 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/strategy/HisFissionAvgReadRateCorrelationRateStrategy.java

@@ -0,0 +1,40 @@
+package com.tzld.longarticle.recommend.server.service.score.strategy;
+
+import com.tzld.longarticle.recommend.server.model.Content;
+import com.tzld.longarticle.recommend.server.service.AccountIndexAvgViewCountService;
+import com.tzld.longarticle.recommend.server.service.score.Score;
+import com.tzld.longarticle.recommend.server.service.score.ScoreParam;
+import com.tzld.longarticle.recommend.server.service.score.ScoreStrategy;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+@Slf4j
+public class HisFissionAvgReadRateCorrelationRateStrategy implements ScoreStrategy {
+
+    @Autowired
+    AccountIndexAvgViewCountService accountIndexAvgViewCountService;
+
+    @Override
+    public List<Score> score(ScoreParam param) {
+        long start = System.currentTimeMillis();
+        List<Score> scores = new ArrayList<>();
+        for (Content content : param.getContents()) {
+            if (CollectionUtils.isEmpty(content.getHisPublishArticleList())) {
+                continue;
+            }
+            Score score = new Score();
+            score.setStrategy(this);
+            score.setContentId(content.getId());
+            score.setScore(content.getT0FissionByReadAvgCorrelationMean());
+            scores.add(score);
+        }
+        log.info("HisFissionAvgReadRateRateStrategy cost:{}", System.currentTimeMillis() - start);
+        return scores;
+    }
+}

+ 148 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/strategy/ViewCountRateCorrelationStrategy.java

@@ -0,0 +1,148 @@
+package com.tzld.longarticle.recommend.server.service.score.strategy;
+
+import com.tzld.longarticle.recommend.server.model.Content;
+import com.tzld.longarticle.recommend.server.model.ContentHisPublishArticle;
+import com.tzld.longarticle.recommend.server.repository.crawler.AccountAvgInfoRepository;
+import com.tzld.longarticle.recommend.server.repository.entity.crawler.AccountAvgInfo;
+import com.tzld.longarticle.recommend.server.service.AccountContentPoolConfigService;
+import com.tzld.longarticle.recommend.server.service.AccountIndexAvgViewCountService;
+import com.tzld.longarticle.recommend.server.service.score.Score;
+import com.tzld.longarticle.recommend.server.service.score.ScoreParam;
+import com.tzld.longarticle.recommend.server.service.score.ScoreStrategy;
+import com.tzld.longarticle.recommend.server.util.MathUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Component
+@Slf4j
+public class ViewCountRateCorrelationStrategy implements ScoreStrategy {
+
+    @Autowired
+    AccountIndexAvgViewCountService accountIndexAvgViewCountService;
+    @Autowired
+    AccountContentPoolConfigService accountContentPoolConfigService;
+    @Autowired
+    AccountAvgInfoRepository accountAvgInfoRepository;
+
+    @Override
+    public List<Score> score(ScoreParam param) {
+        long start = System.currentTimeMillis();
+        List<Score> scores = new ArrayList<>();
+        String[] contentPools = accountContentPoolConfigService.getContentPools(param.getAccountName());
+        List<AccountAvgInfo> avgInfoList = accountAvgInfoRepository.getAllByGhIdEqualsAndStatusEquals(param.getGhId(), 1);
+        double avgViewCountFirst = accountIndexAvgViewCountService.getAvgReadCountByDB(avgInfoList, param.getGhId(), 1);
+        double avgViewCountSecond = accountIndexAvgViewCountService.getAvgReadCountByDB(avgInfoList, param.getGhId(), 2);
+        double avgViewCountThird = accountIndexAvgViewCountService.getAvgReadCountByDB(avgInfoList, param.getGhId(), 3);
+        // 缺省头条均值设置为2w,次条为1w
+        if (avgViewCountFirst < 10) {
+            avgViewCountFirst = 20000D;
+            avgViewCountSecond = 10000D;
+            avgViewCountThird = 400D;
+        }
+        for (Content content : param.getContents()) {
+            for (int i = 0; i < contentPools.length; i++) {
+                if (!contentPools[i].equals(content.getContentPoolType())) {
+                    continue;
+                }
+                double avgViewCountPos = accountIndexAvgViewCountService.getAvgReadCountByDB(avgInfoList, param.getGhId(), i + 1);
+                // 缺省头条均值设置为2w,次条为1w
+                if (avgViewCountPos < 10) {
+                    if (i == 0) {
+                        avgViewCountPos = 20000D;
+                    } else if (i == 1) {
+                        avgViewCountPos = 10000D;
+                    } else {
+                        avgViewCountPos = 400D;
+                    }
+                }
+                double showViewCountSum = 0D;
+                double avgViewCountSum = 0D;
+                double showViewCountSumFirst = 0D;
+                double avgViewCountSumFirst = 0D;
+                double showViewCountSumSecond = 0D;
+                double avgViewCountSumSecond = 0D;
+                double maxAvgViewCount = 0D;
+                for (ContentHisPublishArticle hisItem : content.getHisPublishArticleList()) {
+                    if (hisItem.isInnerAccount() && Objects.nonNull(hisItem.getViewCount())
+                            && hisItem.getViewCount() > 0 && Objects.nonNull(hisItem.getAvgViewCount())
+                            && hisItem.getAvgViewCount() > 0) {
+                        Double correlation = (hisItem.getCorrelation() + 1) / 2;
+                        maxAvgViewCount = Math.max(maxAvgViewCount, hisItem.getAvgViewCount());
+                        if (hisItem.getItemIndex() == 1) {
+                            showViewCountSumFirst += (hisItem.getViewCount() * correlation);
+                            avgViewCountSumFirst += (hisItem.getAvgViewCount() * correlation);
+                        } else if (hisItem.getItemIndex() == 2) {
+                            if (Objects.nonNull(hisItem.getFirstViewCount()) && hisItem.getFirstViewCount() > 0 &&
+                                    Objects.nonNull(hisItem.getFirstViewCountRate()) && hisItem.getFirstViewCountRate() > 0) {
+                                showViewCountSumSecond += (hisItem.getViewCount() * correlation);
+                                if (hisItem.getFirstViewCountRate() > 1) {
+                                    // 对于头条均值倍数大于1的情况,次条均值线性增加,用于debias;
+                                    // TODO: 对于小于1的情况,是否要减去?
+                                    avgViewCountSumSecond += (hisItem.getAvgViewCount() * correlation) * (hisItem.getFirstViewCountRate() - 1);
+                                } else {
+                                    avgViewCountSumSecond += (hisItem.getAvgViewCount() * correlation);
+                                }
+                            }
+                        } else {
+                            if (Objects.nonNull(hisItem.getFirstViewCount()) && hisItem.getFirstViewCount() > 0
+                                    && Objects.nonNull(hisItem.getFirstViewCountRate()) && hisItem.getFirstViewCountRate() > 0) {
+                                showViewCountSum += (hisItem.getViewCount() * correlation);
+                                if (hisItem.getFirstViewCountRate() > 1) {
+                                    // 对于头条均值倍数大于1的情况,次条均值线性增加,用于debias;
+                                    // TODO: 对于小于1的情况,是否要减去?
+                                    avgViewCountSum += (hisItem.getAvgViewCount() * correlation) * (hisItem.getFirstViewCountRate() - 1);
+                                } else {
+                                    avgViewCountSum += (hisItem.getAvgViewCount() * correlation);
+                                }
+                            }
+                        }
+                    }
+                }
+                double viewCountRate = 0D; // 设置默认值
+                double bigRateW = 1D;
+                // 如果有头条反馈数据,优先选取头条反馈数据;
+                if (showViewCountSumFirst > 0) {
+                    showViewCountSum = showViewCountSumFirst;
+                    avgViewCountSum = avgViewCountSumFirst;
+                } else if (showViewCountSumSecond > 0) {
+                    showViewCountSum = showViewCountSumSecond;
+                    avgViewCountSum = avgViewCountSumSecond;
+                    // 如果是大号头条,则降权
+                    if (avgViewCountFirst >= 3000 && i == 0) {
+                        bigRateW = 0.001D;
+                    }
+                }
+                // 均值倍数
+                if (avgViewCountSum > 0) {
+                    viewCountRate = showViewCountSum / avgViewCountSum;
+                }
+                // 置信度
+                double viewCountRateW = MathUtils.sigmoid(avgViewCountSum, 0.0005, avgViewCountPos);
+                double viewCountRateScore = 0;
+
+                if (viewCountRate > 0) {
+                    // 最终分数 = 置信度 * 均值倍数
+                    if (viewCountRate > 1 && bigRateW < 1) {
+                        // 如果是大号头条,则降权
+                        viewCountRateScore = viewCountRateW * ((viewCountRate - 1) * bigRateW + 1);
+                    } else {
+                        viewCountRateScore = viewCountRateW * viewCountRate;
+                    }
+                }
+                Score score = new Score();
+                score.setStrategy(this);
+                score.setContentId(content.getId());
+                score.setScore(viewCountRateScore);
+                scores.add(score);
+                break;
+            }
+        }
+        log.info("ViewCountRateStrategy cost:{}", System.currentTimeMillis() - start);
+        return scores;
+    }
+}

+ 8 - 0
long-article-recommend-service/src/main/resources/mapper/crawler/CrawlerBaseMapper.xml

@@ -31,4 +31,12 @@
         </foreach>
     </insert>
 
+    <insert id="batchInsertAccountCorrelation">
+        INSERT INTO account_correlation (date_str, gh_id, account_name, rel_gh_id, rel_account_name, status, correlation)
+        VALUES
+        <foreach collection="list" item="item" separator=",">
+            (#{item.dateStr}, #{item.ghId}, #{item.accountName}, #{item.relGhId}, #{item.relAccountName}, #{item.status}, #{item.correlation})
+        </foreach>
+    </insert>
+
 </mapper>

+ 65 - 14
long-article-recommend-service/src/test/java/com/tzld/longarticle/recommend/server/RecommendTest.java

@@ -1,25 +1,14 @@
 package com.tzld.longarticle.recommend.server;
 
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
 import com.tzld.longarticle.recommend.server.repository.crawler.AccountAvgInfoRepository;
 import com.tzld.longarticle.recommend.server.repository.crawler.ArticleDetailInfoRepository;
 import com.tzld.longarticle.recommend.server.repository.crawler.ArticleRepository;
-import com.tzld.longarticle.recommend.server.repository.entity.crawler.AccountAvgInfo;
-import com.tzld.longarticle.recommend.server.repository.entity.crawler.Article;
-import com.tzld.longarticle.recommend.server.repository.entity.crawler.ArticleDetailInfo;
+import com.tzld.longarticle.recommend.server.repository.mapper.crawler.CrawlerBaseMapper;
 import com.tzld.longarticle.recommend.server.service.RecommendService;
-import com.tzld.longarticle.recommend.server.service.recall.RecallParam;
-import com.tzld.longarticle.recommend.server.service.recall.RecallResult;
 import com.tzld.longarticle.recommend.server.service.recall.RecallService;
-import org.apache.commons.collections4.CollectionUtils;
-import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 
 import javax.annotation.Resource;
-import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.stream.Collectors;
 
 @SpringBootTest(classes = Application.class)
 public class RecommendTest {
@@ -34,6 +23,8 @@ public class RecommendTest {
     private ArticleDetailInfoRepository articleDetailInfoRepository;
     @Resource
     private AccountAvgInfoRepository accountAvgInfoRepository;
+    @Resource
+    private CrawlerBaseMapper crawlerBaseMapper;
 
 //    @Test
 //    void recall() {
@@ -47,12 +38,12 @@ public class RecommendTest {
 //    @Test
 //    void exportData() {
 //        Set<String> ghIds = new HashSet<>(Arrays.asList("gh_adca24a8f429", "gh_e0eb490115f5", "gh_51e4ad40466d", "gh_95ed5ecf9363"));
-//        List<Article> articleList = articleRepository.getByGhIdInAndUpdateTimeGreaterThanAndTypeEquals(ghIds, 1722441600L);
+//        List<Article> articleList = articleRepository.getByGhIdInAndUpdateTimeGreaterThanAndTypeEquals(ghIds, 1722441600L, "9");
 //
 //        Map<String, Map<Integer, List<Article>>> map = articleList.stream()
 //                .collect(Collectors.groupingBy(Article::getTitle, Collectors.groupingBy(Article::getItemIndex)));
 //        Set<String> snList = articleList.stream().map(Article::getWxSn).collect(Collectors.toSet());
-//        List<ArticleDetailInfo> articleDetailInfoList = articleDetailInfoRepository.getAllByWxSnIn(snList);
+//        List<ArticleDetailInfo> articleDetailInfoList = articleDetailInfoRepository.getAllByWxSnIn(new ArrayList<>(snList));
 //        Map<String, List<ArticleDetailInfo>> articleDetailInfoMap = articleDetailInfoList.stream()
 //                .collect(Collectors.groupingBy(ArticleDetailInfo::getWxSn));
 //
@@ -102,5 +93,65 @@ public class RecommendTest {
 //        }
 //        System.out.println(jsonArray.toJSONString());
 //    }
+//
+//    @Test
+//    void ii() throws IOException {
+//        String dateStr = "20240911";
+//        List<AccountAvgInfo> accountAvgInfoList = accountAvgInfoRepository.getAllByStatusEquals(1);
+//        Map<String, String> accountMap = accountAvgInfoList.stream().collect(
+//                Collectors.toMap(AccountAvgInfo::getAccountName, AccountAvgInfo::getGhId, (existing, replacement) -> replacement));
+//        BufferedReader reader = new BufferedReader(new FileReader("/Users/wangyunpeng/Downloads/账号相关性.json"));
+//        StringBuilder sb = new StringBuilder();
+//        String line;
+//        while ((line = reader.readLine()) != null) {
+//            sb.append(line);
+//        }
+//        String jsonStr = sb.toString();
+//        // 使用 ObjectMapper 解析 JSON
+//        ObjectMapper objectMapper = new ObjectMapper();
+//
+//        try {
+//            // 将 JSON 转换为 Map<String, Map<String, Double>>
+//            Map<String, Map<String, Double>> result = objectMapper.readValue(jsonStr,
+//                    new TypeReference<Map<String, Map<String, Double>>>() {
+//                    });
+//
+//            // 输出转换结果
+//            System.out.println(result);
+//            List<AccountCorrelation> saveList = new ArrayList<>();
+//            result.forEach((k, v) -> {
+//                String ghId = accountMap.get(k);
+//                v.forEach((k1, v1) -> {
+//                    String relGhId = accountMap.get(k1);
+//                    AccountCorrelation save = new AccountCorrelation();
+//                    save.setDateStr(dateStr);
+//                    save.setGhId(ghId);
+//                    save.setAccountName(k);
+//                    save.setRelGhId(relGhId);
+//                    save.setRelAccountName(k1);
+//                    save.setStatus(1);
+//                    save.setCorrelation(v1);
+//                    saveList.add(save);
+//                });
+//            });
+//            List<AccountCorrelation> all = new ArrayList<>(saveList);
+//            for (AccountCorrelation item : all) {
+//                if (!item.getGhId().equals(item.getRelGhId())) {
+//                    AccountCorrelation save = new AccountCorrelation();
+//                    BeanUtils.copyProperties(item, save);
+//                    save.setGhId(item.getRelGhId());
+//                    save.setAccountName(item.getRelAccountName());
+//                    save.setRelGhId(item.getGhId());
+//                    save.setRelAccountName(item.getAccountName());
+//                    saveList.add(save);
+//                }
+//            }
+//            crawlerBaseMapper.batchInsertAccountCorrelation(saveList);
+//
+//
+//        } catch (IOException e) {
+//            e.printStackTrace();
+//        }
+//    }
 
 }