소스 검색

历史表现特征排序策略

wangyunpeng 10 달 전
부모
커밋
4e9b4118d8
14개의 변경된 파일909개의 추가작업 그리고 11개의 파일을 삭제
  1. 4 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/RankStrategyEnum.java
  2. 5 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/ArticleSortResponseDataItem.java
  3. 9 10
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/Content.java
  4. 5 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/ContentHisPublishArticle.java
  5. 168 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV10Strategy.java
  6. 168 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV7Strategy.java
  7. 168 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV8Strategy.java
  8. 168 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV9Strategy.java
  9. 53 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recall/RecallService.java
  10. 9 1
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/ScoreService.java
  11. 38 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/strategy/HisFissionAvgReadRateRateStrategy.java
  12. 38 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/strategy/HisFissionAvgReadSumRateStrategy.java
  13. 38 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/strategy/HisFissionFansRateRateStrategy.java
  14. 38 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/score/strategy/HisFissionFansSumRateStrategy.java

+ 4 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/RankStrategyEnum.java

@@ -11,6 +11,10 @@ public enum RankStrategyEnum {
     ArticleRankV4("ArticleRankV4", "ArticleRankV4", "rankV4Strategy"),
     ArticleRankV5("ArticleRankV5", "ArticleRankV5", "rankV5Strategy"),
     ArticleRankV6("ArticleRankV6", "ArticleRankV6", "rankV6Strategy"),
+    ArticleRankV7("ArticleRankV7", "ArticleRankV7", "rankV7Strategy"),
+    ArticleRankV8("ArticleRankV8", "ArticleRankV8", "rankV8Strategy"),
+    ArticleRankV9("ArticleRankV9", "ArticleRankV9", "rankV9Strategy"),
+    ArticleRankV10("ArticleRankV10", "ArticleRankV10", "rankV10Strategy"),
 
     default_strategy("ArticleRankV1", "默认策略", "defaultRankStrategy"),
     ;

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

@@ -4,6 +4,8 @@ import lombok.Getter;
 import lombok.Setter;
 import lombok.experimental.Accessors;
 
+import java.util.Map;
+
 @Getter
 @Setter
 @Accessors(chain = true)
@@ -12,4 +14,7 @@ public class ArticleSortResponseDataItem {
     private String title;
     private String producePlanName;
     private String filterReason;
+
+    private Map<String, Double> scoreMap;
+    private double score;
 }

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

@@ -6,6 +6,7 @@ import lombok.NoArgsConstructor;
 import lombok.Setter;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author dyp
@@ -18,16 +19,7 @@ import java.util.List;
 public class Content {
     private String id;
     private String title;
-    // private String content;
-//    private String coverUrl;
-//    private List<String> imageUrls;
     private String producePlanName;
-    /**
-     * "Level4": "autoArticlePoolLevel4",
-     * "Level3": "autoArticlePoolLevel3",
-     * "Level2": "autoArticlePoolLevel2",
-     * "Level1": "autoArticlePoolLevel1"
-     */
     private String contentPoolType; // 内容池类别
     private String crawlerChannelContentId; // 抓取内容channelContentId
     private List<String> category; // 品类
@@ -35,11 +27,18 @@ public class Content {
     private String crawlerTitle;
     private String crawlerCoverUrl;
     private Integer crawlerViewCount;
-    //    private Integer crawlerLikeCount;
+//    private Integer crawlerLikeCount;
 //    private Long crawlerPublishTimestamp;
 //    private String crawlerAccountName;
     private String filterReason;
 
     private List<ContentHisPublishArticle> hisPublishArticleList;
+    private Double t0FissionByFansMean;
+    private Double t0FissionByReadAvgMean;
+    private Double t0FissionByFansSumAvg;
+    private Double t0FissionByReadAvgSumAvg;
+
+    private Map<String, Double> scoreMap;
+    private double score;
 }
 

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

@@ -24,4 +24,9 @@ public class ContentHisPublishArticle {
     private boolean isInnerAccount = false;
     private long fans;
     private List<ArticleDetailInfo> articleDetailInfoList;
+
+    // data
+    private Integer t0FissionSum;
+    private Double t0FissionByFans;
+    private Double t0FissionByReadAvg;
 }

+ 168 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV10Strategy.java

@@ -0,0 +1,168 @@
+package com.tzld.longarticle.recommend.server.service.rank.strategy;
+
+
+import com.tzld.longarticle.recommend.server.common.enums.ContentPoolEnum;
+import com.tzld.longarticle.recommend.server.model.Content;
+import com.tzld.longarticle.recommend.server.service.AccountContentPoolConfigService;
+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.TitleSimilarCheckUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * @author dyp
+ */
+@Service
+@Slf4j
+public class RankV10Strategy implements RankStrategy {
+
+    @Autowired
+    private ScoreService scoreService;
+    @Autowired
+    private AccountContentPoolConfigService accountContentPoolConfigService;
+
+    public RankResult rank(RankParam param) {
+
+        //log.info("RankParam {}", JSONUtils.toJson(param));
+        ScoreResult scoreResult = scoreService.score(convertToScoreParam(param));
+
+        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()));
+            double score;
+            if (contentPools[0].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(HisFissionAvgReadSumRateStrategy.class.getSimpleName());
+            } else if (contentPools[1].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(SimilarityStrategy.class.getSimpleName())
+                        + item.getScore(CategoryStrategy.class.getSimpleName())
+                        + item.getScore(FlowCtlDecreaseStrategy.class.getSimpleName());
+                if (item.getScore(PublishTimesStrategy.class.getSimpleName()) >= 0) {
+                    score += item.getScore(ViewCountRateStrategy.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);
+            item.setScore(score);
+            return item;
+        });
+
+        // 1 排序
+        Collections.sort(items, (o1, o2) -> -Double.compare(o1.getScore(), o2.getScore()));
+        // 2 相似去重
+        List<Content> contents = CommonCollectionUtils.toList(items, RankItem::getContent);
+        contents = deduplication(contents);
+
+        // 3 文章按照内容池分组
+        Map<String, List<Content>> contentMap = new HashMap<>();
+        for (Content c : contents) {
+            List<Content> data = contentMap.computeIfAbsent(c.getContentPoolType(), k -> new ArrayList<>());
+            data.add(c);
+        }
+        // 4 选文章
+        List<Content> result = new ArrayList<>();
+
+        // 头
+        List<Content> pool1 = contentMap.get(contentPools[0]);
+        if (CollectionUtils.isNotEmpty(pool1)) {
+            result.add(pool1.get(0));
+        } else {
+            // 替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(1);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool1Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool1Replace)) {
+                    result.add(pool1Replace.get(0));
+                }
+            }
+        }
+        // 次
+        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) {
+                        result.add(pool2.get(1));
+                        break;
+                    }
+                }
+            }
+        } else {
+            // 替补 根据设置替补内容池查找内容尽心替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(2);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool2Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool2Replace)) {
+                    result.add(pool2Replace.get(0));
+                }
+            }
+        }
+
+        // 3-8
+        List<Content> pool = contentMap.get(contentPools[2]);
+        if (CollectionUtils.isNotEmpty(pool)) {
+            result.addAll(pool.subList(0, Math.min(pool.size(), param.getSize() - result.size())));
+        }
+
+        return new RankResult(result);
+    }
+
+    private ScoreParam convertToScoreParam(RankParam param) {
+        ScoreParam scoreParam = new ScoreParam();
+        scoreParam.setGhId(param.getGhId());
+        scoreParam.setAccountName(param.getAccountName());
+        scoreParam.setContents(param.getContents());
+        scoreParam.setStrategy(param.getStrategy());
+        scoreParam.setScene(param.getScene());
+        return scoreParam;
+    }
+
+    private List<Content> deduplication(List<Content> contents) {
+        List<String> titles = new ArrayList<>();
+        List<Content> result = new ArrayList<>();
+        // 遍历所有列表
+        for (String contentPool : ContentPoolEnum.getOrderContentPool()) {
+            for (Content c : contents) {
+                if (!contentPool.equals(c.getContentPoolType())) {
+                    continue;
+                }
+                if (!TitleSimilarCheckUtil.isDuplicateContent(c.getTitle(), titles)) {
+                    result.add(c);
+                    titles.add(c.getTitle());
+                }
+            }
+        }
+
+        return result;
+    }
+
+}

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

@@ -0,0 +1,168 @@
+package com.tzld.longarticle.recommend.server.service.rank.strategy;
+
+
+import com.tzld.longarticle.recommend.server.common.enums.ContentPoolEnum;
+import com.tzld.longarticle.recommend.server.model.Content;
+import com.tzld.longarticle.recommend.server.service.AccountContentPoolConfigService;
+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.TitleSimilarCheckUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * @author dyp
+ */
+@Service
+@Slf4j
+public class RankV7Strategy implements RankStrategy {
+
+    @Autowired
+    private ScoreService scoreService;
+    @Autowired
+    private AccountContentPoolConfigService accountContentPoolConfigService;
+
+    public RankResult rank(RankParam param) {
+
+        //log.info("RankParam {}", JSONUtils.toJson(param));
+        ScoreResult scoreResult = scoreService.score(convertToScoreParam(param));
+
+        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()));
+            double score;
+            if (contentPools[0].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(HisFissionFansRateRateStrategy.class.getSimpleName());
+            } else if (contentPools[1].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(SimilarityStrategy.class.getSimpleName())
+                        + item.getScore(CategoryStrategy.class.getSimpleName())
+                        + item.getScore(FlowCtlDecreaseStrategy.class.getSimpleName());
+                if (item.getScore(PublishTimesStrategy.class.getSimpleName()) >= 0) {
+                    score += item.getScore(ViewCountRateStrategy.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);
+            item.setScore(score);
+            return item;
+        });
+
+        // 1 排序
+        Collections.sort(items, (o1, o2) -> -Double.compare(o1.getScore(), o2.getScore()));
+        // 2 相似去重
+        List<Content> contents = CommonCollectionUtils.toList(items, RankItem::getContent);
+        contents = deduplication(contents);
+
+        // 3 文章按照内容池分组
+        Map<String, List<Content>> contentMap = new HashMap<>();
+        for (Content c : contents) {
+            List<Content> data = contentMap.computeIfAbsent(c.getContentPoolType(), k -> new ArrayList<>());
+            data.add(c);
+        }
+        // 4 选文章
+        List<Content> result = new ArrayList<>();
+
+        // 头
+        List<Content> pool1 = contentMap.get(contentPools[0]);
+        if (CollectionUtils.isNotEmpty(pool1)) {
+            result.add(pool1.get(0));
+        } else {
+            // 替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(1);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool1Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool1Replace)) {
+                    result.add(pool1Replace.get(0));
+                }
+            }
+        }
+        // 次
+        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) {
+                        result.add(pool2.get(1));
+                        break;
+                    }
+                }
+            }
+        } else {
+            // 替补 根据设置替补内容池查找内容尽心替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(2);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool2Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool2Replace)) {
+                    result.add(pool2Replace.get(0));
+                }
+            }
+        }
+
+        // 3-8
+        List<Content> pool = contentMap.get(contentPools[2]);
+        if (CollectionUtils.isNotEmpty(pool)) {
+            result.addAll(pool.subList(0, Math.min(pool.size(), param.getSize() - result.size())));
+        }
+
+        return new RankResult(result);
+    }
+
+    private ScoreParam convertToScoreParam(RankParam param) {
+        ScoreParam scoreParam = new ScoreParam();
+        scoreParam.setGhId(param.getGhId());
+        scoreParam.setAccountName(param.getAccountName());
+        scoreParam.setContents(param.getContents());
+        scoreParam.setStrategy(param.getStrategy());
+        scoreParam.setScene(param.getScene());
+        return scoreParam;
+    }
+
+    private List<Content> deduplication(List<Content> contents) {
+        List<String> titles = new ArrayList<>();
+        List<Content> result = new ArrayList<>();
+        // 遍历所有列表
+        for (String contentPool : ContentPoolEnum.getOrderContentPool()) {
+            for (Content c : contents) {
+                if (!contentPool.equals(c.getContentPoolType())) {
+                    continue;
+                }
+                if (!TitleSimilarCheckUtil.isDuplicateContent(c.getTitle(), titles)) {
+                    result.add(c);
+                    titles.add(c.getTitle());
+                }
+            }
+        }
+
+        return result;
+    }
+
+}

+ 168 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV8Strategy.java

@@ -0,0 +1,168 @@
+package com.tzld.longarticle.recommend.server.service.rank.strategy;
+
+
+import com.tzld.longarticle.recommend.server.common.enums.ContentPoolEnum;
+import com.tzld.longarticle.recommend.server.model.Content;
+import com.tzld.longarticle.recommend.server.service.AccountContentPoolConfigService;
+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.TitleSimilarCheckUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * @author dyp
+ */
+@Service
+@Slf4j
+public class RankV8Strategy implements RankStrategy {
+
+    @Autowired
+    private ScoreService scoreService;
+    @Autowired
+    private AccountContentPoolConfigService accountContentPoolConfigService;
+
+    public RankResult rank(RankParam param) {
+
+        //log.info("RankParam {}", JSONUtils.toJson(param));
+        ScoreResult scoreResult = scoreService.score(convertToScoreParam(param));
+
+        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()));
+            double score;
+            if (contentPools[0].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(HisFissionFansSumRateStrategy.class.getSimpleName());
+            } else if (contentPools[1].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(SimilarityStrategy.class.getSimpleName())
+                        + item.getScore(CategoryStrategy.class.getSimpleName())
+                        + item.getScore(FlowCtlDecreaseStrategy.class.getSimpleName());
+                if (item.getScore(PublishTimesStrategy.class.getSimpleName()) >= 0) {
+                    score += item.getScore(ViewCountRateStrategy.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);
+            item.setScore(score);
+            return item;
+        });
+
+        // 1 排序
+        Collections.sort(items, (o1, o2) -> -Double.compare(o1.getScore(), o2.getScore()));
+        // 2 相似去重
+        List<Content> contents = CommonCollectionUtils.toList(items, RankItem::getContent);
+        contents = deduplication(contents);
+
+        // 3 文章按照内容池分组
+        Map<String, List<Content>> contentMap = new HashMap<>();
+        for (Content c : contents) {
+            List<Content> data = contentMap.computeIfAbsent(c.getContentPoolType(), k -> new ArrayList<>());
+            data.add(c);
+        }
+        // 4 选文章
+        List<Content> result = new ArrayList<>();
+
+        // 头
+        List<Content> pool1 = contentMap.get(contentPools[0]);
+        if (CollectionUtils.isNotEmpty(pool1)) {
+            result.add(pool1.get(0));
+        } else {
+            // 替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(1);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool1Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool1Replace)) {
+                    result.add(pool1Replace.get(0));
+                }
+            }
+        }
+        // 次
+        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) {
+                        result.add(pool2.get(1));
+                        break;
+                    }
+                }
+            }
+        } else {
+            // 替补 根据设置替补内容池查找内容尽心替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(2);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool2Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool2Replace)) {
+                    result.add(pool2Replace.get(0));
+                }
+            }
+        }
+
+        // 3-8
+        List<Content> pool = contentMap.get(contentPools[2]);
+        if (CollectionUtils.isNotEmpty(pool)) {
+            result.addAll(pool.subList(0, Math.min(pool.size(), param.getSize() - result.size())));
+        }
+
+        return new RankResult(result);
+    }
+
+    private ScoreParam convertToScoreParam(RankParam param) {
+        ScoreParam scoreParam = new ScoreParam();
+        scoreParam.setGhId(param.getGhId());
+        scoreParam.setAccountName(param.getAccountName());
+        scoreParam.setContents(param.getContents());
+        scoreParam.setStrategy(param.getStrategy());
+        scoreParam.setScene(param.getScene());
+        return scoreParam;
+    }
+
+    private List<Content> deduplication(List<Content> contents) {
+        List<String> titles = new ArrayList<>();
+        List<Content> result = new ArrayList<>();
+        // 遍历所有列表
+        for (String contentPool : ContentPoolEnum.getOrderContentPool()) {
+            for (Content c : contents) {
+                if (!contentPool.equals(c.getContentPoolType())) {
+                    continue;
+                }
+                if (!TitleSimilarCheckUtil.isDuplicateContent(c.getTitle(), titles)) {
+                    result.add(c);
+                    titles.add(c.getTitle());
+                }
+            }
+        }
+
+        return result;
+    }
+
+}

+ 168 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/rank/strategy/RankV9Strategy.java

@@ -0,0 +1,168 @@
+package com.tzld.longarticle.recommend.server.service.rank.strategy;
+
+
+import com.tzld.longarticle.recommend.server.common.enums.ContentPoolEnum;
+import com.tzld.longarticle.recommend.server.model.Content;
+import com.tzld.longarticle.recommend.server.service.AccountContentPoolConfigService;
+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.TitleSimilarCheckUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+
+/**
+ * @author dyp
+ */
+@Service
+@Slf4j
+public class RankV9Strategy implements RankStrategy {
+
+    @Autowired
+    private ScoreService scoreService;
+    @Autowired
+    private AccountContentPoolConfigService accountContentPoolConfigService;
+
+    public RankResult rank(RankParam param) {
+
+        //log.info("RankParam {}", JSONUtils.toJson(param));
+        ScoreResult scoreResult = scoreService.score(convertToScoreParam(param));
+
+        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()));
+            double score;
+            if (contentPools[0].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(HisFissionAvgReadRateRateStrategy.class.getSimpleName());
+            } else if (contentPools[1].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(SimilarityStrategy.class.getSimpleName())
+                        + item.getScore(CategoryStrategy.class.getSimpleName())
+                        + item.getScore(FlowCtlDecreaseStrategy.class.getSimpleName());
+                if (item.getScore(PublishTimesStrategy.class.getSimpleName()) >= 0) {
+                    score += item.getScore(ViewCountRateStrategy.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);
+            item.setScore(score);
+            return item;
+        });
+
+        // 1 排序
+        Collections.sort(items, (o1, o2) -> -Double.compare(o1.getScore(), o2.getScore()));
+        // 2 相似去重
+        List<Content> contents = CommonCollectionUtils.toList(items, RankItem::getContent);
+        contents = deduplication(contents);
+
+        // 3 文章按照内容池分组
+        Map<String, List<Content>> contentMap = new HashMap<>();
+        for (Content c : contents) {
+            List<Content> data = contentMap.computeIfAbsent(c.getContentPoolType(), k -> new ArrayList<>());
+            data.add(c);
+        }
+        // 4 选文章
+        List<Content> result = new ArrayList<>();
+
+        // 头
+        List<Content> pool1 = contentMap.get(contentPools[0]);
+        if (CollectionUtils.isNotEmpty(pool1)) {
+            result.add(pool1.get(0));
+        } else {
+            // 替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(1);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool1Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool1Replace)) {
+                    result.add(pool1Replace.get(0));
+                }
+            }
+        }
+        // 次
+        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) {
+                        result.add(pool2.get(1));
+                        break;
+                    }
+                }
+            }
+        } else {
+            // 替补 根据设置替补内容池查找内容尽心替补
+            AccountIndexReplacePoolConfig replacePoolConfig = indexReplacePoolConfigMap.get(2);
+            if (Objects.nonNull(replacePoolConfig)) {
+                List<Content> pool2Replace = contentMap.get(replacePoolConfig.getContentPool());
+                if (CollectionUtils.isNotEmpty(pool2Replace)) {
+                    result.add(pool2Replace.get(0));
+                }
+            }
+        }
+
+        // 3-8
+        List<Content> pool = contentMap.get(contentPools[2]);
+        if (CollectionUtils.isNotEmpty(pool)) {
+            result.addAll(pool.subList(0, Math.min(pool.size(), param.getSize() - result.size())));
+        }
+
+        return new RankResult(result);
+    }
+
+    private ScoreParam convertToScoreParam(RankParam param) {
+        ScoreParam scoreParam = new ScoreParam();
+        scoreParam.setGhId(param.getGhId());
+        scoreParam.setAccountName(param.getAccountName());
+        scoreParam.setContents(param.getContents());
+        scoreParam.setStrategy(param.getStrategy());
+        scoreParam.setScene(param.getScene());
+        return scoreParam;
+    }
+
+    private List<Content> deduplication(List<Content> contents) {
+        List<String> titles = new ArrayList<>();
+        List<Content> result = new ArrayList<>();
+        // 遍历所有列表
+        for (String contentPool : ContentPoolEnum.getOrderContentPool()) {
+            for (Content c : contents) {
+                if (!contentPool.equals(c.getContentPoolType())) {
+                    continue;
+                }
+                if (!TitleSimilarCheckUtil.isDuplicateContent(c.getTitle(), titles)) {
+                    result.add(c);
+                    titles.add(c.getTitle());
+                }
+            }
+        }
+
+        return result;
+    }
+
+}

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

@@ -270,10 +270,63 @@ public class RecallService implements ApplicationContextAware {
                 }
                 content.getHisPublishArticleList().add(article);
             }
+            // 设置头条阅读均值
+            setT0Data(content);
         }
         log.info("setTitleAvgViewCount cost:{}", System.currentTimeMillis() - start);
     }
 
+    private void setT0Data(Content content) {
+        if (CollectionUtils.isEmpty(content.getHisPublishArticleList())) {
+            return;
+        }
+        int firstLevelSize = 0;
+        int fissionSum = 0;
+        int fansSum = 0;
+        int avgReadCountSum = 0;
+        Double t0FissionByFansSum = 0.0;
+        Double t0FissionByReadAvgSum = 0.0;
+        for (ContentHisPublishArticle article : content.getHisPublishArticleList()) {
+            if (article.getItemIndex() != 1
+                    || CollectionUtils.isEmpty(article.getArticleDetailInfoList())) {
+                continue;
+            }
+            int sumFission = 0;
+            Date minDate = article.getArticleDetailInfoList().stream().map(ArticleDetailInfo::getRecallDt).min(Date::compareTo).orElse(new Date());
+            for (ArticleDetailInfo articleDetailInfo : article.getArticleDetailInfoList()) {
+                if (articleDetailInfo.getRecallDt().equals(minDate) && Objects.nonNull(articleDetailInfo.getFission0())) {
+                    sumFission += articleDetailInfo.getFission0();
+                }
+            }
+            if (sumFission == 0) {
+                continue;
+            }
+            article.setT0FissionSum(sumFission);
+            if (article.getFans() > 0) {
+                article.setT0FissionByFans(sumFission * 1.0 / article.getFans());
+                fansSum += (int) article.getFans();
+                t0FissionByFansSum += article.getT0FissionByFans();
+            }
+            if (Objects.nonNull(article.getAvgViewCount()) && article.getAvgViewCount() > 0) {
+                article.setT0FissionByReadAvg(sumFission * 1.0 / article.getAvgViewCount());
+                avgReadCountSum += article.getAvgViewCount();
+                t0FissionByReadAvgSum += article.getT0FissionByReadAvg();
+            }
+            fissionSum += sumFission;
+            firstLevelSize++;
+        }
+        if (firstLevelSize > 0) {
+            content.setT0FissionByFansMean(t0FissionByFansSum / firstLevelSize);
+            content.setT0FissionByReadAvgMean(t0FissionByReadAvgSum / firstLevelSize);
+            if (fansSum > 0) {
+                content.setT0FissionByFansSumAvg(fissionSum * 1.0 / fansSum);
+            }
+            if (avgReadCountSum > 0) {
+                content.setT0FissionByReadAvgSumAvg(fissionSum * 1.0 / avgReadCountSum);
+            }
+        }
+    }
+
     public static void main(String[] args) {
         String url = "http://mp.weixin.qq.com/s?__biz=Mzg2ODk4MTg3OQ==&mid=2247488306&idx=1&sn=93ebadc5bc7161a0dee48355013d3bc4&chksm=cfb6c1cb2bcdd80dd16d5d604d741a0019ae791125265a042d26100ba21ddb9e5c643ecc2264&scene=126&sessionid=1679649075#rd";
         String md5 = generateArticleUniqueMd5(url);

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

@@ -93,12 +93,20 @@ public class ScoreService implements ApplicationContextAware {
             strategies.add(strategyMap.get(ViewCountRateStrategy.class.getSimpleName()));
         }
         if (StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV4.getStrategy())
-                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV5.getStrategy())) {
+                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV5.getStrategy())
+                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV7.getStrategy())
+                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV8.getStrategy())
+                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV9.getStrategy())
+                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV10.getStrategy())) {
             strategies.add(strategyMap.get(CategoryStrategy.class.getSimpleName()));
             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(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(HisFissionAvgReadSumRateStrategy.class.getSimpleName()));
         }
 
         return strategies;

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

@@ -0,0 +1,38 @@
+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 HisFissionAvgReadRateRateStrategy implements ScoreStrategy {
+
+    @Autowired
+    AccountIndexAvgViewCountService accountIndexAvgViewCountService;
+
+    @Override
+    public List<Score> score(ScoreParam param) {
+        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.getT0FissionByReadAvgMean());
+            scores.add(score);
+        }
+        return scores;
+    }
+}

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

@@ -0,0 +1,38 @@
+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 HisFissionAvgReadSumRateStrategy implements ScoreStrategy {
+
+    @Autowired
+    AccountIndexAvgViewCountService accountIndexAvgViewCountService;
+
+    @Override
+    public List<Score> score(ScoreParam param) {
+        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.getT0FissionByReadAvgSumAvg());
+            scores.add(score);
+        }
+        return scores;
+    }
+}

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

@@ -0,0 +1,38 @@
+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 HisFissionFansRateRateStrategy implements ScoreStrategy {
+
+    @Autowired
+    AccountIndexAvgViewCountService accountIndexAvgViewCountService;
+
+    @Override
+    public List<Score> score(ScoreParam param) {
+        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.getT0FissionByFansMean());
+            scores.add(score);
+        }
+        return scores;
+    }
+}

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

@@ -0,0 +1,38 @@
+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 HisFissionFansSumRateStrategy implements ScoreStrategy {
+
+    @Autowired
+    AccountIndexAvgViewCountService accountIndexAvgViewCountService;
+
+    @Override
+    public List<Score> score(ScoreParam param) {
+        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.getT0FissionByFansSumAvg());
+            scores.add(score);
+        }
+        return scores;
+    }
+}