Browse Source

Merge branch 'master' into dev-xym-add-match-video

xueyiming 1 tháng trước cách đây
mục cha
commit
0619188fe6
45 tập tin đã thay đổi với 2321 bổ sung177 xóa
  1. 40 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/ContentPositionEnum.java
  2. 2 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/longArticle/VideoPoolPlatformEnum.java
  3. 1 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/recommend/RankStrategyEnum.java
  4. 1 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/recommend/ScoreStrategyEnum.java
  5. 3 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/aigc/AigcBaseMapper.java
  6. 5 4
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/longArticle/ArticleAuditMapper.java
  7. 10 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/longArticle/LongArticleBaseMapper.java
  8. 1 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/dto/ContentHisPublishArticle.java
  9. 2 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/crawler/AccountAvgInfo.java
  10. 5 23
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/longArticle/DatastatSortStrategy.java
  11. 28 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/longArticle/LongArticlesNewVideoCover.java
  12. 34 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/longArticle/LongArticlesPublishMiniprogram.java
  13. 105 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/longArticle/PublishDailyReport.java
  14. 19 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/CategoryGroupDataExport.java
  15. 26 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/CategoryGroupFunnelExport.java
  16. 1 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/ContentEffectGroupBySourceExport.java
  17. 61 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/CoverStatisticDataExport.java
  18. 51 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/CoverStatisticOneDataExport.java
  19. 16 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/DailyAuditProduceExport.java
  20. 1 1
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/NewSortStrategyExport.java
  21. 1 1
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/aigc/ProducePlanExeRecordRepository.java
  22. 2 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/aigc/PublishPlanRepository.java
  23. 5 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/crawler/ArticleDetailInfoRepository.java
  24. 3 1
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/longArticle/DatastatSortStrategyRepository.java
  25. 7 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/longArticle/LongArticlesNewVideoCoverRepository.java
  26. 11 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/longArticle/LongArticlesPublishMiniprogramRepository.java
  27. 10 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/longArticle/PublishDailyReportRepository.java
  28. 1 1
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/ArticleCategoryService.java
  29. 118 10
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/ArticlePromotionService.java
  30. 21 5
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/ArticleVideoAuditService.java
  31. 1216 96
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/DataDashboardService.java
  32. 9 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/RecommendService.java
  33. 172 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/rank/strategy/RankV17Strategy.java
  34. 27 18
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/recall/RecallService.java
  35. 3 1
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/score/ScoreService.java
  36. 3 3
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/score/strategy/SimilarityStrategy.java
  37. 161 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/score/strategy/ViewCountRateV2Strategy.java
  38. 21 0
      long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/web/recommend/DataDashboardController.java
  39. 18 1
      long-article-recommend-service/src/main/resources/mapper/aigc/AigcBaseMapper.xml
  40. 1 1
      long-article-recommend-service/src/main/resources/mapper/crawler/CrawlerBaseMapper.xml
  41. 13 9
      long-article-recommend-service/src/main/resources/mapper/longArticle/ArticleAuditMapper.xml
  42. 74 0
      long-article-recommend-service/src/main/resources/mapper/longArticle/LongArticleBaseMapper.xml
  43. 1 1
      long-article-recommend-service/src/test/java/com/tzld/longarticle/recommend/server/ArticleVideoAuditTest.java
  44. 2 1
      long-article-recommend-service/src/test/java/com/tzld/longarticle/recommend/server/ScoreStrategyTest.java
  45. 9 0
      long-article-recommend-service/src/test/java/com/tzld/longarticle/recommend/server/XxlJobTest.java

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

@@ -0,0 +1,40 @@
+package com.tzld.longarticle.recommend.server.common.enums;
+
+import java.util.Objects;
+
+public enum ContentPositionEnum {
+
+    One(1, "头条"),
+    Two(2, "次条"),
+    Three(3, "3-8"),
+    Four(4, "3-8"),
+    Five(5, "3-8"),
+    Six(6, "3-8"),
+    Seven(7, "3-8"),
+    Eight(8, "3-8"),
+    ;
+
+    ContentPositionEnum(Integer position, String name) {
+        this.position = position;
+        this.name = name;
+    }
+
+    public final Integer position;
+
+    public final String name;
+
+
+    public static String from(Integer position) {
+        if (position == null) {
+            return null;
+        }
+        for (ContentPositionEnum positionEnum : ContentPositionEnum.values()) {
+            if (Objects.equals(positionEnum.position, position)) {
+                return positionEnum.name;
+            }
+        }
+        return null;
+    }
+
+
+}

+ 2 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/common/enums/longArticle/VideoPoolPlatformEnum.java

@@ -11,6 +11,8 @@ public enum VideoPoolPlatformEnum {
     HkSP("hksp", "好看视频"),
     SPH("sph", "视频号视频"),
     TOUTIAO("toutiao", "头条视频"),
+    SOHU("sohu", "搜狐视频"),
+    PQ("piaoquan", "票圈视频"),
     ;
 
     private final String platform;

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

@@ -22,6 +22,7 @@ public enum RankStrategyEnum {
 
     ArticleRankV15("ArticleRankV15", "ArticleRankV15", "rankV15Strategy"),
     ArticleRankV16("ArticleRankV16", "ArticleRankV16", "rankV16Strategy"),
+    ArticleRankV17("ArticleRankV17", "ArticleRankV17", "rankV17Strategy"),
 
     HIS_JUMP_STRATEGY("ArticleRankHisJump", "历史表现跳过相似度策略", "hisJumpRankStrategy"),
     INFINITE_STRATEGY("ArticleRankInfinite", "无限发表", "infiniteRankStrategy"),

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

@@ -15,6 +15,7 @@ public enum ScoreStrategyEnum {
     SIMILARITY("SimilarityStrategy"),
     VIEW_COUNT_RATE_CORRELATION("ViewCountRateCorrelationStrategy"),
     VIEW_COUNT_RATE("ViewCountRateStrategy"),
+    VIEW_COUNT_RATE_V2("ViewCountRateV2Strategy"),
     VIEW_COUNT("ViewCountStrategy"),
     VIEW_MULTIPLIER("ViewMultiplierStrategy"),
     CRAWLER_DAYS_DECREASE_STRATEGY("CrawlerDaysDecreaseStrategy"),

+ 3 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/aigc/AigcBaseMapper.java

@@ -5,6 +5,7 @@ import com.tzld.longarticle.recommend.server.model.dto.aigc.BadCrawlerAccountDTO
 import com.tzld.longarticle.recommend.server.model.dto.aigc.IdChannelDTO;
 import com.tzld.longarticle.recommend.server.model.dto.aigc.IdPlatformDTO;
 import com.tzld.longarticle.recommend.server.model.entity.aigc.*;
+import com.tzld.longarticle.recommend.server.model.vo.DailyAuditProduceExport;
 import com.tzld.longarticle.recommend.server.model.vo.IdNameVO;
 import org.apache.ibatis.annotations.Mapper;
 
@@ -84,4 +85,6 @@ public interface AigcBaseMapper {
     Long getVideoPoolPlatformProduceAuditPassCount(List<String> producePlanIds, List<Integer> auditStatus, Long start, Long end, String platform);
 
     List<IdPlatformDTO> getIdPlatformByContentId(List<String> idList);
+
+    List<DailyAuditProduceExport> getAccountDailyProduceAuditCount(Long startTimeStamp, Long endTimeStamp);
 }

+ 5 - 4
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/longArticle/ArticleAuditMapper.java

@@ -28,10 +28,11 @@ public interface ArticleAuditMapper {
                                                   List<String> producePlanIds, String poolLevel,
                                                   List<String> excludeContentIds);
 
-    List<ArticleVideoAuditListVO> articleVideoWatingAuditList(List<Integer> status,
-                                                              List<String> flowPoolLevels,
-                                                              List<String> excludeContentIds,
-                                                              Integer size);
+    List<ArticleVideoAuditListVO> articleVideoWaitingAuditList(List<Integer> status,
+                                                               List<String> flowPoolLevels,
+                                                               List<String> excludeContentIds,
+                                                               List<String> producePlanIds,
+                                                               Integer size);
 
 
     void updateCrawlerVideoIsIllegal(List<Integer> illegalVideoIds, Integer isIllegal);

+ 10 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/mapper/longArticle/LongArticleBaseMapper.java

@@ -5,6 +5,8 @@ import com.tzld.longarticle.recommend.server.model.dto.LongArticlesCrawlerVideos
 import com.tzld.longarticle.recommend.server.model.dto.LongArticlesMatchVideos;
 import com.tzld.longarticle.recommend.server.model.entity.longArticle.*;
 import com.tzld.longarticle.recommend.server.model.param.ArticleVideoPoolSourceParam;
+import com.tzld.longarticle.recommend.server.model.vo.CategoryGroupFunnelExport;
+import com.tzld.longarticle.recommend.server.model.vo.DailyAuditProduceExport;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.Date;
@@ -32,6 +34,8 @@ public interface LongArticleBaseMapper {
     List<DatastatSortStrategy> getArticlePromotion(Integer viewCount, Double viewCountRate,
                                                    Integer fans, String dateStr, List<Integer> positions);
 
+    List<DatastatSortStrategy> getArticlePromotionCandidates(Integer fans, String dateStr, List<Integer> positions);
+
     void batchInsertLongArticlesRootSourceId(List<LongArticlesRootSourceId> list);
 
     void batchInsertGetOffVideos(List<GetOffVideos> list);
@@ -93,4 +97,10 @@ public interface LongArticleBaseMapper {
     VideoEndScreenTransformationTask getVideoEndScreenTransformationTask(Long videoOriginId);
 
     void saveVideoEndScreenTransformationTask(VideoEndScreenTransformationTask task);
+
+    List<DailyAuditProduceExport> getAccountDailyVideoAuditCount(Long startTimeStamp, Long endTimeStamp);
+
+    List<CategoryGroupFunnelExport> getCategoryMatchCount(Date start, Date end);
+
+    List<CategoryGroupFunnelExport> getCategoryTitleAuditCount(Long start, Long end);
 }

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

@@ -20,6 +20,7 @@ public class ContentHisPublishArticle {
     private Long updateTime;
     private Long publishTimestamp;
     private Integer avgViewCount;
+    private Double readAvgCiUpper;
     private Double viewCountRate;
     private Integer firstViewCount;
     private Double firstViewCountRate;

+ 2 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/crawler/AccountAvgInfo.java

@@ -46,6 +46,8 @@ public class AccountAvgInfo implements Serializable {
     private Integer businessType; // 1 表示长文,2 表示投流,3 表示企微
     @Column(name = "read_rate_avg")
     private Double readRateAvg;
+    @Column(name = "read_avg_ci_upper")
+    private Double readAvgCiUpper;
 
     @Data
     public static class PK implements Serializable {

+ 5 - 23
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/longArticle/DatastatSortStrategy.java

@@ -12,18 +12,18 @@ import java.io.Serializable;
 @NoArgsConstructor
 @Entity
 @Table(name = "datastat_sort_strategy")
-@IdClass(DatastatSortStrategy.PK.class)
 public class DatastatSortStrategy implements Serializable {
 
     @Id
+    private Long id;
+    @Column(name = "date_str")
     private String dateStr;
-    @Id
+    @Column(name = "publish_time")
     private String publishTime;
-    @Id
+    @Column(name = "account_name")
     private String accountName;
-    @Id
+    @Column(name = "position")
     private Integer position;
-
     @Column(name = "type")
     private String type;
     @Column(name = "account_mode")
@@ -171,22 +171,4 @@ public class DatastatSortStrategy implements Serializable {
     @Column(name = "source_id")
     private String sourceId;
 
-
-    @Data
-    public static class PK implements Serializable {
-
-        @Column(name = "date_str")
-        private String dateStr;
-        @Column(name = "publish_time")
-        private String publishTime;
-        @Column(name = "account_name")
-        private String accountName;
-        @Column(name = "position")
-        private Integer position;
-
-        public PK() {
-        }
-
-
-    }
 }

+ 28 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/longArticle/LongArticlesNewVideoCover.java

@@ -0,0 +1,28 @@
+package com.tzld.longarticle.recommend.server.model.entity.longArticle;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "long_articles_new_video_cover")
+public class LongArticlesNewVideoCover {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private Long id;
+
+
+    @Column(name = "version")
+    private Integer version;
+
+    @Column(name = "get_cover_status")
+    private Integer coverStatus;
+
+}

+ 34 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/longArticle/LongArticlesPublishMiniprogram.java

@@ -0,0 +1,34 @@
+package com.tzld.longarticle.recommend.server.model.entity.longArticle;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "long_articles_publish_miniprogram")
+public class LongArticlesPublishMiniprogram {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    @Column(name = "id")
+    private Long id;
+
+    @Column(name = "crawler_video_id")
+    private Integer crawlerVideoId;
+
+
+    @Column(name = "root_source_id")
+    private String rootSourceId;
+
+
+    @Column(name = "new_video_cover_id")
+    private Long newVideoCoverId;
+
+
+
+}

+ 105 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/entity/longArticle/PublishDailyReport.java

@@ -0,0 +1,105 @@
+package com.tzld.longarticle.recommend.server.model.entity.longArticle;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.*;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "publish_daily_report")
+public class PublishDailyReport {
+
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private Long id;
+
+    @Column(name = "date_str")
+    private String dateStr;
+
+    @Column(name = "publish_time")
+    private String publishTime;
+
+    @Column(name = "account_source")
+    private String accountSource;
+
+    @Column(name = "account_status")
+    private String accountStatus;
+
+    @Column(name = "business_type")
+    private String businessType;
+
+    @Column(name = "account_name")
+    private String accountName;
+
+    @Column(name = "strategy")
+    private String strategy;
+
+    @Column(name = "fans")
+    private Integer fans;
+
+    @Column(name = "avg_view_count")
+    private Double avgViewCount;
+
+    @Column(name = "read_rate")
+    private Double readRate = 0.0;
+
+    @Column(name = "read_fans_rate")
+    private Double readFansRate = 0.0;
+
+    @Column(name = "first_read_rate")
+    private Double firstReadRate = 0.0;
+
+    @Column(name = "fission0_first_rate")
+    private Double fission0FirstRate = 0.0;
+
+    @Column(name = "fission0_head_first_rate")
+    private Double fission0HeadFirstRate = 0.0;
+
+    @Column(name = "fission0_recommend_first_rate")
+    private Double fission0RecommendFirstRate = 0.0;
+
+    @Column(name = "fission1_fission0_rate")
+    private Double fission1Fission0Rate = 0.0;
+
+    @Column(name = "fission0_read_avg_rate")
+    private Double fission0ReadAvgRate = 0.0;
+
+    @Column(name = "his_read_rate")
+    private Double hisReadRate = 0.0;
+
+    @Column(name = "his_first_read_rate")
+    private Double hisFirstReadRate = 0.0;
+
+    @Column(name = "his_fission0_first_rate")
+    private Double hisFission0FirstRate = 0.0;
+
+    @Column(name = "position")
+    private Integer position;
+
+    @Column(name = "gh_id")
+    private String ghId;
+
+    @Column(name = "title")
+    private String title;
+
+    @Column(name = "link")
+    private String link;
+
+    @Column(name = "crawler_plan_name")
+    private String crawlerPlanName;
+
+    @Column(name = "produce_plan_name")
+    private String producePlanName;
+
+    @Column(name = "publish_plan_name")
+    private String publishPlanName;
+
+    @Column(name = "source_produce_plan_name")
+    private String sourceProducePlanName;
+}

+ 19 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/CategoryGroupDataExport.java

@@ -0,0 +1,19 @@
+package com.tzld.longarticle.recommend.server.model.vo;
+
+import lombok.Data;
+
+@Data
+public class CategoryGroupDataExport {
+    private String dateStr;
+    private String position;
+    private String category;
+    private Integer publishTimes = 0;
+    private Long fansCount = 0L;
+    private Integer viewCount = 0;
+    private Integer firstLevel = 0;
+    private Integer fission0 = 0;
+    private Double readRate;
+    private Double openRate;
+    private Double fissionRate;
+    private Double openFansRate;
+}

+ 26 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/CategoryGroupFunnelExport.java

@@ -0,0 +1,26 @@
+package com.tzld.longarticle.recommend.server.model.vo;
+
+import lombok.Data;
+
+@Data
+public class CategoryGroupFunnelExport {
+    private String dateStr;
+    private String category;
+    private Long matchCount;
+    private Long matchSuccessCount;
+    private Double matchSuccessRate;
+    private Long videoAuditCount;
+    private Double auditRate;
+    private Long videoAuditPassCount;
+    private Double videoAuditPassRate;
+    private Double supplyDemandProportion;
+    private Double auditPassTotalProportion;
+    private Double auditPassSupplyDemandRate;
+    private Double readRate;
+    private Double openRate;
+    private Double fissionRate;
+    private Double readOpenFissionRate = 0.0;
+    private Double passReadOpenFissionRate = 0.0;
+    private Double viewCountProportion;
+
+}

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

@@ -8,6 +8,7 @@ public class ContentEffectGroupBySourceExport {
     private String type;
     private String source;
     private String isSameMiniprogram;
+    private String position = "SUM";
     private Integer fansCount = 0;
     private Integer publishContentCount = 0;
     private Integer readCount = 0;

+ 61 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/CoverStatisticDataExport.java

@@ -0,0 +1,61 @@
+package com.tzld.longarticle.recommend.server.model.vo;
+
+import lombok.Data;
+
+@Data
+public class CoverStatisticDataExport {
+
+    public CoverStatisticDataExport() {
+
+    }
+
+    public CoverStatisticDataExport(String dateStr, String channel, String group, String index) {
+        this.dateStr = dateStr;
+        this.channel = channel;
+        this.index = index;
+        this.group = group;
+
+    }
+
+    private String dateStr;
+
+    private String channel;
+
+    private String group;
+
+    private String index;
+
+    private Integer fans = 0;
+
+    private Integer publishCount = 0;
+
+    private Integer showViewCount = 0;
+
+    private Double showViewRate = 0.0;
+
+    private Integer allFirstLevel = 0;
+
+    private Integer oneFirstLevel = 0;
+
+    private Integer twoFirstLevel = 0;
+
+    private Double allFirstLevelRate = 0.0;
+
+    private Double oneFirstLevelRate = 0.0;
+
+    private Double twoFirstLevelRate = 0.0;
+
+    private Integer allFission0 = 0;
+
+    private Integer oneFission0 = 0;
+
+    private Integer twoFission0 = 0;
+
+    private Double allFission0Rate = 0.0;
+
+    private Double oneFission0Rate = 0.0;
+
+    private Double twoFission0Rate = 0.0;
+
+
+}

+ 51 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/CoverStatisticOneDataExport.java

@@ -0,0 +1,51 @@
+package com.tzld.longarticle.recommend.server.model.vo;
+
+import lombok.Data;
+
+@Data
+public class CoverStatisticOneDataExport {
+
+    private String dateStr;
+
+    private String channel;
+
+    private String accountName;
+
+    private String title;
+
+    private String contentUrl;
+
+    private String index;
+
+    private Integer fans = 0;
+
+    private Integer showViewCount = 0;
+
+    private Double showViewRate = 0.0;
+
+    private Integer allFirstLevel = 0;
+
+    private Integer oneFirstLevel = 0;
+
+    private Integer twoFirstLevel = 0;
+
+    private Double allFirstLevelRate = 0.0;
+
+    private Double oneFirstLevelRate = 0.0;
+
+    private Double twoFirstLevelRate = 0.0;
+
+    private Integer allFission0 = 0;
+
+    private Integer oneFission0 = 0;
+
+    private Integer twoFission0 = 0;
+
+    private Double allFission0Rate = 0.0;
+
+    private Double oneFission0Rate = 0.0;
+
+    private Double twoFission0Rate = 0.0;
+
+
+}

+ 16 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/model/vo/DailyAuditProduceExport.java

@@ -0,0 +1,16 @@
+package com.tzld.longarticle.recommend.server.model.vo;
+
+import lombok.Data;
+
+@Data
+public class DailyAuditProduceExport {
+    private String dateStr;
+    private String type;
+    private String name;
+    private Integer auditCount = 0;
+    private Integer auditPassCount = 0;
+    private Double auditPassRate = 0.0;
+    private Integer videoAuditCount = 0;
+    private Integer videoAuditPassCount = 0;
+    private Double videoAuditPassRate = 0.0;
+}

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

@@ -20,7 +20,7 @@ public class NewSortStrategyExport {
     private String businessType;
     private String accountName;
     private String strategy;
-    private long fans;
+    private Integer fans;
 //    private Integer viewCount;
     private Double avgViewCount;
 //    private Integer firstViewCount;

+ 1 - 1
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/aigc/ProducePlanExeRecordRepository.java

@@ -9,7 +9,7 @@ import java.util.List;
 @Repository
 public interface ProducePlanExeRecordRepository extends JpaRepository<ProducePlanExeRecord, String> {
 
-    List<ProducePlanExeRecord> findByPlanExeIdIn(List<String> planExeIds);
+    List<ProducePlanExeRecord> getByPlanExeIdIn(List<String> planExeIds);
 
     ProducePlanExeRecord getByPlanExeId(String contentId);
 }

+ 2 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/aigc/PublishPlanRepository.java

@@ -10,4 +10,6 @@ import java.util.List;
 public interface PublishPlanRepository extends JpaRepository<PublishPlan, String> {
 
     List<PublishPlan> findByIdIn(List<String> planIds);
+
+    PublishPlan getById(String planId);
 }

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

@@ -4,10 +4,15 @@ import com.tzld.longarticle.recommend.server.model.entity.crawler.ArticleDetailI
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 
+import java.util.Date;
 import java.util.List;
 
 @Repository
 public interface ArticleDetailInfoRepository extends JpaRepository<ArticleDetailInfo, ArticleDetailInfo.PK> {
 
     List<ArticleDetailInfo> getByWxSnIn(List<String> wxSnList);
+
+    List<ArticleDetailInfo> findByWxSnInAndPublishDtBetween(List<String> wxSnList, Date startDate, Date endDate);
+
+    List<ArticleDetailInfo> findByWxSnEqualsAndPublishDtBetween(String wxSn, Date startDate, Date endDate);
 }

+ 3 - 1
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/longArticle/DatastatSortStrategyRepository.java

@@ -7,10 +7,12 @@ import org.springframework.stereotype.Repository;
 import java.util.List;
 
 @Repository
-public interface DatastatSortStrategyRepository extends JpaRepository<DatastatSortStrategy, DatastatSortStrategy.PK> {
+public interface DatastatSortStrategyRepository extends JpaRepository<DatastatSortStrategy, Long> {
 
     List<DatastatSortStrategy> getByViewCountGreaterThanEqualAndReadRateGreaterThanEqualAndPositionIn(Integer viewCount,
                                                                                                       Double readRate,
                                                                                                       List<Integer> positionList);
     List<DatastatSortStrategy> getByWxSnIn(List<String> wxSnList);
+
+    List<DatastatSortStrategy> getByDateStr(String dateStr);
 }

+ 7 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/longArticle/LongArticlesNewVideoCoverRepository.java

@@ -0,0 +1,7 @@
+package com.tzld.longarticle.recommend.server.repository.longArticle;
+
+import com.tzld.longarticle.recommend.server.model.entity.longArticle.LongArticlesNewVideoCover;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface LongArticlesNewVideoCoverRepository extends JpaRepository<LongArticlesNewVideoCover, Long> {
+}

+ 11 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/longArticle/LongArticlesPublishMiniprogramRepository.java

@@ -0,0 +1,11 @@
+package com.tzld.longarticle.recommend.server.repository.longArticle;
+
+import com.tzld.longarticle.recommend.server.model.entity.longArticle.LongArticlesPublishMiniprogram;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface LongArticlesPublishMiniprogramRepository extends JpaRepository<LongArticlesPublishMiniprogram, Long> {
+
+    LongArticlesPublishMiniprogram findByRootSourceIdEquals(String rootSourceId);
+}

+ 10 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/repository/longArticle/PublishDailyReportRepository.java

@@ -0,0 +1,10 @@
+package com.tzld.longarticle.recommend.server.repository.longArticle;
+
+import com.tzld.longarticle.recommend.server.model.entity.longArticle.PublishDailyReport;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface PublishDailyReportRepository extends JpaRepository<PublishDailyReport, Long> {
+
+}

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

@@ -98,7 +98,7 @@ public class ArticleCategoryService {
         // 晋级 生成计划 添加品类处理任务
         addPromotionArticleCategoryByProducePlan();
         // 视频内容池 添加品类处理任务
-        addVideoPoolArticleCategory();
+//        addVideoPoolArticleCategory();
         // 调用大模型进行内容分类
         dealArticleCategory();
     }

+ 118 - 10
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/ArticlePromotionService.java

@@ -44,6 +44,7 @@ import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.handler.annotation.XxlJob;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.math3.distribution.NormalDistribution;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -52,7 +53,9 @@ import org.springframework.util.StringUtils;
 
 import java.net.URLDecoder;
 import java.util.*;
+import java.util.function.Function;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @Service
 @Slf4j
@@ -88,27 +91,131 @@ public class ArticlePromotionService {
     AigcBaseMapper aigcBaseMapper;
     @Autowired
     VideoTitleReWriteRepository videoTitleReWriteRepository;
-
     @ApolloJsonValue("${articlePromotionProduceConfig:{}}")
     private Map<String, Map<String, Map<String, String>>> produceConfig;
     @Value("${topProducePlanId:}")
     private String topProducePlanId;
+    @Value("${readOpenFissionRateThreshold:}")
+    private Double readOpenFissionRateThreshold;
 
     private final List<String> contentPoolType = Arrays.asList("autoArticlePoolLevel1", "autoArticlePoolLevel3", "autoArticlePoolLevel4");
 
+    /**
+     *
+     * @param readAvg:对照组分子
+     * @param fansA:对照组分母
+     * @param viewCount: 实验组分子
+     * @param fansB: 实验组分母
+     * @param confidence: 置信度
+     * @return boolean: true / false
+     */
+    public static boolean isExperimentGroupStatisticallySuperior(double readAvg, int fansA, double viewCount, int fansB, double confidence) {
+
+        if (fansA <= 0 || fansB <= 0) {
+            throw new IllegalArgumentException("样本量必须大于零");
+        }
+        if (confidence <= 0 || confidence >= 1) {
+            throw new IllegalArgumentException("置信水平必须在 (0,1) 区间");
+        }
+        double pA = readAvg / fansA;
+        double pB = viewCount / fansB;
+
+        if (pA <= 0) {
+            throw new IllegalArgumentException("A组比例必须大于零");
+        }
+        double varA = pA * (1 - pA) / fansA;
+        double varB = pB * (1 - pB) / fansB;
+        double se = Math.sqrt(varA + varB);
+
+        NormalDistribution normal = new NormalDistribution();
+        double alpha = (1 - confidence) / 2;
+        double zThreshold = normal.inverseCumulativeProbability(1 - alpha);
+        double ciLow = (pB - pA - zThreshold * se) / pA;
+        return ciLow > 0;
+    }
+
+    private Double nullSafe(Double value) {
+        return value != null ? value : 0.0;
+    }
+
+    private Integer nullSafe(Integer value) {
+        return value != null ? value : 0;
+    }
+
+    public List<DatastatSortStrategy> promotionWithReadOpenFissionRate(double threshold, List<DatastatSortStrategy> list) {
+        if (CollectionUtils.isEmpty(list)) {
+            throw new IllegalArgumentException("List cannot be empty");
+        }
+
+        return list.stream().filter(o -> {
+            double denominator =
+                    nullSafe(o.getFirstLevel()) +
+                            nullSafe(o.getSecondFirstLevel()) +
+                            nullSafe(o.getThirdFirstLevel());
+            if (denominator == 0) return false;
+
+            double numerator =
+                    nullSafe(o.getFission0()) + nullSafe(o.getFission1()) + nullSafe(o.getFission2()) +
+                            nullSafe(o.getSecondFission0()) + nullSafe(o.getSecondFission1()) + nullSafe(o.getSecondFission2()) +
+                            nullSafe(o.getThirdFission0()) + nullSafe(o.getThirdFission1()) + nullSafe(o.getThirdFission2());
+
+            double readRate = nullSafe(o.getReadRate());
+            double firstReadRate = nullSafe(o.getFirstReadRate());
+            // 计算 log10(阅读量)
+            double logViewCount = (nullSafe(o.getViewCount()) > 0) ? Math.log10(nullSafe(o.getViewCount())) : 0;
+            // 计算完整公式  score = 阅读均值倍数 * 阅读率 * T2裂变率 * log10(阅读量)
+            double result = readRate * firstReadRate * (numerator / denominator) * logViewCount;
+            return result > threshold;
+        }).collect(Collectors.toList());
+    }
+
     public void articlePromotion(String pos, String way, String accountNickName, String tag,
                                  Integer viewCountFilter, Double viewCountRateFilter, List<Integer> positionFilter) {
         String today = DateUtils.getCurrentDateStr("yyyyMMdd");
         String dateStrFilter = DateUtils.getBeforeDaysDateStr("yyyyMMdd", 10);
         // 获取内部表现
-        List<DatastatSortStrategy> list = longArticleBaseMapper.getArticlePromotion(viewCountFilter, viewCountRateFilter,
-                10000, dateStrFilter, positionFilter);
+        List<DatastatSortStrategy> list;
+        list = longArticleBaseMapper.getArticlePromotionCandidates(10000, dateStrFilter, positionFilter);
+        // 使用阅读均值倍数+阅读量晋级
+        List<DatastatSortStrategy> listStrategyV1 = list.stream()
+                .filter(o -> o.getReadRate() >= viewCountRateFilter && o.getViewCount() >= viewCountFilter)
+                .collect(Collectors.toList());
+        List<String> StrategyV1WxSn = listStrategyV1.stream().map(DatastatSortStrategy::getWxSn).collect(Collectors.toList());
+        List<DatastatSortStrategy> listStrategyV2;
+        if (pos.equals("【2】")) {
+            // 使用显著性检验晋级
+            listStrategyV2 = list.stream()
+                    .filter(o -> {
+                        try {
+                            return isExperimentGroupStatisticallySuperior(o.getAvgViewCount() * 1.1 * 30, o.getFans() * 30,
+                                    o.getViewCount(), o.getFans(), 0.95);
+                        }
+                        catch (Exception e) {
+                            log.error("显著性检验, 出现异常: {}", e.getMessage());
+                            return false;
+                        }
+                    })
+                    .collect(Collectors.toList());
+        } else {
+            // 使用新规则过滤
+            listStrategyV2 = promotionWithReadOpenFissionRate(readOpenFissionRateThreshold, list);
+        }
+        listStrategyV2 = listStrategyV2.stream().filter(o -> !StrategyV1WxSn.contains(o.getWxSn())).collect(Collectors.toList());
+
+        //策略 1 晋级
+        filterAndAdd2CrawlerPlan(listStrategyV1, "策略V1", accountNickName, tag, pos, way, today);
+
+        //策略 2 晋级
+        filterAndAdd2CrawlerPlan(listStrategyV2, "策略V2", accountNickName, tag, pos, way, today);
+    }
+
+    private void filterAndAdd2CrawlerPlan(List<DatastatSortStrategy> list, String promotionStrategy, String accountNickName, String tag, String pos, String way, String today) {
         list = filterEarlyContent(list, true);
         log.info("优质{}文章数量: {}", accountNickName, list.size());
         List<DatastatSortStrategy> distinct = filterSameTitle(list);
         distinct.sort(Comparator.comparing(DatastatSortStrategy::getDateStr, Comparator.reverseOrder()));
         log.info("优质{}文章数量(去重后): {}", accountNickName, distinct.size());
-        addUrlListToAccount(accountNickName, distinct, pos, way, today, tag);
+        addUrlListToAccount(accountNickName, distinct, pos, way, today, tag, promotionStrategy);
     }
 
     private List<DatastatSortStrategy> filterEarlyContent(List<DatastatSortStrategy> list, Boolean filterVideoPool) {
@@ -147,7 +254,7 @@ public class ArticlePromotionService {
     }
 
     private void addUrlListToAccount(String accountNickName, List<DatastatSortStrategy> list, String pos, String way,
-                                     String today, String tag) {
+                                     String today, String tag, String promotionStrategy) {
         List<String> urlList = list.stream().map(DatastatSortStrategy::getLink).collect(Collectors.toList());
         if (!produceConfig.containsKey(accountNickName)) {
             log.info("account_nickname not in produceConfig: " + accountNickName);
@@ -194,7 +301,7 @@ public class ArticlePromotionService {
                 }
             }
             if (CollectionUtils.isNotEmpty(filterUrlList)) {
-                String planName = String.format("%d_%s_%s_%s【%s】_%s", filterUrlList.size(), today, accountNickName, pos, way, today);
+                String planName = String.format("%d_%s_%s_%s【%s】_%s_%s", filterUrlList.size(), today, accountNickName, pos, way, today, promotionStrategy);
                 log.info("url_len: " + list.size() + ", " + filterUrlList.size());
                 IdNameVO<String> planInfo = aigcCrawlerPlanSaveService.createArticleUrlPlan(planName, filterUrlList, tag, CrawlerModeEnum.ContentIDs.getVal());
                 if (StringUtils.hasText(produceId)) {
@@ -204,14 +311,14 @@ public class ArticlePromotionService {
                 log.info("{}, {}, produce plan not exist: {}, {}, {}", planInfo.getName(), planInfo.getId(), accountNickName, pos, way);
             }
             if (CollectionUtils.isNotEmpty(publishContentIds)) {
-                String planName = String.format("%d_%s_%s_%s【%s】_%s", publishContentIds.size(), today, accountNickName, pos, way, today);
+                String planName = String.format("%d_%s_%s_%s【%s】_%s_%s", publishContentIds.size(), today, accountNickName, pos, way, today, promotionStrategy);
                 IdNameVO<String> planInfo = aigcCrawlerPlanSaveService.createArticleUrlPlan(planName, publishContentIds, tag, CrawlerModeEnum.PublishContentIds.getVal());
                 if (StringUtils.hasText(produceId)) {
                     String inputSourceLabel = String.format("原始帖子-长文-微信公众号-内容添加计划-%s", planInfo.getName());
                     articleAddDependPlan(produceId, planInfo.getId(), inputSourceLabel, ProducePlanInputSourceTypeEnum.contentPlan.getVal());
                 }
             }
-            sendFeishuJobFinishMessage(accountNickName, filterUrlList.size(), publishContentIds.size());
+            sendFeishuJobFinishMessage(accountNickName, filterUrlList.size(), publishContentIds.size(), promotionStrategy);
         } catch (Exception e) {
             log.error("articlePromotion error: ", e);
             FeishuMessageSender.sendWebHookMessage(FeishuRobotIdEnum.DAILY.getRobotId(),
@@ -222,13 +329,14 @@ public class ArticlePromotionService {
         }
     }
 
-    private void sendFeishuJobFinishMessage(String accountNickName, Integer urlListSize, Integer contentListSize) {
+    private void sendFeishuJobFinishMessage(String accountNickName, Integer urlListSize, Integer contentListSize, String promotionStrategy) {
         log.info("articlePromotion finish: 晋级任务:{}, id晋级数量:{}, url晋级数量:{}", accountNickName, contentListSize, urlListSize);
         FeishuMessageSender.sendWebHookMessage(FeishuRobotIdEnum.DAILY.getRobotId(),
                 "【文章晋级job完成】\n" +
                         "晋级任务:" + accountNickName + "\n" +
                         "id晋级数量:" + contentListSize + "\n" +
-                        "url晋级数量:" + urlListSize + "\n");
+                        "url晋级数量:" + urlListSize + "\n" +
+                        "晋级策略: " + promotionStrategy + "\n");
     }
 
     private List<ProduceContentListItemVO> getProduceContentList(String accountNickName, String pos, String way) {

+ 21 - 5
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/ArticleVideoAuditService.java

@@ -72,6 +72,9 @@ public class ArticleVideoAuditService {
     @ApolloJsonValue("${daily.article.audit.pool.count.config:{}}")
     private Map<String, Integer> dailyAuditPoolCount;
 
+    @ApolloJsonValue("${highPriorityProducePlan:[]}")
+    private List<String> highPriorityProducePlan;
+
     public Page<ArticleVideoAuditListVO> list(ArticleVideoAuditListParam param) {
         int offset = (param.getPageNum() - 1) * param.getPageSize();
         int count = articleAuditMapper.articleVideoAuditListCount(param.getContentId(), param.getStatus(),
@@ -172,7 +175,7 @@ public class ArticleVideoAuditService {
             } else {
                 // 从待审核队列中获取数据, 如获取到的内容已审核,则重新获取
                 while (true) {
-                    Set<String> ids = redisTemplate.opsForZSet().reverseRangeByScore(auditQueueRedisKey, 0, 100, 0, 1);
+                    Set<String> ids = redisTemplate.opsForZSet().reverseRangeByScore(auditQueueRedisKey, 0, 1000, 0, 1);
                     if (CollectionUtils.isNotEmpty(ids)) {
                         id = ids.iterator().next();
                         redisTemplate.opsForZSet().remove(auditQueueRedisKey, id);
@@ -475,6 +478,18 @@ public class ArticleVideoAuditService {
                 auditQueuePoolCountMap = auditQueueList.stream().collect(Collectors.groupingBy(item -> ContentPoolEnum.from(item.getFlowPoolLevel()).getContentPool()));
                 excludeContentIds.addAll(auditQueueIds);
             }
+            // 高优先级计划内容添加
+            if (CollectionUtils.isNotEmpty(highPriorityProducePlan)) {
+                List<ArticleVideoAuditListVO> addList = articleAuditMapper.articleVideoWaitingAuditList(
+                        Arrays.asList(ArticleVideoAuditStatusEnum.WAITING.getCode()),
+                        null, excludeContentIds, highPriorityProducePlan, 100);
+                if (CollectionUtils.isNotEmpty(addList)) {
+                    for (ArticleVideoAuditListVO item : addList) {
+                        redisTemplate.opsForZSet().add(auditQueueRedisKey, item.getContentId(), 101);
+                        excludeContentIds.add(item.getContentId());
+                    }
+                }
+            }
             // 每日配置发送量不足添加
             for (Map.Entry<String, Integer> entry : dailyAuditPoolCount.entrySet()) {
                 ContentPoolEnum poolEnum = ContentPoolEnum.from(entry.getKey());
@@ -485,9 +500,9 @@ public class ArticleVideoAuditService {
                     List<LongArticleTitleAudit> inAuditListPool = inAuditListPoolCountMap.getOrDefault(poolEnum.getContentPool(), new ArrayList<>());
                     int needCount = entry.getValue() - (totalCount + auditQueueList.size() + inAuditListPool.size());
                     if (needCount > 0) {
-                        List<ArticleVideoAuditListVO> addList = articleAuditMapper.articleVideoWatingAuditList(
+                        List<ArticleVideoAuditListVO> addList = articleAuditMapper.articleVideoWaitingAuditList(
                                 Arrays.asList(ArticleVideoAuditStatusEnum.WAITING.getCode()),
-                                Arrays.asList(poolEnum.getContentPool()), excludeContentIds, needCount);
+                                Arrays.asList(poolEnum.getContentPool()), excludeContentIds, null, needCount);
                         if (CollectionUtils.isNotEmpty(addList)) {
                             for (ArticleVideoAuditListVO item : addList) {
                                 redisTemplate.opsForZSet().add(auditQueueRedisKey, item.getContentId(), poolEnum.getWeight());
@@ -500,8 +515,9 @@ public class ArticleVideoAuditService {
             // 待发布内容不足添加
             Long auditQueueSize = redisTemplate.opsForZSet().size(auditQueueRedisKey);
             if (Objects.isNull(auditQueueSize) || auditQueueSize < 20) {
-                List<ArticleVideoAuditListVO> list = articleAuditMapper.articleVideoWatingAuditList(
-                        Arrays.asList(ArticleVideoAuditStatusEnum.WAITING.getCode()), null, excludeContentIds, 40);
+                List<ArticleVideoAuditListVO> list = articleAuditMapper.articleVideoWaitingAuditList(
+                        Arrays.asList(ArticleVideoAuditStatusEnum.WAITING.getCode()), null,
+                        excludeContentIds, null, 40);
                 if (CollectionUtils.isNotEmpty(list)) {
                     for (ArticleVideoAuditListVO item : list) {
                         ContentPoolEnum poolEnum = ContentPoolEnum.from(item.getFlowPoolLevel());

+ 1216 - 96
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/DataDashboardService.java

@@ -6,6 +6,7 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.google.common.collect.Lists;
+import com.tzld.longarticle.recommend.server.common.enums.ContentPositionEnum;
 import com.tzld.longarticle.recommend.server.common.enums.StatusEnum;
 import com.tzld.longarticle.recommend.server.common.enums.aigc.ChannelEnum;
 import com.tzld.longarticle.recommend.server.common.enums.aigc.ProduceContentAuditStatusEnum;
@@ -13,10 +14,7 @@ import com.tzld.longarticle.recommend.server.common.enums.aigc.PublishPlanInputS
 import com.tzld.longarticle.recommend.server.common.enums.longArticle.ArticleVideoAuditStatusEnum;
 import com.tzld.longarticle.recommend.server.common.enums.longArticle.ArticleVideoBadStatusEnum;
 import com.tzld.longarticle.recommend.server.common.enums.longArticle.VideoPoolPlatformEnum;
-import com.tzld.longarticle.recommend.server.common.enums.recommend.AccountBusinessTypeEnum;
-import com.tzld.longarticle.recommend.server.common.enums.recommend.ArticleTypeEnum;
-import com.tzld.longarticle.recommend.server.common.enums.recommend.ContentPoolEnum;
-import com.tzld.longarticle.recommend.server.common.enums.recommend.RankStrategyEnum;
+import com.tzld.longarticle.recommend.server.common.enums.recommend.*;
 import com.tzld.longarticle.recommend.server.mapper.aigc.AigcBaseMapper;
 import com.tzld.longarticle.recommend.server.mapper.aigc.PublishContentMapper;
 import com.tzld.longarticle.recommend.server.mapper.crawler.CrawlerBaseMapper;
@@ -46,13 +44,16 @@ import com.tzld.longarticle.recommend.server.repository.longArticle.*;
 import com.tzld.longarticle.recommend.server.service.recommend.score.ScoreStrategy;
 import com.tzld.longarticle.recommend.server.util.DateUtils;
 import com.tzld.longarticle.recommend.server.util.MapBuilder;
+import com.tzld.longarticle.recommend.server.util.Md5Util;
 import com.tzld.longarticle.recommend.server.util.feishu.FeiShu;
 import com.xxl.job.core.biz.model.ReturnT;
 import com.xxl.job.core.handler.annotation.XxlJob;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.MapUtils;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.util.Pair;
 import org.springframework.http.*;
 import org.springframework.stereotype.Service;
@@ -60,6 +61,8 @@ import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
 
 import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -114,11 +117,20 @@ public class DataDashboardService {
     private LongArticlesRootSourceIdRepository rootSourceIdRepository;
     @Autowired
     private DatastatSortStrategyRepository datastatSortStrategyRepository;
+    @Autowired
+    private LongArticlesPublishMiniprogramRepository longArticlesPublishMiniprogramRepository;
+    @Autowired
+    private LongArticlesNewVideoCoverRepository longArticlesNewVideoCoverRepository;
+    @Autowired
+    private PublishDailyReportRepository publishDailyReportRepository;
 
     @ApolloJsonValue("${export.account.ghId:[]}")
     private static List<String> ghIdList;
     @ApolloJsonValue("${audit.producePlan.list:[]}")
     private static List<String> auditProducePlanList;
+
+    @Value("${category.active.version:1}")
+    private Integer activeVersion;
     private static final String dailyDetailSheetToken = "M0pLs3uF6hfL0htn2dMcB9eFn8e";
     private static final String dailySafeSheetToken = "Xoass3ZGYhi9BVtZDoMclGFpnYe";
 
@@ -140,6 +152,28 @@ public class DataDashboardService {
         return ReturnT.SUCCESS;
     }
 
+    @XxlJob("scheduledExportMysql")
+    public ReturnT<String> scheduledExportMysql(String param) {
+        if (!StringUtils.hasText(param)) {
+            return ReturnT.FAIL;
+        }
+        JSONObject jsonObject = JSONObject.parseObject(param);
+        String minDate = jsonObject.getString("minDate");
+        String maxDate = jsonObject.getString("maxDate");
+        List<NewSortStrategyExport> newContentsYesData = newSortStrategyData(minDate, maxDate,
+                ArticleTypeEnum.QUNFA.getVal(), StatusEnum.ZERO.getCode());
+        if (CollectionUtils.isNotEmpty(newContentsYesData)) {
+            List<PublishDailyReport> reports = new ArrayList<>();
+            for (NewSortStrategyExport newSortStrategyExport : newContentsYesData) {
+                PublishDailyReport publishDailyReport = new PublishDailyReport();
+                BeanUtils.copyProperties(newSortStrategyExport, publishDailyReport);
+                reports.add(publishDailyReport);
+            }
+            publishDailyReportRepository.saveAll(reports);
+        }
+        return ReturnT.SUCCESS;
+    }
+
     public void exportWuXianLiu(String beginDate, String endDate) {
         List<String> dateStrList = DateUtils.getBeforeDays(beginDate, endDate, 5);
         exportFeishuNewSortStrategy(dateStrList, ArticleTypeEnum.WUXIANLIU.getVal(), StatusEnum.ONE.getCode(),
@@ -284,7 +318,7 @@ public class DataDashboardService {
                 .map(PublishContentDTO::getSourceId).distinct().collect(Collectors.toList());
         List<ProducePlanExeRecord> planExeRecordList = new ArrayList<>();
         for (List<String> partitions : Lists.partition(contentSourceIds, 1000)) {
-            planExeRecordList.addAll(producePlanExeRecordRepository.findByPlanExeIdIn(partitions));
+            planExeRecordList.addAll(producePlanExeRecordRepository.getByPlanExeIdIn(partitions));
         }
         log.info("newSortStrategyData planExeRecordList finish");
         List<String> videoPoolSourceIds = publishContents.stream()
@@ -528,8 +562,8 @@ public class DataDashboardService {
                 ProducePlan producePlan = producePlanMap.get(record.getPlanId());
                 obj.setProducePlanName(producePlan.getName());
                 obj.setProducePlanTag(producePlan.getPlanTag());
+                producePlanInputSourceList = inputSourceMap.get(record.getPlanId());
             }
-            producePlanInputSourceList = inputSourceMap.get(record.getPlanId());
         }
         if (publishContent.getSourceType().equals(PublishPlanInputSourceTypesEnum.longArticleVideoPoolSource.getVal())) {
             PublishSingleVideoSource videoPoolSource = videoPoolSourceMap.get(publishContent.getSourceId());
@@ -545,18 +579,25 @@ public class DataDashboardService {
             }
         }
         List<CrawlerPlanResultRel> crawlerPlanRelList = resultRelMap.get(publishContent.getCrawlerChannelContentId());
-        if (CollectionUtil.isNotEmpty(crawlerPlanRelList) && CollectionUtil.isNotEmpty(producePlanInputSourceList)) {
+        if (CollectionUtils.isNotEmpty(crawlerPlanRelList) && CollectionUtils.isNotEmpty(producePlanInputSourceList)) {
             List<String> inputSourceValues = producePlanInputSourceList.stream()
                     .map(ProducePlanInputSource::getInputSourceValue).collect(Collectors.toList());
-            List<CrawlerPlan> crawlerPlanItemList = new ArrayList<>();
-            for (CrawlerPlanResultRel crawlerPlanResultRel : crawlerPlanRelList) {
-                crawlerPlanItemList.add(crawlerPlanMap.get(crawlerPlanResultRel.getPlanId()));
-            }
-            for (CrawlerPlan crawlerPlan : crawlerPlanItemList) {
-                if (inputSourceValues.contains(crawlerPlan.getId())) {
-                    obj.setCrawlerPlanName(crawlerPlan.getName());
-                    obj.setCrawlerPlanTag(crawlerPlan.getPlanTag());
-                    break;
+            if (CollectionUtils.isNotEmpty(inputSourceValues)) {
+                List<CrawlerPlan> crawlerPlanItemList = new ArrayList<>();
+                for (CrawlerPlanResultRel crawlerPlanResultRel : crawlerPlanRelList) {
+                    CrawlerPlan crawlerPlan = crawlerPlanMap.get(crawlerPlanResultRel.getPlanId());
+                    if (Objects.nonNull(crawlerPlan)) {
+                        crawlerPlanItemList.add(crawlerPlan);
+                    }
+                }
+                if (CollectionUtils.isNotEmpty(crawlerPlanItemList)) {
+                    for (CrawlerPlan crawlerPlan : crawlerPlanItemList) {
+                        if (inputSourceValues.contains(crawlerPlan.getId())) {
+                            obj.setCrawlerPlanName(crawlerPlan.getName());
+                            obj.setCrawlerPlanTag(crawlerPlan.getPlanTag());
+                            break;
+                        }
+                    }
                 }
             }
         }
@@ -2434,7 +2475,9 @@ public class DataDashboardService {
                                 Pair.of("公众号视频", "#F8E6AB"),
                                 Pair.of("好看视频", "#A9EFE6"),
                                 Pair.of("视频号视频", "#FDE2E2"),
-                                Pair.of("头条视频", "#ECE2FE")))
+                                Pair.of("头条视频", "#ECE2FE"),
+                                Pair.of("搜狐视频", "#D9F5D6"),
+                                Pair.of("票圈视频", "#F8DEF8")))
                 );
         doSendFeishuSheet(dateStrList, dailyDetailSheetToken, "6aW60b", rowNum, rows,
                 3, styles, null, thank);
@@ -2511,7 +2554,8 @@ public class DataDashboardService {
         }
         // 视频
         List<VideoPoolPlatformEnum> videoPoolType = Arrays.asList(VideoPoolPlatformEnum.GZH, VideoPoolPlatformEnum.HkSP,
-                VideoPoolPlatformEnum.SPH, VideoPoolPlatformEnum.TOUTIAO);
+                VideoPoolPlatformEnum.SPH, VideoPoolPlatformEnum.TOUTIAO, VideoPoolPlatformEnum.SOHU,
+                VideoPoolPlatformEnum.PQ);
         for (VideoPoolPlatformEnum videoType : videoPoolType) {
             ContentGroupFunnelExport item = ContentGroupFunnelExport.getDefault();
             item.setDateStr(dateStr);
@@ -2572,38 +2616,48 @@ public class DataDashboardService {
             result.add(item);
         }
         // sum
-        result.add(buildContentFunnelGroupSum(dateStr, result));
+        result.add(buildContentFunnelGroupSum(dateStr, result, "文章"));
+        result.add(buildContentFunnelGroupSum(dateStr, result, "视频"));
+        result.add(buildContentFunnelGroupSum(dateStr, result, "SUM"));
+        result.removeIf(o -> o.getCrawlerCount() == 0 && o.getProduceCount() == 0 && o.getProduceAuditCount() == 0
+                && o.getVideoPoolPQAuditCount() == 0 && o.getVideoAuditCount() == 0);
+        result.sort(Comparator.comparing(ContentGroupFunnelExport::getType));
         return result;
     }
 
-    private ContentGroupFunnelExport buildContentFunnelGroupSum(String dateStr, List<ContentGroupFunnelExport> result) {
+    private ContentGroupFunnelExport buildContentFunnelGroupSum(String dateStr, List<ContentGroupFunnelExport> result,
+                                                                String type) {
         ContentGroupFunnelExport sum = ContentGroupFunnelExport.getDefault();
-        sum.setType("SUM");
+        sum.setType(type);
         sum.setSource("SUM");
         sum.setDateStr(dateStr);
-        for (ContentGroupFunnelExport contentGroupFunnelExport : result) {
-            sum.setCrawlerCount(sum.getCrawlerCount() + contentGroupFunnelExport.getCrawlerCount());
-            sum.setProduceCount(sum.getProduceCount() + contentGroupFunnelExport.getProduceCount());
+        for (ContentGroupFunnelExport export : result) {
+            if ((!export.getType().equals(type) && !"SUM".equals(type))
+                    || export.getSource().equals("SUM")) {
+                continue;
+            }
+            sum.setCrawlerCount(sum.getCrawlerCount() + export.getCrawlerCount());
+            sum.setProduceCount(sum.getProduceCount() + export.getProduceCount());
             if (sum.getCrawlerCount() > 0) {
                 sum.setProduceRate(sum.getProduceCount() * 1.0 / sum.getCrawlerCount());
             }
-            sum.setProduceAuditCount(sum.getProduceAuditCount() + contentGroupFunnelExport.getProduceAuditCount());
-            sum.setProduceAuditPassCount(sum.getProduceAuditPassCount() + contentGroupFunnelExport.getProduceAuditPassCount());
+            sum.setProduceAuditCount(sum.getProduceAuditCount() + export.getProduceAuditCount());
+            sum.setProduceAuditPassCount(sum.getProduceAuditPassCount() + export.getProduceAuditPassCount());
             if (sum.getProduceAuditCount() > 0) {
                 sum.setProduceAuditPassRate(sum.getProduceAuditPassCount() * 1.0 / sum.getProduceAuditCount());
             }
-            sum.setMatchCount(sum.getMatchCount() + contentGroupFunnelExport.getMatchCount());
-            sum.setMatchSuccessCount(sum.getMatchSuccessCount() + contentGroupFunnelExport.getMatchSuccessCount());
+            sum.setMatchCount(sum.getMatchCount() + export.getMatchCount());
+            sum.setMatchSuccessCount(sum.getMatchSuccessCount() + export.getMatchSuccessCount());
             if (sum.getMatchCount() > 0) {
                 sum.setMatchSuccessRate(sum.getMatchSuccessCount() * 1.0 / sum.getMatchCount());
             }
-            sum.setVideoAuditCount(sum.getVideoAuditCount() + contentGroupFunnelExport.getVideoAuditCount());
-            sum.setVideoAuditPassCount(sum.getVideoAuditPassCount() + contentGroupFunnelExport.getVideoAuditPassCount());
+            sum.setVideoAuditCount(sum.getVideoAuditCount() + export.getVideoAuditCount());
+            sum.setVideoAuditPassCount(sum.getVideoAuditPassCount() + export.getVideoAuditPassCount());
             if (sum.getVideoAuditCount() > 0) {
                 sum.setVideoAuditPassRate(sum.getVideoAuditPassCount() * 1.0 / sum.getVideoAuditCount());
             }
-            sum.setVideoPoolPQAuditCount(sum.getVideoPoolPQAuditCount() + contentGroupFunnelExport.getVideoPoolPQAuditCount());
-            sum.setVideoPoolPQAuditPassCount(sum.getVideoPoolPQAuditPassCount() + contentGroupFunnelExport.getVideoPoolPQAuditPassCount());
+            sum.setVideoPoolPQAuditCount(sum.getVideoPoolPQAuditCount() + export.getVideoPoolPQAuditCount());
+            sum.setVideoPoolPQAuditPassCount(sum.getVideoPoolPQAuditPassCount() + export.getVideoPoolPQAuditPassCount());
             if (sum.getVideoPoolPQAuditCount() > 0) {
                 sum.setVideoPoolPQAuditPassRate(sum.getVideoPoolPQAuditPassCount() * 1.0 / sum.getVideoPoolPQAuditCount());
             }
@@ -2656,13 +2710,13 @@ public class DataDashboardService {
 
         List<Pair<String, String>> styles = Arrays
                 .asList(
-                        Pair.of("H", "0.00%"),
-                        Pair.of("L", "0.00%"),
+                        Pair.of("I", "0.00%"),
                         Pair.of("M", "0.00%"),
                         Pair.of("N", "0.00%"),
-                        Pair.of("R", "0.00%"),
+                        Pair.of("O", "0.00%"),
                         Pair.of("S", "0.00%"),
-                        Pair.of("T", "0.00%")
+                        Pair.of("T", "0.00%"),
+                        Pair.of("U", "0.00%")
                 );
         List<Pair<String, List<Pair<String, String>>>> thank = Arrays
                 .asList(
@@ -2681,11 +2735,18 @@ public class DataDashboardService {
                                 Pair.of("视频号视频", "#FDE2E2"),
                                 Pair.of("头条视频", "#ECE2FE"),
                                 Pair.of("文章SUM", "#D9F5D6"),
-                                Pair.of("视频SUM", "#F8DEF8"))),
+                                Pair.of("视频SUM", "#F8DEF8"),
+                                Pair.of("搜狐视频", "#EEF6C6"),
+                                Pair.of("票圈视频", "#BACEFD"))),
                         Pair.of("D", Arrays.asList(
                                 Pair.of("SUM", "#BACEFD"),
                                 Pair.of("相同", "#FED4A4"),
-                                Pair.of("不同", "#B1E8FC")))
+                                Pair.of("不同", "#B1E8FC"))),
+                        Pair.of("E", Arrays.asList(
+                                Pair.of("SUM", "#BACEFD"),
+                                Pair.of("头条", "#FED4A4"),
+                                Pair.of("次条", "#B1E8FC"),
+                                Pair.of("3-8", "#F8E6AB")))
                 );
         doSendFeishuSheet(dateStrList, dailyDetailSheetToken, "qvxJsD", rowNum, rows,
                 2, styles, null, thank);
@@ -2711,8 +2772,10 @@ public class DataDashboardService {
         List<String> rootSourceIdList = new ArrayList<>();
         for (Article article : articleList) {
             List<String> rootSourceIds = JSONArray.parseArray(article.getRootSourceIdList(), String.class);
-            rootSourceIdMap.put(article.getWxSn(), rootSourceIds);
-            rootSourceIdList.addAll(rootSourceIds);
+            if (CollectionUtil.isNotEmpty(rootSourceIds)) {
+                rootSourceIdMap.put(article.getWxSn(), rootSourceIds);
+                rootSourceIdList.addAll(rootSourceIds);
+            }
         }
         List<LongArticlesRootSourceId> longArticlesRootSourceIdList = rootSourceIdRepository.getByRootSourceIdIn(rootSourceIdList);
         Map<String, LongArticlesRootSourceId> longArticlesRootSourceIdMap = longArticlesRootSourceIdList.stream()
@@ -2722,11 +2785,49 @@ public class DataDashboardService {
         List<LongArticleCrawlerVideo> longArticleCrawlerVideoList = crawlerVideoRepository.getByContentIdInAndIsIllegal(contentIdList, 0);
         Map<String, List<LongArticleCrawlerVideo>> longArticleCrawlerVideoMap = longArticleCrawlerVideoList.stream()
                 .collect(Collectors.groupingBy(LongArticleCrawlerVideo::getContentId));
+        List<String> videoOssPathList = longArticleCrawlerVideoList.stream().map(LongArticleCrawlerVideo::getVideoOssPath)
+                .filter(StringUtils::hasText).collect(Collectors.toList());
+        List<PublishSingleVideoSource> singleVideoSourceList = videoPoolRepository.getByVideoOssPathIn(videoOssPathList);
+        Map<String, List<PublishSingleVideoSource>> singgleVideoSourceMap = singleVideoSourceList.stream()
+                .collect(Collectors.groupingBy(PublishSingleVideoSource::getVideoOssPath));
+        // 获取晋级
+        List<String> sourceIds = datastatSortStrategyList.stream().map(DatastatSortStrategy::getSourceId).distinct()
+                .collect(Collectors.toList());
+        List<ProducePlanExeRecord> exeRecordList = producePlanExeRecordRepository.getByPlanExeIdIn(sourceIds);
+        Map<String, String> sourceIdMap = exeRecordList.stream()
+                .collect(Collectors.toMap(ProducePlanExeRecord::getPlanExeId, ProducePlanExeRecord::getChannelContentId));
+        List<String> channelContentIds = exeRecordList.stream().map(ProducePlanExeRecord::getChannelContentId)
+                .collect(Collectors.toList());
+        List<ArticlePoolPromotionSource> promotionSourceList = articlePoolPromotionSourceRepository.getByChannelContentIdInAndStatusAndDeleted(
+                channelContentIds, ArticlePoolPromotionSourceStatusEnum.FINISH.getCode(), 0);
+        Map<String, String> promotionSourceMap = promotionSourceList.stream()
+                .collect(Collectors.toMap(ArticlePoolPromotionSource::getChannelContentId, ArticlePoolPromotionSource::getRootProduceContentId));
+        List<String> rootProduceContentIds = promotionSourceList.stream().map(ArticlePoolPromotionSource::getRootProduceContentId)
+                .collect(Collectors.toList());
+        // 视频溯源信息
+        List<LongArticleCrawlerVideo> promotionCrawlerVideoList = crawlerVideoRepository.getByContentIdInAndIsIllegal(rootProduceContentIds, 0);
+        Map<String, List<LongArticleCrawlerVideo>> promotionCrawlerVideoMap = promotionCrawlerVideoList.stream()
+                .collect(Collectors.groupingBy(LongArticleCrawlerVideo::getContentId));
+        List<String> promotionVideoOssPathList = promotionCrawlerVideoList.stream().map(LongArticleCrawlerVideo::getVideoOssPath)
+                .filter(StringUtils::hasText).collect(Collectors.toList());
+        List<PublishSingleVideoSource> promotionSingleVideoSourceList = videoPoolRepository.getByVideoOssPathIn(promotionVideoOssPathList);
+        Map<String, List<PublishSingleVideoSource>> promotionSinggleVideoSourceMap = promotionSingleVideoSourceList.stream()
+                .collect(Collectors.groupingBy(PublishSingleVideoSource::getVideoOssPath));
+        // 文章溯源信息
+        List<ProducePlanExeRecord> rootProduceContentList = producePlanExeRecordRepository.getByPlanExeIdIn(rootProduceContentIds);
+        Map<String, String> rootProduceContentMap = rootProduceContentList.stream()
+                .collect(Collectors.toMap(ProducePlanExeRecord::getPlanExeId, ProducePlanExeRecord::getPlanId));
+        List<String> rootPlanIdList = rootProduceContentList.stream().map(ProducePlanExeRecord::getPlanId).distinct().collect(Collectors.toList());
+        List<ProducePlan> producePlanList = producePlanRepository.findByIdIn(rootPlanIdList);
+        Map<String, ProducePlan> producePlanMap = producePlanList.stream().collect(Collectors.toMap(ProducePlan::getId, Function.identity()));
+
         for (Article article : articleList) {
             boolean isVideo = false;
             boolean isSameMiniProgram = false;
             String source = "";
             List<String> rootSourceIds = rootSourceIdMap.get(article.getWxSn());
+            List<ArticleDetailInfo> detailInfoList = articleDetailInfoMap.get(article.getWxSn());
+            DatastatSortStrategy datastatSortStrategy = datastatSortStrategyMap.get(article.getWxSn());
             if (CollectionUtil.isNotEmpty(rootSourceIds)) {
                 List<LongArticlesRootSourceId> rootSourceIdItemList = new ArrayList<>();
                 for (String rootSourceId : rootSourceIds) {
@@ -2736,21 +2837,58 @@ public class DataDashboardService {
                     }
                     rootSourceIdItemList.add(longArticlesRootSourceId);
                 }
+                if (CollectionUtils.isEmpty(rootSourceIdItemList)) {
+                    continue;
+                }
+                // 判断是否冷启视频内容池
                 String contentId = rootSourceIdItemList.get(0).getContentId();
-                List<LongArticleCrawlerVideo> crawlerVideoList = longArticleCrawlerVideoMap.get(contentId);
+                List<LongArticleCrawlerVideo> crawlerVideoList = longArticleCrawlerVideoMap.getOrDefault(contentId, new ArrayList<>());
                 List<String> videoOssPaths = crawlerVideoList.stream().map(LongArticleCrawlerVideo::getVideoOssPath)
                         .filter(StringUtils::hasText).collect(Collectors.toList());
-                List<PublishSingleVideoSource> singleVideoSources = videoPoolRepository.getByVideoOssPathIn(videoOssPaths);
+                List<PublishSingleVideoSource> singleVideoSources = new ArrayList<>();
+                for (String videoOssPath : videoOssPaths) {
+                    List<PublishSingleVideoSource> item = singgleVideoSourceMap.get(videoOssPath);
+                    if (CollectionUtil.isEmpty(item)) {
+                        continue;
+                    }
+                    singleVideoSources.addAll(item);
+                }
                 if (CollectionUtil.isNotEmpty(singleVideoSources)) {
                     isVideo = true;
                     source = VideoPoolPlatformEnum.from(singleVideoSources.get(0).getPlatform()).getDescription();
                 }
+                // 判断是否是晋级内容池
+                String channelContentId = null;
+                if (Objects.nonNull(datastatSortStrategy)) {
+                    channelContentId = sourceIdMap.get(datastatSortStrategy.getSourceId());
+                }
+                String rootProduceContentId = promotionSourceMap.get(channelContentId);
+                List<LongArticleCrawlerVideo> promotionCrawlerVideos = promotionCrawlerVideoMap.get(rootProduceContentId);
+                if (!isVideo && CollectionUtil.isNotEmpty(promotionCrawlerVideos)) {
+                    List<String> promotionVideoOssPaths = promotionCrawlerVideos.stream().map(LongArticleCrawlerVideo::getVideoOssPath)
+                            .filter(StringUtils::hasText).collect(Collectors.toList());
+                    List<PublishSingleVideoSource> promotionSingleVideoSources = new ArrayList<>();
+                    for (String videoOssPath : promotionVideoOssPaths) {
+                        List<PublishSingleVideoSource> item = promotionSinggleVideoSourceMap.get(videoOssPath);
+                        if (CollectionUtil.isEmpty(item)) {
+                            continue;
+                        }
+                        promotionSingleVideoSources.addAll(item);
+                    }
+                    if (CollectionUtil.isNotEmpty(promotionSingleVideoSources)) {
+                        isVideo = true;
+                        source = VideoPoolPlatformEnum.from(promotionSingleVideoSources.get(0).getPlatform()).getDescription();
+                    }
+                }
+                if (!isVideo) {
+                    String planId = rootProduceContentMap.get(rootProduceContentId);
+                    ProducePlan producePlan = producePlanMap.get(planId);
+                    source = getArticleSource(producePlan);
+                }
                 if (CollectionUtil.isNotEmpty(crawlerVideoList) && crawlerVideoList.size() == 1) {
                     isSameMiniProgram = true;
                 }
             }
-            List<ArticleDetailInfo> detailInfoList = articleDetailInfoMap.get(article.getWxSn());
-            DatastatSortStrategy datastatSortStrategy = datastatSortStrategyMap.get(article.getWxSn());
             if (isVideo) {
                 addContentEffectGroupBySourceRate(article, "视频", source, isSameMiniProgram, result, detailInfoList,
                         datastatSortStrategy);
@@ -2764,6 +2902,13 @@ public class DataDashboardService {
         }
 
         result.add(buildContentEffectGroupBySourceSum(dateStr, result));
+        Iterator<ContentEffectGroupBySourceExport> iterator = result.iterator();
+        while (iterator.hasNext()) {
+            ContentEffectGroupBySourceExport export = iterator.next();
+            if (export.getFansCount() == 0) {
+                iterator.remove();
+            }
+        }
         return result;
     }
 
@@ -2775,53 +2920,55 @@ public class DataDashboardService {
         if (Objects.isNull(datastatSortStrategy)) {
             return;
         }
-        if ("文章".equals(type) && "".equals(source)) {
-            source = getArticleSource(datastatSortStrategy);
-        }
         for (ContentEffectGroupBySourceExport export : result) {
             if ((export.getType().contains("SUM") && export.getType().contains(type)) || export.getType().equals(type)) {
                 if ((export.getIsSameMiniprogram().equals("相同") && isSameMiniProgram)
                         || (export.getIsSameMiniprogram().equals("不同") && !isSameMiniProgram)
                         || export.getIsSameMiniprogram().equals("SUM")) {
                     if (export.getSource().contains("SUM") || export.getSource().equals(source)) {
-                        if (Objects.nonNull(datastatSortStrategy.getFans())) {
-                            export.setFansCount(export.getFansCount() + datastatSortStrategy.getFans());
-                        }
-                        export.setPublishContentCount(export.getPublishContentCount() + 1);
-                        export.setReadCount(export.getReadCount() + article.getShowViewCount());
-                        if (CollectionUtil.isNotEmpty(detailInfoList)) {
-                            int totalFirstLevel = 0;
-                            int totalT0Fission = 0;
-                            int card1FirstLevel = 0;
-                            int card1T0Fission = 0;
-                            int card2FirstLevel = 0;
-                            int card2T0Fission = 0;
-                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
-                                if (Objects.isNull(articleDetailInfo.getFirstLevel())) {
-                                    continue;
-                                }
-                                if (articleDetailInfo.getVideoIndex() == 1) {
-                                    card1FirstLevel += articleDetailInfo.getFirstLevel();
-                                    if (Objects.nonNull(articleDetailInfo.getFission0())) {
-                                        card1T0Fission += articleDetailInfo.getFission0();
+                        if ((article.getItemIndex() == 1 && "头条".equals(export.getPosition()))
+                                || (article.getItemIndex() == 2 && "次条".equals(export.getPosition()))
+                                || (article.getItemIndex() > 2 && "3-8".equals(export.getPosition()))
+                                || export.getPosition().equals("SUM")) {
+                            if (Objects.nonNull(datastatSortStrategy.getFans())) {
+                                export.setFansCount(export.getFansCount() + datastatSortStrategy.getFans());
+                            }
+                            export.setPublishContentCount(export.getPublishContentCount() + 1);
+                            export.setReadCount(export.getReadCount() + article.getShowViewCount());
+                            if (CollectionUtil.isNotEmpty(detailInfoList)) {
+                                int totalFirstLevel = 0;
+                                int totalT0Fission = 0;
+                                int card1FirstLevel = 0;
+                                int card1T0Fission = 0;
+                                int card2FirstLevel = 0;
+                                int card2T0Fission = 0;
+                                for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                    if (Objects.isNull(articleDetailInfo.getFirstLevel())) {
+                                        continue;
+                                    }
+                                    if (articleDetailInfo.getVideoIndex() == 1) {
+                                        card1FirstLevel += articleDetailInfo.getFirstLevel();
+                                        if (Objects.nonNull(articleDetailInfo.getFission0())) {
+                                            card1T0Fission += articleDetailInfo.getFission0();
+                                        }
+                                    } else if (articleDetailInfo.getVideoIndex() == 2) {
+                                        card2FirstLevel += articleDetailInfo.getFirstLevel();
+                                        if (Objects.nonNull(articleDetailInfo.getFission0())) {
+                                            card2T0Fission += articleDetailInfo.getFission0();
+                                        }
                                     }
-                                } else if (articleDetailInfo.getVideoIndex() == 2) {
-                                    card2FirstLevel += articleDetailInfo.getFirstLevel();
+                                    totalFirstLevel += articleDetailInfo.getFirstLevel();
                                     if (Objects.nonNull(articleDetailInfo.getFission0())) {
-                                        card2T0Fission += articleDetailInfo.getFission0();
+                                        totalT0Fission += articleDetailInfo.getFission0();
                                     }
                                 }
-                                totalFirstLevel += articleDetailInfo.getFirstLevel();
-                                if (Objects.nonNull(articleDetailInfo.getFission0())) {
-                                    totalT0Fission += articleDetailInfo.getFission0();
-                                }
+                                export.setFirstLevel(export.getFirstLevel() + totalFirstLevel);
+                                export.setMiniprogram1FirstLevel(export.getMiniprogram1FirstLevel() + card1FirstLevel);
+                                export.setMiniprogram2FirstLevel(export.getMiniprogram2FirstLevel() + card2FirstLevel);
+                                export.setT0Fission(export.getT0Fission() + totalT0Fission);
+                                export.setMiniprogram1T0Fission(export.getMiniprogram1T0Fission() + card1T0Fission);
+                                export.setMiniprogram2T0Fission(export.getMiniprogram2T0Fission() + card2T0Fission);
                             }
-                            export.setFirstLevel(export.getFirstLevel() + totalFirstLevel);
-                            export.setMiniprogram1FirstLevel(export.getMiniprogram1FirstLevel() + card1FirstLevel);
-                            export.setMiniprogram2FirstLevel(export.getMiniprogram2FirstLevel() + card2FirstLevel);
-                            export.setT0Fission(export.getT0Fission() + totalT0Fission);
-                            export.setMiniprogram1T0Fission(export.getMiniprogram1T0Fission() + card1T0Fission);
-                            export.setMiniprogram2T0Fission(export.getMiniprogram2T0Fission() + card2T0Fission);
                         }
                     }
                 }
@@ -2829,9 +2976,9 @@ public class DataDashboardService {
         }
     }
 
-    private String getArticleSource(DatastatSortStrategy datastatSortStrategy) {
-        if (StringUtils.hasText(datastatSortStrategy.getProducePlanName())
-                && datastatSortStrategy.getProducePlanName().contains("头条")) {
+    private String getArticleSource(ProducePlan producePlan) {
+        if (Objects.nonNull(producePlan)
+                && producePlan.getName().contains("今日头条")) {
             return "头条文章";
         } else {
             return "公众号文章";
@@ -2843,15 +2990,19 @@ public class DataDashboardService {
         List<ContentEffectGroupBySourceExport> result = new ArrayList<>();
         List<String> isSameMiniprogram = Arrays.asList("相同", "不同", "SUM");
         List<String> articleSource = Arrays.asList("公众号文章", "头条文章");
-        List<String> videoSource = Arrays.asList("公众号视频", "好看视频", "视频号视频", "头条视频");
+        List<String> videoSource = Arrays.asList("公众号视频", "好看视频", "视频号视频", "头条视频", "搜狐视频", "票圈视频");
+        List<String> positionList = Arrays.asList("头条", "次条", "3-8");
         for (String source : articleSource) {
             for (String same : isSameMiniprogram) {
-                ContentEffectGroupBySourceExport item = new ContentEffectGroupBySourceExport();
-                item.setDateStr(dateStr);
-                item.setType("文章");
-                item.setSource(source);
-                item.setIsSameMiniprogram(same);
-                result.add(item);
+                for (String position : positionList) {
+                    ContentEffectGroupBySourceExport item = new ContentEffectGroupBySourceExport();
+                    item.setDateStr(dateStr);
+                    item.setType("文章");
+                    item.setSource(source);
+                    item.setIsSameMiniprogram(same);
+                    item.setPosition(position);
+                    result.add(item);
+                }
             }
         }
         for (String same : isSameMiniprogram) {
@@ -2863,12 +3014,15 @@ public class DataDashboardService {
             result.add(item);
         }
         for (String source : videoSource) {
-            ContentEffectGroupBySourceExport item = new ContentEffectGroupBySourceExport();
-            item.setDateStr(dateStr);
-            item.setType("视频");
-            item.setSource(source);
-            item.setIsSameMiniprogram("SUM");
-            result.add(item);
+            for (String position : positionList) {
+                ContentEffectGroupBySourceExport item = new ContentEffectGroupBySourceExport();
+                item.setDateStr(dateStr);
+                item.setType("视频");
+                item.setSource(source);
+                item.setIsSameMiniprogram("SUM");
+                item.setPosition(position);
+                result.add(item);
+            }
         }
         ContentEffectGroupBySourceExport item = new ContentEffectGroupBySourceExport();
         item.setDateStr(dateStr);
@@ -2924,4 +3078,970 @@ public class DataDashboardService {
         }
     }
 
+    @XxlJob("buildCoverStatisticDataExportJob")
+    public ReturnT<String> buildCoverStatisticDataExportJob(String param) {
+        if (StringUtils.hasText(param)) {
+            coverStatisticDataExport(param);
+            coverStatisticTestDataExport(param);
+        } else {
+            String dateStr = DateUtils.getDateString(System.currentTimeMillis() - 24 * 60 * 60 * 1000, "yyyMMdd");
+            coverStatisticDataExport(dateStr);
+            coverStatisticTestDataExport(dateStr);
+        }
+        return ReturnT.SUCCESS;
+    }
+
+    public void coverStatisticDataExport(String dateStr) {
+        List<String> dateStrList = Arrays.asList(dateStr);
+        List<CoverStatisticDataExport> exportList = buildCoverStatisticDataExport(dateStr);
+        if (CollectionUtil.isEmpty(exportList)) {
+            return;
+        }
+        int rowNum = exportList.size();
+        List<List<Object>> rows = new ArrayList<>();
+        Field[] fields = CoverStatisticDataExport.class.getDeclaredFields();
+        for (CoverStatisticDataExport datum : exportList) {
+            List<Object> rowDatas = new ArrayList<>();
+            rows.add(rowDatas);
+
+            for (Field field : fields) {
+                field.setAccessible(true);
+                try {
+                    rowDatas.add(field.get(datum));
+                } catch (IllegalAccessException e) {
+                    log.error("获取值出错:{}", field.getName());
+                } catch (Exception e) {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        }
+        doSendFeishuSheet(dateStrList, dailyDetailSheetToken, "jwayC4", rowNum, rows,
+                2, null, null, null);
+    }
+
+    public void coverStatisticTestDataExport(String dateStr) {
+        List<String> dateStrList = Arrays.asList(dateStr);
+        List<CoverStatisticOneDataExport> exportList = buildCoverStatisticTestDataExport(dateStr);
+        if (CollectionUtil.isEmpty(exportList)) {
+            return;
+        }
+        int rowNum = exportList.size();
+        List<List<Object>> rows = new ArrayList<>();
+        Field[] fields = CoverStatisticOneDataExport.class.getDeclaredFields();
+        for (CoverStatisticOneDataExport datum : exportList) {
+            List<Object> rowDatas = new ArrayList<>();
+            rows.add(rowDatas);
+
+            for (Field field : fields) {
+                field.setAccessible(true);
+                try {
+                    rowDatas.add(field.get(datum));
+                } catch (IllegalAccessException e) {
+                    log.error("获取值出错:{}", field.getName());
+                } catch (Exception e) {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        }
+        doSendFeishuSheet(dateStrList, dailyDetailSheetToken, "QiEZiz", rowNum, rows,
+                2, null, null, null);
+    }
+
+
+    private List<CoverStatisticDataExport> buildCoverStatisticDataExport(String dateStr) {
+        List<CoverStatisticDataExport> result = new ArrayList<>();
+        CoverStatisticDataExport articleTestFirst = new CoverStatisticDataExport(dateStr, "文章", "实验组", "头条");
+        CoverStatisticDataExport articleTestSecond = new CoverStatisticDataExport(dateStr, "文章", "实验组", "次条");
+        CoverStatisticDataExport articleTestLast = new CoverStatisticDataExport(dateStr, "文章", "实验组", "3-8条");
+        CoverStatisticDataExport articleTestSum = new CoverStatisticDataExport(dateStr, "文章", "实验组", "sum");
+
+        CoverStatisticDataExport articleBaseFirst = new CoverStatisticDataExport(dateStr, "文章", "base组", "头条");
+        CoverStatisticDataExport articleBaseSecond = new CoverStatisticDataExport(dateStr, "文章", "base组", "次条");
+        CoverStatisticDataExport articleBaseLast = new CoverStatisticDataExport(dateStr, "文章", "base组", "3-8条");
+        CoverStatisticDataExport articleBaseSum = new CoverStatisticDataExport(dateStr, "文章", "base组", "sum");
+
+
+        CoverStatisticDataExport articleMinimumFirst = new CoverStatisticDataExport(dateStr, "文章", "兜底组", "头条");
+        CoverStatisticDataExport articleMinimumSecond = new CoverStatisticDataExport(dateStr, "文章", "兜底组", "次条");
+        CoverStatisticDataExport articleMinimumLast = new CoverStatisticDataExport(dateStr, "文章", "兜底组", "3-8条");
+        CoverStatisticDataExport articleMinimumSum = new CoverStatisticDataExport(dateStr, "文章", "兜底组", "sum");
+
+        CoverStatisticDataExport articleSum = new CoverStatisticDataExport(dateStr, "文章", "sum", "sum");
+
+        CoverStatisticDataExport videoTestFirst = new CoverStatisticDataExport(dateStr, "视频", "实验组", "头条");
+        CoverStatisticDataExport videoTestSecond = new CoverStatisticDataExport(dateStr, "视频", "实验组", "次条");
+        CoverStatisticDataExport videoTestLast = new CoverStatisticDataExport(dateStr, "视频", "实验组", "3-8条");
+        CoverStatisticDataExport videoTestSum = new CoverStatisticDataExport(dateStr, "视频", "实验组", "sum");
+
+
+        CoverStatisticDataExport videoBaseFirst = new CoverStatisticDataExport(dateStr, "视频", "base组", "头条");
+        CoverStatisticDataExport videoBaseSecond = new CoverStatisticDataExport(dateStr, "视频", "base组", "次条");
+        CoverStatisticDataExport videoBaseLast = new CoverStatisticDataExport(dateStr, "视频", "base组", "3-8条");
+        CoverStatisticDataExport videoBaseSum = new CoverStatisticDataExport(dateStr, "视频", "base组", "sum");
+
+
+        CoverStatisticDataExport videoMinimumFirst = new CoverStatisticDataExport(dateStr, "视频", "兜底组", "头条");
+        CoverStatisticDataExport videoMinimumSecond = new CoverStatisticDataExport(dateStr, "视频", "兜底组", "次条");
+        CoverStatisticDataExport videoMinimumLast = new CoverStatisticDataExport(dateStr, "视频", "兜底组", "3-8条");
+        CoverStatisticDataExport videoMinimumSum = new CoverStatisticDataExport(dateStr, "视频", "兜底组", "sum");
+
+        CoverStatisticDataExport videoSum = new CoverStatisticDataExport(dateStr, "视频", "sum", "sum");
+
+        Long start = DateUtils.getStartOfDay(dateStr, "yyyyMMdd");
+        Long end = start + 86400;
+        Date startDate = new Date(start * 1000);
+        Date endDate = new Date(end * 1000);
+        List<Article> articleList = articleRepository.getByPublishTimestampBetweenAndTypeEquals(start, end, ArticleTypeEnum.QUNFA.getVal());
+        List<String> wxSnList = articleList.stream().map(Article::getWxSn).collect(Collectors.toList());
+        List<ArticleDetailInfo> articleDetailInfoList = new ArrayList<>();
+        for (List<String> partitions : Lists.partition(wxSnList, 1000)) {
+            articleDetailInfoList.addAll(articleDetailInfoRepository.findByWxSnInAndPublishDtBetween(partitions, startDate, endDate));
+        }
+        List<DatastatSortStrategy> datastatSortStrategyList = datastatSortStrategyRepository.getByWxSnIn(wxSnList);
+        Map<String, DatastatSortStrategy> datastatSortStrategyMap = datastatSortStrategyList.stream()
+                .collect(Collectors.toMap(DatastatSortStrategy::getWxSn, Function.identity()));
+        Map<String, List<ArticleDetailInfo>> articleDetailInfoMap = articleDetailInfoList.stream()
+                .collect(Collectors.groupingBy(ArticleDetailInfo::getWxSn));
+        // 获取rootSourceId
+        Map<String, List<String>> rootSourceIdMap = new HashMap<>();
+        List<String> rootSourceIdList = new ArrayList<>();
+        for (Article article : articleList) {
+            List<String> rootSourceIds = JSONArray.parseArray(article.getRootSourceIdList(), String.class);
+            if (CollectionUtils.isEmpty(rootSourceIds)) {
+                continue;
+            }
+            rootSourceIdMap.put(article.getWxSn(), rootSourceIds);
+            rootSourceIdList.addAll(rootSourceIds);
+        }
+        List<LongArticlesRootSourceId> longArticlesRootSourceIdList = rootSourceIdRepository.getByRootSourceIdIn(rootSourceIdList);
+        Map<String, LongArticlesRootSourceId> longArticlesRootSourceIdMap = longArticlesRootSourceIdList.stream()
+                .collect(Collectors.toMap(LongArticlesRootSourceId::getRootSourceId, Function.identity()));
+        // 获取视频
+        List<String> contentIdList = longArticlesRootSourceIdList.stream().map(LongArticlesRootSourceId::getContentId).distinct().collect(Collectors.toList());
+        List<LongArticleCrawlerVideo> longArticleCrawlerVideoList = crawlerVideoRepository.getByContentIdInAndIsIllegal(contentIdList, 0);
+        Map<String, List<LongArticleCrawlerVideo>> longArticleCrawlerVideoMap = longArticleCrawlerVideoList.stream()
+                .collect(Collectors.groupingBy(LongArticleCrawlerVideo::getContentId));
+        for (Article article : articleList) {
+            boolean isVideo = false;
+            boolean isSameMiniProgram = false;
+            boolean isTest = false;
+            List<String> rootSourceIds = rootSourceIdMap.get(article.getWxSn());
+            if (CollectionUtil.isNotEmpty(rootSourceIds)) {
+                List<LongArticlesRootSourceId> rootSourceIdItemList = new ArrayList<>();
+                for (String rootSourceId : rootSourceIds) {
+                    LongArticlesRootSourceId longArticlesRootSourceId = longArticlesRootSourceIdMap.get(rootSourceId);
+                    if (!Objects.isNull(longArticlesRootSourceId)) {
+                        rootSourceIdItemList.add(longArticlesRootSourceId);
+                    }
+                    LongArticlesPublishMiniprogram longArticlesPublishMiniprogram
+                            = longArticlesPublishMiniprogramRepository.findByRootSourceIdEquals(rootSourceId);
+                    if (!Objects.isNull(longArticlesPublishMiniprogram)) {
+                        if (longArticlesPublishMiniprogram.getNewVideoCoverId() != null) {
+                            isTest = true;
+                        }
+                    }
+
+                }
+                String contentId = rootSourceIdItemList.get(0).getContentId();
+                List<LongArticleCrawlerVideo> crawlerVideoList = longArticleCrawlerVideoMap.get(contentId);
+                List<String> videoOssPaths = crawlerVideoList.stream().map(LongArticleCrawlerVideo::getVideoOssPath)
+                        .filter(StringUtils::hasText).collect(Collectors.toList());
+                List<PublishSingleVideoSource> singleVideoSources = videoPoolRepository.getByVideoOssPathIn(videoOssPaths);
+                if (CollectionUtil.isNotEmpty(singleVideoSources)) {
+                    isVideo = true;
+                }
+                if (CollectionUtil.isNotEmpty(crawlerVideoList) && crawlerVideoList.size() == 1) {
+                    isSameMiniProgram = true;
+                }
+            }
+            List<ArticleDetailInfo> detailInfoList = articleDetailInfoMap.get(article.getWxSn());
+            if (CollectionUtils.isEmpty(detailInfoList)) {
+                continue;
+            }
+            List<AccountAvgInfo> accountAvgInfoList = accountAvgInfoRepository.getAllByGhIdEqualsAndStatusEquals(article.getGhId(), 1);
+            Integer fans = 0;
+            if (CollectionUtils.isNotEmpty(accountAvgInfoList)) {
+                fans = accountAvgInfoList.get(0).getFans();
+            }
+            if (isVideo) {
+                if (isSameMiniProgram) {
+                    if (isTest) {
+                        if (article.getItemIndex() == 1) {
+                            save(videoTestFirst, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(videoTestFirst, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        } else if (article.getItemIndex() == 2) {
+                            save(videoTestSecond, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(videoTestSecond, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        } else {
+                            save(videoTestLast, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(videoTestLast, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        }
+                    } else {
+                        if (article.getItemIndex() == 1) {
+                            save(videoMinimumFirst, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(videoMinimumFirst, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        } else if (article.getItemIndex() == 2) {
+                            save(videoMinimumSecond, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(videoMinimumSecond, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        } else {
+                            save(videoMinimumLast, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(videoMinimumLast, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        }
+                    }
+                } else {
+                    if (article.getItemIndex() == 1) {
+                        save(videoBaseFirst, fans, article.getShowViewCount());
+                        for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                            saveMini(videoBaseFirst, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                        }
+                    } else if (article.getItemIndex() == 2) {
+                        save(videoBaseSecond, fans, article.getShowViewCount());
+                        for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                            saveMini(videoBaseSecond, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                        }
+                    } else {
+                        save(videoBaseLast, fans, article.getShowViewCount());
+                        for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                            saveMini(videoBaseLast, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                        }
+                    }
+                }
+            } else {
+                if (isSameMiniProgram) {
+                    if (isTest) {
+                        if (article.getItemIndex() == 1) {
+                            save(articleTestFirst, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(articleTestFirst, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        } else if (article.getItemIndex() == 2) {
+                            save(articleTestSecond, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(articleTestSecond, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        } else {
+                            save(articleTestLast, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(articleTestLast, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        }
+                    } else {
+                        if (article.getItemIndex() == 1) {
+                            save(articleMinimumFirst, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(articleMinimumFirst, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        } else if (article.getItemIndex() == 2) {
+                            save(articleMinimumSecond, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(articleMinimumSecond, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        } else {
+                            save(articleMinimumLast, fans, article.getShowViewCount());
+                            for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                                saveMini(articleMinimumLast, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                            }
+                        }
+                    }
+                } else {
+                    if (article.getItemIndex() == 1) {
+                        save(articleBaseFirst, fans, article.getShowViewCount());
+                        for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                            saveMini(articleBaseFirst, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                        }
+                    } else if (article.getItemIndex() == 2) {
+                        save(articleBaseSecond, fans, article.getShowViewCount());
+                        for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                            saveMini(articleBaseSecond, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                        }
+                    } else {
+                        save(articleBaseLast, fans, article.getShowViewCount());
+                        for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                            saveMini(articleBaseLast, articleDetailInfo.getFirstLevel(), articleDetailInfo.getFission0(), articleDetailInfo.getVideoIndex());
+                        }
+                    }
+                }
+            }
+        }
+        sum(articleTestSum, articleTestFirst, articleTestSecond, articleTestLast);
+        sum(articleBaseSum, articleBaseFirst, articleBaseSecond, articleBaseLast);
+        sum(articleMinimumSum, articleMinimumFirst, articleMinimumSecond, articleMinimumLast);
+        sum(articleSum, articleTestSum, articleBaseSum, articleMinimumSum);
+
+        sum(videoTestSum, videoTestFirst, videoTestSecond, videoTestLast);
+        sum(videoBaseSum, videoBaseFirst, videoBaseSecond, videoBaseLast);
+        sum(videoMinimumSum, videoMinimumFirst, videoMinimumSecond, videoMinimumLast);
+        sum(videoSum, videoTestSum, videoBaseSum, videoMinimumSum);
+
+        result.add(rate(articleTestFirst));
+        result.add(rate(articleTestSecond));
+        result.add(rate(articleTestLast));
+        result.add(rate(articleTestSum));
+        result.add(rate(articleBaseFirst));
+        result.add(rate(articleBaseSecond));
+        result.add(rate(articleBaseLast));
+        result.add(rate(articleBaseSum));
+        result.add(rate(articleMinimumFirst));
+        result.add(rate(articleMinimumSecond));
+        result.add(rate(articleMinimumLast));
+        result.add(rate(articleMinimumSum));
+        result.add(rate(articleSum));
+
+
+        result.add(rate(videoTestFirst));
+        result.add(rate(videoTestSecond));
+        result.add(rate(videoTestLast));
+        result.add(rate(videoTestSum));
+        result.add(rate(videoBaseFirst));
+        result.add(rate(videoBaseSecond));
+        result.add(rate(videoBaseLast));
+        result.add(rate(videoBaseSum));
+        result.add(rate(videoMinimumFirst));
+        result.add(rate(videoMinimumSecond));
+        result.add(rate(videoMinimumLast));
+        result.add(rate(videoMinimumSum));
+        result.add(rate(videoSum));
+
+        return result;
+    }
+
+    private List<CoverStatisticOneDataExport> buildCoverStatisticTestDataExport(String dateStr) {
+        List<CoverStatisticOneDataExport> result = new ArrayList<>();
+        Long start = DateUtils.getStartOfDay(dateStr, "yyyyMMdd");
+        Long end = start + 86400;
+        Date startDate = new Date(start * 1000);
+        Date endDate = new Date(end * 1000);
+        List<Article> articleList = articleRepository.getByPublishTimestampBetweenAndTypeEquals(start, end, ArticleTypeEnum.QUNFA.getVal());
+        List<String> wxSnList = articleList.stream().map(Article::getWxSn).collect(Collectors.toList());
+        List<ArticleDetailInfo> articleDetailInfoList = new ArrayList<>();
+        for (List<String> partitions : Lists.partition(wxSnList, 1000)) {
+            articleDetailInfoList.addAll(articleDetailInfoRepository.findByWxSnInAndPublishDtBetween(partitions, startDate, endDate));
+        }
+        List<DatastatSortStrategy> datastatSortStrategyList = datastatSortStrategyRepository.getByWxSnIn(wxSnList);
+        Map<String, DatastatSortStrategy> datastatSortStrategyMap = datastatSortStrategyList.stream()
+                .collect(Collectors.toMap(DatastatSortStrategy::getWxSn, Function.identity()));
+        Map<String, List<ArticleDetailInfo>> articleDetailInfoMap = articleDetailInfoList.stream()
+                .collect(Collectors.groupingBy(ArticleDetailInfo::getWxSn));
+        // 获取rootSourceId
+        Map<String, List<String>> rootSourceIdMap = new HashMap<>();
+        List<String> rootSourceIdList = new ArrayList<>();
+        for (Article article : articleList) {
+            List<String> rootSourceIds = JSONArray.parseArray(article.getRootSourceIdList(), String.class);
+            if (CollectionUtils.isEmpty(rootSourceIds)) {
+                continue;
+            }
+            rootSourceIdMap.put(article.getWxSn(), rootSourceIds);
+            rootSourceIdList.addAll(rootSourceIds);
+        }
+        List<LongArticlesRootSourceId> longArticlesRootSourceIdList = rootSourceIdRepository.getByRootSourceIdIn(rootSourceIdList);
+        Map<String, LongArticlesRootSourceId> longArticlesRootSourceIdMap = longArticlesRootSourceIdList.stream()
+                .collect(Collectors.toMap(LongArticlesRootSourceId::getRootSourceId, Function.identity()));
+        // 获取视频
+        List<String> contentIdList = longArticlesRootSourceIdList.stream().map(LongArticlesRootSourceId::getContentId).distinct().collect(Collectors.toList());
+        List<LongArticleCrawlerVideo> longArticleCrawlerVideoList = crawlerVideoRepository.getByContentIdInAndIsIllegal(contentIdList, 0);
+        Map<String, List<LongArticleCrawlerVideo>> longArticleCrawlerVideoMap = longArticleCrawlerVideoList.stream()
+                .collect(Collectors.groupingBy(LongArticleCrawlerVideo::getContentId));
+        for (Article article : articleList) {
+            boolean isVideo = false;
+            boolean isSameMiniProgram = false;
+            boolean isTest = false;
+            List<String> rootSourceIds = rootSourceIdMap.get(article.getWxSn());
+            if (CollectionUtil.isNotEmpty(rootSourceIds)) {
+                List<LongArticlesRootSourceId> rootSourceIdItemList = new ArrayList<>();
+                for (String rootSourceId : rootSourceIds) {
+                    LongArticlesRootSourceId longArticlesRootSourceId = longArticlesRootSourceIdMap.get(rootSourceId);
+                    if (!Objects.isNull(longArticlesRootSourceId)) {
+                        rootSourceIdItemList.add(longArticlesRootSourceId);
+                    }
+                    LongArticlesPublishMiniprogram longArticlesPublishMiniprogram
+                            = longArticlesPublishMiniprogramRepository.findByRootSourceIdEquals(rootSourceId);
+                    if (!Objects.isNull(longArticlesPublishMiniprogram)) {
+                        if (longArticlesPublishMiniprogram.getNewVideoCoverId() != null) {
+                            isTest = true;
+                        }
+                    }
+                }
+                String contentId = rootSourceIdItemList.get(0).getContentId();
+                List<LongArticleCrawlerVideo> crawlerVideoList = longArticleCrawlerVideoMap.get(contentId);
+                List<String> videoOssPaths = crawlerVideoList.stream().map(LongArticleCrawlerVideo::getVideoOssPath)
+                        .filter(StringUtils::hasText).collect(Collectors.toList());
+                List<PublishSingleVideoSource> singleVideoSources = videoPoolRepository.getByVideoOssPathIn(videoOssPaths);
+                if (CollectionUtil.isNotEmpty(singleVideoSources)) {
+                    isVideo = true;
+                }
+                if (CollectionUtil.isNotEmpty(crawlerVideoList) && crawlerVideoList.size() == 1) {
+                    isSameMiniProgram = true;
+                }
+            }
+            List<ArticleDetailInfo> detailInfoList = articleDetailInfoMap.get(article.getWxSn());
+            if (CollectionUtils.isEmpty(detailInfoList)) {
+                continue;
+            }
+            List<AccountAvgInfo> accountAvgInfoList = accountAvgInfoRepository.getAllByGhIdEqualsAndStatusEquals(article.getGhId(), 1);
+            Integer fans = 0;
+            if (CollectionUtils.isNotEmpty(accountAvgInfoList)) {
+                fans = accountAvgInfoList.get(0).getFans();
+            }
+            if (isSameMiniProgram && isTest) {
+                CoverStatisticOneDataExport coverStatisticOneDataExport = new CoverStatisticOneDataExport();
+                if (isVideo) {
+                    coverStatisticOneDataExport.setChannel("视频");
+                } else {
+                    coverStatisticOneDataExport.setChannel("文章");
+                }
+                coverStatisticOneDataExport.setDateStr(dateStr);
+                coverStatisticOneDataExport.setAccountName(article.getAccountName());
+                coverStatisticOneDataExport.setTitle(article.getTitle());
+                coverStatisticOneDataExport.setContentUrl(article.getContentUrl());
+                if (article.getItemIndex() == 1) {
+                    coverStatisticOneDataExport.setIndex("头条");
+                } else if (article.getItemIndex() == 2) {
+                    coverStatisticOneDataExport.setIndex("次条");
+                } else {
+                    coverStatisticOneDataExport.setIndex("3-8条");
+                }
+                coverStatisticOneDataExport.setFans(fans);
+                coverStatisticOneDataExport.setShowViewCount(article.getShowViewCount());
+                for (ArticleDetailInfo articleDetailInfo : detailInfoList) {
+                    if (articleDetailInfo.getFirstLevel() != null) {
+                        coverStatisticOneDataExport.setAllFirstLevel(coverStatisticOneDataExport.getAllFirstLevel() + articleDetailInfo.getFirstLevel());
+                        if (articleDetailInfo.getVideoIndex() == 1) {
+                            coverStatisticOneDataExport.setOneFirstLevel(coverStatisticOneDataExport.getOneFirstLevel() + articleDetailInfo.getFirstLevel());
+                        }
+                        if (articleDetailInfo.getVideoIndex() == 2) {
+                            coverStatisticOneDataExport.setTwoFirstLevel(coverStatisticOneDataExport.getTwoFirstLevel() + articleDetailInfo.getFirstLevel());
+                        }
+                    }
+                    if (articleDetailInfo.getFission0() != null) {
+                        coverStatisticOneDataExport.setAllFission0(coverStatisticOneDataExport.getAllFission0() + articleDetailInfo.getFission0());
+                        if (articleDetailInfo.getVideoIndex() == 1) {
+                            coverStatisticOneDataExport.setOneFission0(coverStatisticOneDataExport.getOneFission0() + articleDetailInfo.getFission0());
+                        }
+                        if (articleDetailInfo.getVideoIndex() == 2) {
+                            coverStatisticOneDataExport.setTwoFission0(coverStatisticOneDataExport.getTwoFission0() + articleDetailInfo.getFission0());
+                        }
+                    }
+                }
+
+                if (coverStatisticOneDataExport.getShowViewCount() != 0 && coverStatisticOneDataExport.getFans() != 0) {
+                    coverStatisticOneDataExport.setShowViewRate((double) coverStatisticOneDataExport.getShowViewCount() / coverStatisticOneDataExport.getFans());
+                }
+                if (coverStatisticOneDataExport.getShowViewCount() != 0) {
+                    if (coverStatisticOneDataExport.getAllFirstLevel() != 0) {
+                        coverStatisticOneDataExport.setAllFirstLevelRate((double) coverStatisticOneDataExport.getAllFirstLevel() / coverStatisticOneDataExport.getShowViewCount());
+                    }
+                    if (coverStatisticOneDataExport.getOneFirstLevel() != 0) {
+                        coverStatisticOneDataExport.setOneFirstLevelRate((double) coverStatisticOneDataExport.getOneFirstLevel() / coverStatisticOneDataExport.getShowViewCount());
+                    }
+                    if (coverStatisticOneDataExport.getTwoFirstLevel() != 0) {
+                        coverStatisticOneDataExport.setTwoFirstLevelRate((double) coverStatisticOneDataExport.getTwoFirstLevel() / coverStatisticOneDataExport.getShowViewCount());
+                    }
+                }
+
+                if (coverStatisticOneDataExport.getAllFirstLevel() != 0 && coverStatisticOneDataExport.getAllFission0() != 0) {
+                    coverStatisticOneDataExport.setAllFission0Rate((double) coverStatisticOneDataExport.getAllFission0() / coverStatisticOneDataExport.getAllFirstLevel());
+                }
+                if (coverStatisticOneDataExport.getOneFirstLevel() != 0 && coverStatisticOneDataExport.getOneFission0() != 0) {
+                    coverStatisticOneDataExport.setOneFission0Rate((double) coverStatisticOneDataExport.getOneFission0() / coverStatisticOneDataExport.getOneFirstLevel());
+                }
+                if (coverStatisticOneDataExport.getTwoFirstLevel() != 0 && coverStatisticOneDataExport.getTwoFission0() != 0) {
+                    coverStatisticOneDataExport.setTwoFission0Rate((double) coverStatisticOneDataExport.getTwoFission0() / coverStatisticOneDataExport.getTwoFirstLevel());
+                }
+                result.add(coverStatisticOneDataExport);
+            }
+        }
+        if (!CollectionUtils.isEmpty(result)) {
+            result = result.stream().sorted(Comparator.comparing(CoverStatisticOneDataExport::getChannel)).collect(Collectors.toList());
+        }
+        return result;
+    }
+
+    private void save(CoverStatisticDataExport coverStatisticDataExport, Integer fans, Integer showViewCount) {
+        if (fans != null) {
+            coverStatisticDataExport.setFans(coverStatisticDataExport.getFans() + fans);
+        }
+        if (showViewCount != null) {
+            coverStatisticDataExport.setShowViewCount(coverStatisticDataExport.getShowViewCount() + showViewCount);
+        }
+        coverStatisticDataExport.setPublishCount(coverStatisticDataExport.getPublishCount() + 1);
+    }
+
+    private void saveMini(CoverStatisticDataExport coverStatisticDataExport, Integer firstLevel, Integer fission0, Integer index) {
+        if (firstLevel != null) {
+            coverStatisticDataExport.setAllFirstLevel(coverStatisticDataExport.getAllFirstLevel() + firstLevel);
+            if (index == 1) {
+                coverStatisticDataExport.setOneFirstLevel(coverStatisticDataExport.getOneFirstLevel() + firstLevel);
+            }
+            if (index == 2) {
+                coverStatisticDataExport.setTwoFirstLevel(coverStatisticDataExport.getTwoFirstLevel() + firstLevel);
+            }
+        }
+        if (fission0 != null) {
+            coverStatisticDataExport.setAllFission0(coverStatisticDataExport.getAllFission0() + fission0);
+            if (index == 1) {
+                coverStatisticDataExport.setOneFission0(coverStatisticDataExport.getOneFission0() + fission0);
+            }
+            if (index == 2) {
+                coverStatisticDataExport.setTwoFission0(coverStatisticDataExport.getTwoFission0() + fission0);
+            }
+        }
+    }
+
+    private void sum(CoverStatisticDataExport sumCoverStatisticDataExport,
+                     CoverStatisticDataExport coverStatisticDataExport1,
+                     CoverStatisticDataExport coverStatisticDataExport2,
+                     CoverStatisticDataExport coverStatisticDataExport3) {
+        sumCoverStatisticDataExport.setFans(coverStatisticDataExport1.getFans() + coverStatisticDataExport2.getFans() + coverStatisticDataExport3.getFans());
+        sumCoverStatisticDataExport.setPublishCount(coverStatisticDataExport1.getPublishCount() + coverStatisticDataExport2.getPublishCount() + coverStatisticDataExport3.getPublishCount());
+        sumCoverStatisticDataExport.setShowViewCount(coverStatisticDataExport1.getShowViewCount() + coverStatisticDataExport2.getShowViewCount() + coverStatisticDataExport3.getShowViewCount());
+        sumCoverStatisticDataExport.setAllFirstLevel(coverStatisticDataExport1.getAllFirstLevel() + coverStatisticDataExport2.getAllFirstLevel() + coverStatisticDataExport3.getAllFirstLevel());
+        sumCoverStatisticDataExport.setOneFirstLevel(coverStatisticDataExport1.getOneFirstLevel() + coverStatisticDataExport2.getOneFirstLevel() + coverStatisticDataExport3.getOneFirstLevel());
+        sumCoverStatisticDataExport.setTwoFirstLevel(coverStatisticDataExport1.getTwoFirstLevel() + coverStatisticDataExport2.getTwoFirstLevel() + coverStatisticDataExport3.getTwoFirstLevel());
+        sumCoverStatisticDataExport.setAllFission0(coverStatisticDataExport1.getAllFission0() + coverStatisticDataExport2.getAllFission0() + coverStatisticDataExport3.getAllFission0());
+        sumCoverStatisticDataExport.setOneFission0(coverStatisticDataExport1.getOneFission0() + coverStatisticDataExport2.getOneFission0() + coverStatisticDataExport3.getOneFission0());
+        sumCoverStatisticDataExport.setTwoFission0(coverStatisticDataExport1.getTwoFission0() + coverStatisticDataExport2.getTwoFission0() + coverStatisticDataExport3.getTwoFission0());
+    }
+
+    private CoverStatisticDataExport rate(CoverStatisticDataExport coverStatisticDataExport) {
+        if (coverStatisticDataExport.getShowViewCount() != 0 && coverStatisticDataExport.getFans() != 0) {
+            coverStatisticDataExport.setShowViewRate((double) coverStatisticDataExport.getShowViewCount() / coverStatisticDataExport.getFans());
+        }
+        if (coverStatisticDataExport.getShowViewCount() != 0) {
+            if (coverStatisticDataExport.getAllFirstLevel() != 0) {
+                coverStatisticDataExport.setAllFirstLevelRate((double) coverStatisticDataExport.getAllFirstLevel() / coverStatisticDataExport.getShowViewCount());
+            }
+            if (coverStatisticDataExport.getOneFirstLevel() != 0) {
+                coverStatisticDataExport.setOneFirstLevelRate((double) coverStatisticDataExport.getOneFirstLevel() / coverStatisticDataExport.getShowViewCount());
+            }
+            if (coverStatisticDataExport.getTwoFirstLevel() != 0) {
+                coverStatisticDataExport.setTwoFirstLevelRate((double) coverStatisticDataExport.getTwoFirstLevel() / coverStatisticDataExport.getShowViewCount());
+            }
+        }
+
+        if (coverStatisticDataExport.getAllFirstLevel() != 0 && coverStatisticDataExport.getAllFission0() != 0) {
+            coverStatisticDataExport.setAllFission0Rate((double) coverStatisticDataExport.getAllFission0() / coverStatisticDataExport.getAllFirstLevel());
+        }
+        if (coverStatisticDataExport.getOneFirstLevel() != 0 && coverStatisticDataExport.getOneFission0() != 0) {
+            coverStatisticDataExport.setOneFission0Rate((double) coverStatisticDataExport.getOneFission0() / coverStatisticDataExport.getOneFirstLevel());
+        }
+        if (coverStatisticDataExport.getTwoFirstLevel() != 0 && coverStatisticDataExport.getTwoFission0() != 0) {
+            coverStatisticDataExport.setTwoFission0Rate((double) coverStatisticDataExport.getTwoFission0() / coverStatisticDataExport.getTwoFirstLevel());
+        }
+        return coverStatisticDataExport;
+    }
+
+
+    @XxlJob("dailyAuditProduceExport")
+    public ReturnT<String> dailyAuditProduceJob(String param) {
+        List<String> dateStrList = DateUtils.getBeforeDays(null, null, 1);
+        dailyAuditProduce(dateStrList);
+        return ReturnT.SUCCESS;
+    }
+
+    public void dailyAuditProduce(String dateStr) {
+        if (!StringUtils.hasText(dateStr)) {
+            dateStr = DateUtils.getBeforeDaysDateStr("yyyyMMdd", 1);
+        }
+        dailyAuditProduce(Collections.singletonList(dateStr));
+    }
+
+    public void dailyAuditProduce(List<String> dateStrList) {
+        List<DailyAuditProduceExport> exportList = new ArrayList<>();
+        dateStrList = Lists.reverse(dateStrList);
+        for (String dateStr : dateStrList) {
+            exportList.addAll(buildDailyAuditProduceExport(dateStr));
+        }
+        if (CollectionUtil.isEmpty(exportList)) {
+            return;
+        }
+        int rowNum = exportList.size();
+        List<List<Object>> rows = new ArrayList<>();
+        Field[] fields = DailyAuditProduceExport.class.getDeclaredFields();
+        for (DailyAuditProduceExport datum : exportList) {
+            List<Object> rowDatas = new ArrayList<>();
+            rows.add(rowDatas);
+
+            for (Field field : fields) {
+                field.setAccessible(true);
+                try {
+                    rowDatas.add(field.get(datum));
+                } catch (IllegalAccessException e) {
+                    log.error("获取值出错:{}", field.getName());
+                } catch (Exception e) {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        }
+
+        List<Pair<String, String>> styles = Arrays
+                .asList(
+                        Pair.of("F", "0.00%"),
+                        Pair.of("I", "0.00%")
+                );
+        List<Pair<String, List<Pair<String, String>>>> thank = Arrays
+                .asList(
+                        Pair.of("B", Arrays.asList(
+                                Pair.of("长文审核", "#BACEFD"),
+                                Pair.of("文章内容池", "#FED4A4")))
+                );
+        doSendFeishuSheet(dateStrList, dailyDetailSheetToken, "Lushkl", rowNum, rows,
+                3, styles, null, thank);
+    }
+
+    private List<DailyAuditProduceExport> buildDailyAuditProduceExport(String dateStr) {
+        Long startTimeStamp = DateUtils.getStartOfDay(dateStr, "yyyyMMdd") * 1000;
+        Long endTimeStamp = startTimeStamp + 86400000;
+        List<DailyAuditProduceExport> result = new ArrayList<>();
+        List<DailyAuditProduceExport> produceAuditList = aigcBaseMapper.getAccountDailyProduceAuditCount(startTimeStamp, endTimeStamp);
+        List<DailyAuditProduceExport> videoAuditList = longArticleBaseMapper.getAccountDailyVideoAuditCount(startTimeStamp, endTimeStamp);
+        for (DailyAuditProduceExport dailyAuditProduceExport : produceAuditList) {
+            dailyAuditProduceExport.setType("长文审核");
+            result.add(dailyAuditProduceExport);
+        }
+        for (DailyAuditProduceExport dailyAuditProduceExport : videoAuditList) {
+            dailyAuditProduceExport.setType("文章内容池");
+            result.add(dailyAuditProduceExport);
+        }
+        for (DailyAuditProduceExport item : result) {
+            item.setDateStr(dateStr);
+            if (item.getAuditCount() != null && item.getAuditCount() != 0) {
+                item.setAuditPassRate((double) item.getAuditPassCount() / item.getAuditCount());
+            }
+            if (item.getVideoAuditCount() != null && item.getVideoAuditCount() != 0) {
+                item.setVideoAuditPassRate((double) item.getVideoAuditPassCount() / item.getVideoAuditCount());
+            }
+        }
+        return result;
+    }
+
+    @XxlJob("categoryGroupDataExport")
+    public ReturnT<String> categoryGroupDataJob(String param) {
+        List<String> dateStrList = DateUtils.getBeforeDays(null, null, 1);
+        categoryGroupData(dateStrList);
+        return ReturnT.SUCCESS;
+    }
+
+    public void categoryGroupData(String dateStr) {
+        if (!StringUtils.hasText(dateStr)) {
+            dateStr = DateUtils.getBeforeDaysDateStr("yyyyMMdd", 1);
+        }
+        categoryGroupData(Collections.singletonList(dateStr));
+    }
+
+    public void categoryGroupData(List<String> dateStrList) {
+        List<CategoryGroupDataExport> exportList = new ArrayList<>();
+        dateStrList = Lists.reverse(dateStrList);
+        for (String dateStr : dateStrList) {
+            exportList.addAll(buildCategoryGroupDataExport(dateStr));
+        }
+        if (CollectionUtil.isEmpty(exportList)) {
+            return;
+        }
+        int rowNum = exportList.size();
+        List<List<Object>> rows = new ArrayList<>();
+        Field[] fields = CategoryGroupDataExport.class.getDeclaredFields();
+        for (CategoryGroupDataExport datum : exportList) {
+            List<Object> rowDatas = new ArrayList<>();
+            rows.add(rowDatas);
+
+            for (Field field : fields) {
+                field.setAccessible(true);
+                try {
+                    rowDatas.add(field.get(datum));
+                } catch (IllegalAccessException e) {
+                    log.error("获取值出错:{}", field.getName());
+                } catch (Exception e) {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        }
+
+        List<Pair<String, String>> styles = Arrays
+                .asList(
+                        Pair.of("I", "0.00%"),
+                        Pair.of("J", "0.00%"),
+                        Pair.of("K", "0.00%"),
+                        Pair.of("L", "0.00%")
+                );
+        doSendFeishuSheet(dateStrList, dailyDetailSheetToken, "bl1eL2", rowNum, rows,
+                2, styles, null, null);
+    }
+
+    private List<CategoryGroupDataExport> buildCategoryGroupDataExport(String dateStr) {
+        List<DatastatSortStrategy> list = datastatSortStrategyRepository.getByDateStr(dateStr);
+        List<ArticleCategory> articleCategoryList = articleCategoryRepository.getByStatusAndVersion(
+                ArticleCategoryStatusEnum.SUCCESS.getCode(), activeVersion);
+        Map<String, ArticleCategory> categoryMap = articleCategoryList.stream()
+                .collect(Collectors.toMap(ArticleCategory::getProduceContentId, Function.identity()));
+        Map<String, ArticleCategory> titleCategoryMap = articleCategoryList.stream()
+                .collect(Collectors.toMap(ArticleCategory::getTitleMd5, Function.identity(), (a, b) -> a));
+        Map<String, Map<String, CategoryGroupDataExport>> resultMap = new HashMap<>();
+        for (DatastatSortStrategy item : list) {
+            if (Objects.isNull(item.getViewCount()) || Objects.isNull(item.getFirstLevel()) || Objects.isNull(item.getFans())) {
+                continue;
+            }
+            Map<String, CategoryGroupDataExport> positionMap = resultMap.get(ContentPositionEnum.from(item.getPosition()));
+            if (MapUtils.isEmpty(positionMap)) {
+                positionMap = new HashMap<>();
+                resultMap.put(ContentPositionEnum.from(item.getPosition()), positionMap);
+            }
+            ArticleCategory articleCategory = categoryMap.get(item.getSourceId());
+            if (Objects.isNull(articleCategory)) {
+                String titleMd5 = Md5Util.encoderByMd5(item.getTitle());
+                articleCategory = titleCategoryMap.get(titleMd5);
+            }
+            if (Objects.isNull(articleCategory)) {
+                continue;
+            }
+            CategoryGroupDataExport export = positionMap.get(articleCategory.getCategory());
+            if (Objects.isNull(export)) {
+                export = new CategoryGroupDataExport();
+                export.setDateStr(dateStr);
+                export.setPosition(ContentPositionEnum.from(item.getPosition()));
+                export.setCategory(articleCategory.getCategory());
+                positionMap.put(articleCategory.getCategory(), export);
+            }
+            export.setPublishTimes(export.getPublishTimes() + 1);
+            export.setViewCount(export.getViewCount() + item.getViewCount());
+            export.setFirstLevel(export.getFirstLevel() + item.getFirstLevel());
+            export.setFission0(export.getFission0() + item.getFission0());
+            export.setFansCount(export.getFansCount() + item.getFans());
+        }
+        List<CategoryGroupDataExport> result = new ArrayList<>();
+        for (Map.Entry<String, Map<String, CategoryGroupDataExport>> entry : resultMap.entrySet()) {
+            for (Map.Entry<String, CategoryGroupDataExport> item : entry.getValue().entrySet()) {
+                CategoryGroupDataExport export = item.getValue();
+                if (export.getFansCount() != null && export.getFansCount() != 0) {
+                    export.setReadRate((double) export.getViewCount() / export.getFansCount());
+                    export.setOpenFansRate((double) export.getFirstLevel() / export.getFansCount());
+                }
+                if (export.getViewCount() != null && export.getViewCount() != 0) {
+                    export.setOpenRate(((double) export.getFirstLevel() / export.getViewCount()));
+                }
+                if (export.getFirstLevel() != null && export.getFirstLevel() != 0) {
+                    export.setFissionRate(((double) export.getFission0() / export.getFirstLevel()));
+                }
+                result.add(export);
+            }
+        }
+        result.addAll(buildCategoryGroupDataSum(dateStr, resultMap));
+        return result;
+    }
+
+    private List<CategoryGroupDataExport> buildCategoryGroupDataSum(String dateStr,
+                                                                    Map<String, Map<String, CategoryGroupDataExport>> resultMap) {
+        List<CategoryGroupDataExport> result = new ArrayList<>();
+        Map<String, CategoryGroupDataExport> sumMap = new HashMap<>();
+        for (Map.Entry<String, Map<String, CategoryGroupDataExport>> positionMap : resultMap.entrySet()) {
+            for (Map.Entry<String, CategoryGroupDataExport> categoryMap : positionMap.getValue().entrySet()) {
+                String category = categoryMap.getKey();
+                CategoryGroupDataExport export = sumMap.get(category);
+                if (Objects.isNull(export)) {
+                    export = new CategoryGroupDataExport();
+                    export.setDateStr(dateStr);
+                    export.setPosition("SUM");
+                    export.setCategory(category);
+                    sumMap.put(category, export);
+                }
+                export.setPublishTimes(export.getPublishTimes() + categoryMap.getValue().getPublishTimes());
+                export.setViewCount(export.getViewCount() + categoryMap.getValue().getViewCount());
+                export.setFirstLevel(export.getFirstLevel() + categoryMap.getValue().getFirstLevel());
+                export.setFission0(export.getFission0() + categoryMap.getValue().getFission0());
+                export.setFansCount(export.getFansCount() + categoryMap.getValue().getFansCount());
+            }
+        }
+        for (Map.Entry<String, CategoryGroupDataExport> item : sumMap.entrySet()) {
+            CategoryGroupDataExport export = item.getValue();
+            if (export.getFansCount() != null && export.getFansCount() != 0) {
+                export.setReadRate((double) export.getViewCount() / export.getFansCount());
+                export.setOpenFansRate((double) export.getFirstLevel() / export.getFansCount());
+            }
+            if (export.getViewCount() != null && export.getViewCount() != 0) {
+                export.setOpenRate(((double) export.getFirstLevel() / export.getViewCount()));
+            }
+            if (export.getFirstLevel() != null && export.getFirstLevel() != 0) {
+                export.setFissionRate(((double) export.getFission0() / export.getFirstLevel()));
+            }
+            result.add(export);
+        }
+        return result;
+    }
+
+
+    @XxlJob("categoryGroupFunnelExport")
+    public ReturnT<String> categoryGroupFunnelJob(String param) {
+        List<String> dateStrList = DateUtils.getBeforeDays(null, null, 1);
+        categoryGroupFunnel(dateStrList);
+        return ReturnT.SUCCESS;
+    }
+
+    public void categoryGroupFunnel(String dateStr) {
+        if (!StringUtils.hasText(dateStr)) {
+            dateStr = DateUtils.getBeforeDaysDateStr("yyyyMMdd", 1);
+        }
+        categoryGroupFunnel(Collections.singletonList(dateStr));
+    }
+
+    public void categoryGroupFunnel(List<String> dateStrList) {
+        List<CategoryGroupFunnelExport> exportList = new ArrayList<>();
+        dateStrList = Lists.reverse(dateStrList);
+        for (String dateStr : dateStrList) {
+            exportList.addAll(buildCategoryGroupFunnelExport(dateStr));
+        }
+        if (CollectionUtil.isEmpty(exportList)) {
+            return;
+        }
+        int rowNum = exportList.size();
+        List<List<Object>> rows = new ArrayList<>();
+        Field[] fields = CategoryGroupFunnelExport.class.getDeclaredFields();
+        for (CategoryGroupFunnelExport datum : exportList) {
+            List<Object> rowDatas = new ArrayList<>();
+            rows.add(rowDatas);
+
+            for (Field field : fields) {
+                field.setAccessible(true);
+                try {
+                    rowDatas.add(field.get(datum));
+                } catch (IllegalAccessException e) {
+                    log.error("获取值出错:{}", field.getName());
+                } catch (Exception e) {
+                    throw new RuntimeException(e.getMessage());
+                }
+            }
+        }
+
+        List<Pair<String, String>> styles = Arrays
+                .asList(
+                        Pair.of("G", "0%"),
+                        Pair.of("I", "0%"),
+                        Pair.of("J", "0%"),
+                        Pair.of("K", "0%"),
+                        Pair.of("L", "0%"),
+                        Pair.of("M", "0.00%"),
+                        Pair.of("N", "0%"),
+                        Pair.of("O", "0%"),
+                        Pair.of("P", "0.00%"),
+                        Pair.of("Q", "0.00%"),
+                        Pair.of("R", "0%")
+                );
+        doSendFeishuSheet(dateStrList, dailyDetailSheetToken, "0Lwz8A", rowNum, rows,
+                2, styles, null, null);
+    }
+
+    private List<CategoryGroupFunnelExport> buildCategoryGroupFunnelExport(String dateStr) {
+        List<CategoryGroupFunnelExport> result = new ArrayList<>();
+        Long start = DateUtils.getStartOfDay(dateStr, "yyyyMMdd") * 1000;
+        Long end = start + 86400000;
+        Date dateStart = DateUtils.getStartDateOfDay(start / 1000);
+        Date dateEnd = DateUtils.getStartDateOfDay(end / 1000);
+        // 匹配
+        List<CategoryGroupFunnelExport> matchCategoryList = longArticleBaseMapper.getCategoryMatchCount(dateStart, dateEnd);
+        Map<String, CategoryGroupFunnelExport> categoryMap = matchCategoryList.stream()
+                .collect(Collectors.toMap(CategoryGroupFunnelExport::getCategory, Function.identity()));
+        for (CategoryGroupFunnelExport item : matchCategoryList) {
+            item.setDateStr(dateStr);
+            if (Objects.nonNull(item.getMatchCount()) && item.getMatchCount() > 0) {
+                item.setMatchSuccessRate(round((double) item.getMatchSuccessCount() / item.getMatchCount(), 2));
+            }
+            result.add(item);
+        }
+        // 视频审核
+        List<CategoryGroupFunnelExport> auditCategoryList = longArticleBaseMapper.getCategoryTitleAuditCount(start, end);
+        for (CategoryGroupFunnelExport item : auditCategoryList) {
+            CategoryGroupFunnelExport export = categoryMap.get(item.getCategory());
+            if (Objects.isNull(export)) {
+                export = new CategoryGroupFunnelExport();
+                export.setDateStr(dateStr);
+                export.setCategory(item.getCategory());
+                result.add(export);
+            }
+            export.setVideoAuditPassCount(item.getVideoAuditPassCount());
+            export.setVideoAuditCount(item.getVideoAuditCount());
+            if (Objects.nonNull(export.getMatchSuccessCount()) && export.getMatchSuccessCount() > 0
+                    && Objects.nonNull(export.getVideoAuditCount())) {
+                export.setAuditRate(round((double) export.getVideoAuditCount() / export.getMatchSuccessCount(), 2));
+            }
+            if (Objects.nonNull(export.getVideoAuditCount()) && export.getVideoAuditCount() > 0) {
+                export.setVideoAuditPassRate(round((double) export.getVideoAuditPassCount() / export.getVideoAuditCount(), 2));
+            }
+        }
+        List<CategoryGroupDataExport> dataList = buildCategoryGroupDataExport(dateStr);
+        Map<String, CategoryGroupDataExport> categoryDataMap = dataList.stream().filter(o -> o.getPosition().equals("头条"))
+                .collect(Collectors.toMap(CategoryGroupDataExport::getCategory, Function.identity()));
+        long auditPassTotal = result.stream().filter(o -> Objects.nonNull(o.getVideoAuditPassCount()))
+                .mapToLong(CategoryGroupFunnelExport::getVideoAuditPassCount).sum();
+        int viewCountTotal = dataList.stream().filter(o -> o.getPosition().equals("头条"))
+                .filter(o -> Objects.nonNull(o.getViewCount()))
+                .mapToInt(CategoryGroupDataExport::getViewCount).sum();
+
+        List<String> dateStrList = DateUtils.getBeforeDays(null, dateStr, 7);
+        List<CategoryGroupDataExport> dataSevenDaysList = new ArrayList<>();
+        for (String dt : dateStrList) {
+            dataSevenDaysList.addAll(buildCategoryGroupDataExport(dt));
+        }
+        int sevenSumViewCount = dataSevenDaysList.stream().filter(o -> o.getPosition().equals("头条"))
+                .filter(o -> Objects.nonNull(o.getViewCount()))
+                .mapToInt(CategoryGroupDataExport::getViewCount).sum();
+        Map<String, List<CategoryGroupDataExport>> categoryDataSevenDaysMap = dataSevenDaysList.stream().filter(o -> o.getPosition().equals("头条"))
+                .collect(Collectors.groupingBy(CategoryGroupDataExport::getCategory));
+        double sumReadOpenFissionRate = 0;
+        for (CategoryGroupFunnelExport export : result) {
+            CategoryGroupDataExport data = categoryDataMap.get(export.getCategory());
+            if (Objects.nonNull(data)) {
+                export.setReadRate(data.getReadRate());
+                export.setOpenRate(data.getOpenRate());
+                export.setFissionRate(data.getFissionRate());
+                if (viewCountTotal > 0 && Objects.nonNull(data.getViewCount())) {
+                    export.setViewCountProportion(round((double) data.getViewCount() / viewCountTotal, 2));
+                }
+            }
+            List<CategoryGroupDataExport> sevenDaysCategoryDataList = categoryDataSevenDaysMap.get(export.getCategory());
+            if (CollectionUtil.isNotEmpty(sevenDaysCategoryDataList)) {
+                int sevenDaysViewCount = sevenDaysCategoryDataList.stream().filter(o -> Objects.nonNull(o.getViewCount()))
+                        .mapToInt(CategoryGroupDataExport::getViewCount).sum();
+                if (sevenSumViewCount > 0) {
+                    export.setSupplyDemandProportion(round((double) sevenDaysViewCount / sevenSumViewCount, 2));
+                }
+            }
+            if (Objects.nonNull(export.getVideoAuditPassCount()) && auditPassTotal > 0) {
+                export.setAuditPassTotalProportion(round((double) export.getVideoAuditPassCount() / auditPassTotal, 2));
+            }
+            if (Objects.nonNull(export.getSupplyDemandProportion())
+                    && export.getSupplyDemandProportion() > 0
+                    && Objects.nonNull(export.getAuditPassTotalProportion())) {
+                export.setAuditPassSupplyDemandRate(round(export.getAuditPassTotalProportion() / export.getSupplyDemandProportion(), 2));
+            }
+            if (Objects.nonNull(export.getReadRate()) && Objects.nonNull(export.getOpenRate()) && Objects.nonNull(export.getFissionRate())) {
+                export.setReadOpenFissionRate(export.getReadRate() * export.getOpenRate() * export.getFissionRate());
+                sumReadOpenFissionRate += export.getReadOpenFissionRate();
+            }
+        }
+        for (CategoryGroupFunnelExport item : result) {
+            if (Objects.nonNull(item.getAuditPassTotalProportion()) && Objects.nonNull(item.getReadOpenFissionRate())
+                    && item.getReadOpenFissionRate() > 0 && sumReadOpenFissionRate > 0) {
+                item.setPassReadOpenFissionRate(item.getAuditPassTotalProportion() / (item.getReadOpenFissionRate() / sumReadOpenFissionRate));
+            }
+        }
+
+        result.sort((o1, o2) -> Double.compare(o2.getReadOpenFissionRate(), o1.getReadOpenFissionRate()));
+        return result;
+    }
+
+    private double round(double value, int places) {
+        BigDecimal bd = BigDecimal.valueOf(value);
+        bd = bd.setScale(places, RoundingMode.HALF_UP);
+        return bd.doubleValue();
+    }
 }

+ 9 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/RecommendService.java

@@ -9,6 +9,7 @@ import com.tzld.longarticle.recommend.server.common.enums.recommend.ArticleTypeE
 import com.tzld.longarticle.recommend.server.common.enums.recommend.RankStrategyEnum;
 import com.tzld.longarticle.recommend.server.mapper.crawler.ArticleUserGroupMapper;
 import com.tzld.longarticle.recommend.server.model.dto.Content;
+import com.tzld.longarticle.recommend.server.model.entity.aigc.PublishPlan;
 import com.tzld.longarticle.recommend.server.model.entity.crawler.AccountAvgInfo;
 import com.tzld.longarticle.recommend.server.model.entity.crawler.PublishSortLog;
 import com.tzld.longarticle.recommend.server.model.param.RecommendParam;
@@ -17,6 +18,7 @@ import com.tzld.longarticle.recommend.server.model.vo.ArticleSortResponseData;
 import com.tzld.longarticle.recommend.server.model.vo.ArticleSortResponseDataItem;
 import com.tzld.longarticle.recommend.server.model.vo.RecommendResponse;
 import com.tzld.longarticle.recommend.server.model.vo.RecommendWithUserGroupResponse;
+import com.tzld.longarticle.recommend.server.repository.aigc.PublishPlanRepository;
 import com.tzld.longarticle.recommend.server.repository.crawler.AccountAvgInfoRepository;
 import com.tzld.longarticle.recommend.server.repository.crawler.ArticleRepository;
 import com.tzld.longarticle.recommend.server.repository.crawler.PublishSortLogRepository;
@@ -65,6 +67,8 @@ public class RecommendService {
     private ArticleUserGroupMapper articleUserGroupMapper;
     @Autowired
     private ArticleRepository articleRepository;
+    @Autowired
+    private PublishPlanRepository publishPlanRepository;
 
     @ApolloJsonValue("${accountStrategyConfig:{}}")
     private Map<String, String> accountStrategyConfigMap;
@@ -148,6 +152,11 @@ public class RecommendService {
                 param.setStrategy(RankStrategyEnum.LATE_STRATEGY.getStrategy());
             }
         }
+        // 服务号计划走服务号策略
+        PublishPlan publishPlan = publishPlanRepository.getById(param.getPlanId());
+        if (publishPlan.getName().contains("服务号")) {
+            param.setStrategy(RankStrategyEnum.FWH_STRATEGY.getStrategy());
+        }
     }
 
     public RecommendWithUserGroupResponse recommend4FwhColdStart(RecommendRequest request) {

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

@@ -0,0 +1,172 @@
+package com.tzld.longarticle.recommend.server.service.recommend.rank.strategy;
+
+
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
+import com.tzld.longarticle.recommend.server.common.enums.aigc.PublishPlanInputSourceTypesEnum;
+import com.tzld.longarticle.recommend.server.common.enums.recommend.ScoreStrategyEnum;
+import com.tzld.longarticle.recommend.server.model.dto.Content;
+import com.tzld.longarticle.recommend.server.model.entity.crawler.Article;
+import com.tzld.longarticle.recommend.server.repository.crawler.ArticleRepository;
+import com.tzld.longarticle.recommend.server.service.recommend.config.AccountContentPoolConfigService;
+import com.tzld.longarticle.recommend.server.service.recommend.config.StrategyIndexScoreWeightService;
+import com.tzld.longarticle.recommend.server.service.recommend.rank.*;
+import com.tzld.longarticle.recommend.server.service.recommend.score.AccountIndexReplacePoolConfig;
+import com.tzld.longarticle.recommend.server.service.recommend.score.ScoreResult;
+import com.tzld.longarticle.recommend.server.service.recommend.score.ScoreService;
+import com.tzld.longarticle.recommend.server.util.CommonCollectionUtils;
+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.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author dyp
+ */
+@Service
+@Slf4j
+public class RankV17Strategy implements RankStrategy {
+
+    @Autowired
+    private ScoreService scoreService;
+    @Autowired
+    private AccountContentPoolConfigService accountContentPoolConfigService;
+    @Autowired
+    private ArticleRepository articleRepository;
+    @Autowired
+    private StrategyIndexScoreWeightService weightService;
+
+    @ApolloJsonValue("${touliu.account.ghIds:[\"gh_93e00e187787\", \"gh_ac43e43b253b\", \"gh_68e7fdc09fe4\",\"gh_77f36c109fb1\", \"gh_b181786a6c8c\", \"gh_1ee2e1b39ccf\"]}")
+    private List<String> touliuAccountGhIds;
+    @Value("${topProducePlanId:}")
+    private String topProducePlanId;
+
+    public RankResult rank(RankParam param) {
+        List<Content> result = new ArrayList<>();
+
+        ScoreResult scoreResult = scoreService.score(RankStrategy.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;
+            int index = weightService.getIndex(item.getContent().getContentPoolType(), contentPools);
+            if (contentPools[0].equals(item.getContent().getContentPoolType())
+                    || contentPools[1].equals(item.getContent().getContentPoolType())) {
+                score = item.getScore(ScoreStrategyEnum.SIMILARITY.value())
+                        * weightService.getWeight(param.getStrategy(), param.getGhId(), index,
+                        ScoreStrategyEnum.SIMILARITY.value())
+                        + item.getScore(ScoreStrategyEnum.CATEGORY.value())
+                        * weightService.getWeight(param.getStrategy(), param.getGhId(), index,
+                        ScoreStrategyEnum.CATEGORY.value())
+                        + item.getScore(ScoreStrategyEnum.FLOW_CTL_DECREASE.value())
+                        + item.getScore(ScoreStrategyEnum.CRAWLER_DAYS_DECREASE_STRATEGY.value());
+                if (item.getScore(ScoreStrategyEnum.PUBLISH_TIMES.value()) >= 0) {
+                    score += item.getScore(ScoreStrategyEnum.VIEW_COUNT_RATE_V2.value())
+                            * weightService.getWeight(param.getStrategy(), param.getGhId(), index,
+                            ScoreStrategyEnum.VIEW_COUNT_RATE_V2.value());
+                }
+            } else {
+                score = item.getScore(ScoreStrategyEnum.SIMILARITY.value())
+                        * weightService.getWeight(param.getStrategy(), param.getGhId(), index,
+                        ScoreStrategyEnum.SIMILARITY.value())
+                        + item.getScore(ScoreStrategyEnum.CATEGORY.value())
+                        * weightService.getWeight(param.getStrategy(), param.getGhId(), index,
+                        ScoreStrategyEnum.CATEGORY.value())
+                        + item.getScore(ScoreStrategyEnum.ACCOUNT_PRE_DISTRIBUTE.value())
+                        + item.getScore(ScoreStrategyEnum.PUBLISH_TIMES.value())
+                        + item.getScore(ScoreStrategyEnum.CRAWLER_DAYS_DECREASE_STRATEGY.value())
+                        + item.getScore(ScoreStrategyEnum.FLOW_CTL_DECREASE.value());
+            }
+            c.setScore(score);
+            item.setScore(score);
+            return item;
+        });
+        // 相似度评分为0 报警返回
+        List<Article> hisPublishFirstArticleList = articleRepository.getByGhIdAndItemIndexAndTypeEqualsAndStatusEquals(
+                param.getGhId(), 1, param.getType(), 1);
+        if (RankStrategy.SimilarityScoreZero(items, param, hisPublishFirstArticleList)) {
+            return new RankResult(result);
+        }
+        // 安全分降权
+        RankService.safeScoreDecrease(items);
+
+        // 1 排序
+        Collections.sort(items, (o1, o2) -> -Double.compare(o1.getScore(), o2.getScore()));
+        // 2 相似去重
+        List<Content> contents = CommonCollectionUtils.toList(items, RankItem::getContent);
+
+        // 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 选文章
+        String[] publishPool = Arrays.copyOf(contentPools, contentPools.length);
+
+        // 头
+        List<Content> pool1 = contentMap.get(contentPools[0]);
+        if (CollectionUtils.isNotEmpty(pool1)) {
+            pool1 = RankService.contentSourceTypeFilter(param.getStrategy(), pool1, 1);
+        }
+        RankService.printSortLog(param.getStrategy(), param.getAccountName(), "头条", pool1);
+        if (CollectionUtils.isNotEmpty(pool1)) {
+            if (topProducePlanId.equals(pool1.get(0).getProducePlanId())) {
+                int i = RandomUtils.nextInt(0, 2);
+                if (i == 0) {
+                    for (Content content : pool1) {
+                        if (!topProducePlanId.equals(content.getProducePlanId())) {
+                            result.add(content);
+                            break;
+                        }
+                    }
+                }
+            }
+            if (CollectionUtils.isEmpty(result)) {
+                result.add(pool1.get(0));
+            }
+        } else {
+            RankStrategy.sendFeishuFirstPoolEmpty(param, contentPools[0]);
+            return new RankResult(result);
+        }
+
+        // 次
+        RankService.commonAddSecondContent(param, result, publishPool, contentPools, contentMap,
+                indexReplacePoolConfigMap, param.getStrategy());
+
+        // 3-8
+        // RankService.commonAdd38Content(param, result, contentPools, contentMap, param.getStrategy());
+        List<Content> pool = contentMap.get(contentPools[2]);
+        if (CollectionUtils.isNotEmpty(pool)) {
+            Integer videoSourceType = PublishPlanInputSourceTypesEnum.longArticleVideoPoolSource.getVal();
+            Queue<Content> videoPoolQueue = pool.stream().filter(o -> Objects.equals(o.getSourceType(), videoSourceType))
+                    .collect(Collectors.toCollection(LinkedList::new));
+            Queue<Content> otherPoolQueue = pool.stream().filter(o -> !Objects.equals(o.getSourceType(), videoSourceType))
+                    .collect(Collectors.toCollection(LinkedList::new));
+            for (int i = 3; i < param.getSize() + 1; i++) {
+                Integer sourceType = RankService.getStrategyPoolSourceType(param.getStrategy(), i);
+                if (Objects.equals(sourceType, videoSourceType) && !videoPoolQueue.isEmpty()) {
+                    result.add(videoPoolQueue.poll());
+                } else if (!otherPoolQueue.isEmpty()) {
+                    result.add(otherPoolQueue.poll());
+                }
+            }
+        }
+
+        RankStrategy.deduplication(result, contentMap, publishPool);
+
+        return new RankResult(result);
+    }
+
+}

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

@@ -216,7 +216,7 @@ public class RecallService implements ApplicationContextAware {
         }
         long t3 = System.currentTimeMillis();
         // 标题历史均值
-        setTitleAvgViewCount(content, param.getGhId(), param.getType());
+        setTitleAvgViewCount(content, param.getGhId(), param.getType(), param.getStrategy());
         long t4 = System.currentTimeMillis();
         CostMonitor.logCost("Recall", "SetAvgViewCount", t4 - t3);
         // 视频内容池查询抓取时间
@@ -230,12 +230,14 @@ public class RecallService implements ApplicationContextAware {
         }
         // 长文内容池审核过滤
         List<String> sourceIds = content.stream()
-                .filter(o -> !Objects.equals(o.getSourceType(), PublishPlanInputSourceTypesEnum.longArticleVideoPoolSource.getVal()))
+                .filter(o -> (Objects.equals(o.getSourceType(), PublishPlanInputSourceTypesEnum.producePlan.getVal())
+                        || Objects.equals(o.getSourceType(), PublishPlanInputSourceTypesEnum.produceContent.getVal())))
                 .map(Content::getSourceId).collect(Collectors.toList());
         List<LongArticleTitleAudit> titleAuditList = titleAuditRepository.getByContentIdIn(sourceIds);
         Map<String, Integer> titleAuditMap = titleAuditList.stream()
                 .collect(Collectors.toMap(LongArticleTitleAudit::getContentId, LongArticleTitleAudit::getStatus));
-        content.removeIf(o -> !Objects.equals(o.getSourceType(), PublishPlanInputSourceTypesEnum.longArticleVideoPoolSource.getVal())
+        content.removeIf(o -> (Objects.equals(o.getSourceType(), PublishPlanInputSourceTypesEnum.producePlan.getVal())
+                || Objects.equals(o.getSourceType(), PublishPlanInputSourceTypesEnum.produceContent.getVal()))
                 && (Objects.isNull(titleAuditMap.get(o.getSourceId()))
                 || titleAuditMap.get(o.getSourceId()) != ArticleVideoAuditStatusEnum.PASS.getCode()));
         if (CollectionUtils.isEmpty(content)) {
@@ -331,7 +333,7 @@ public class RecallService implements ApplicationContextAware {
         }
     }
 
-    public void setTitleAvgViewCount(List<Content> contentList, String ghId, String type) {
+    public void setTitleAvgViewCount(List<Content> contentList, String ghId, String type, String strategy) {
         long start = System.currentTimeMillis();
         contentList.forEach(content -> content.setTitleMd5(Md5Util.encoderByMd5(content.getTitle())));
         List<String> sourceIdList = contentList.stream().map(Content::getSourceId).distinct().collect(Collectors.toList());
@@ -369,11 +371,18 @@ public class RecallService implements ApplicationContextAware {
                 content.setHisPublishArticleList(hisPublishArticleList);
                 setT0Data(content, ghId);
             } else {
-                if (!newCacheSourceIdSet.contains(content.getSourceId())) {
+                if (!newCacheSourceIdSet.contains(content.getSourceId())
+                        && Arrays.asList(PublishPlanInputSourceTypesEnum.producePlan.getVal(),
+                        PublishPlanInputSourceTypesEnum.produceContent.getVal(),
+                        PublishPlanInputSourceTypesEnum.longArticleVideoPoolSource.getVal()
+                ).contains(content.getSourceType())) {
                     newCacheSaveList.add(content);
                     newCacheSourceIdSet.add(content.getSourceId());
                 }
             }
+            if (Objects.isNull(content.getContentPoolType()) && RankStrategyEnum.FWH_STRATEGY.getStrategy().equals(strategy)) {
+                content.setContentPoolType(ContentPoolEnum.autoArticlePoolLevel1.getContentPool());
+            }
         }
         // 写入缓存
         saveArticleTitleHisCache(newCacheSaveList, type);
@@ -440,9 +449,8 @@ public class RecallService implements ApplicationContextAware {
         Set<String> appMsgIds = hisArticleList.stream().map(Article::getAppMsgId).collect(Collectors.toSet());
         List<Article> firstIndexHisArticleList = articleMapper.getByGhIdInAndAppMsgIdInAndItemIndexAndTypeEqualsAndStatusEquals(
                 ghIds, appMsgIds, 1, type, 1);
-        Map<String, Map<String, Map<String, Article>>> firstIndexHisArticleMap = firstIndexHisArticleList.stream()
-                .collect(Collectors.groupingBy(Article::getGhId, Collectors.groupingBy(Article::getAppMsgId,
-                        Collectors.toMap(Article::getTitle, o -> o, (a, b) -> a))));
+        Map<String, Map<String, Article>> firstIndexHisArticleMap = firstIndexHisArticleList.stream()
+                .collect(Collectors.groupingBy(Article::getGhId, Collectors.toMap(Article::getAppMsgId, o -> o, (a, b) -> a)));
         // 获取发布账号 位置历史均值
         List<AccountAvgInfo> accountAvgInfoList = crawlerBaseMapper.getAllByGhIdIn(ghIds);
         Map<String, Map<String, Map<String, AccountAvgInfo>>> accountAvgInfoIndexMap = accountAvgInfoList.stream()
@@ -485,7 +493,7 @@ public class RecallService implements ApplicationContextAware {
         Map<String, ArticleCategory> titleCategoryMap = articleCategoryList.stream()
                 .collect(Collectors.toMap(ArticleCategory::getTitleMd5, Function.identity(), (a, b) -> a));
         // 获取生成计划
-        List<ProducePlanExeRecord> planExeRecordList = producePlanExeRecordRepository.findByPlanExeIdIn(sourceIds);
+        List<ProducePlanExeRecord> planExeRecordList = producePlanExeRecordRepository.getByPlanExeIdIn(sourceIds);
         Map<String, ProducePlanExeRecord> planExeRecordMap = planExeRecordList.stream()
                 .collect(Collectors.toMap(ProducePlanExeRecord::getPlanExeId, Function.identity()));
         // 根据sourceId查询kimiSafeScore
@@ -585,6 +593,7 @@ public class RecallService implements ApplicationContextAware {
                 article.setArticleDetailInfoList(articleDetailInfoMap.get(hisArticle.getWxSn()));
                 // 设置账号位置阅读均值
                 int avgViewCount = 0;
+                double readAvgCiUpper = 0.0;
                 Map<String, Map<String, AccountAvgInfo>> dateAvgMap = accountAvgInfoIndexMap.get(hisArticle.getGhId());
                 String hisPublishDate = DateUtils.timestampToYMDStr(article.getPublishTimestamp(), "yyyy-MM-dd");
                 if (Objects.nonNull(dateAvgMap)) {
@@ -595,6 +604,8 @@ public class RecallService implements ApplicationContextAware {
                         article.setInnerAccount(true);
                         avgViewCount = Optional.ofNullable(indexMap.get(hisArticle.getItemIndex().toString()).getReadAvg())
                                 .orElse(0.0).intValue();
+                        readAvgCiUpper = Optional.ofNullable(indexMap.get(hisArticle.getItemIndex().toString()).getReadAvgCiUpper())
+                                .orElse(0.0).intValue();
                     } else {
                         if (ArticleTypeEnum.QUNFA.getVal().equals(type)) {
                             log.error("历史表现阅读均值获取失败 ghId:{} accountName:{} date:{} index:{}",
@@ -610,6 +621,7 @@ public class RecallService implements ApplicationContextAware {
                     }
                 }
                 article.setAvgViewCount(avgViewCount);
+                article.setReadAvgCiUpper(readAvgCiUpper);
                 if (Objects.nonNull(article.getAvgViewCount()) && article.getAvgViewCount() > 0
                         && Objects.nonNull(article.getViewCount())) {
                     article.setViewCountRate((article.getViewCount() * 1.0) / article.getAvgViewCount());
@@ -623,16 +635,13 @@ public class RecallService implements ApplicationContextAware {
                         article.setFans(firstIndexAvgInfo.getFans());
                     }
                 }
-                Map<String, Map<String, Article>> firstIndexArticle = firstIndexHisArticleMap.get(hisArticle.getGhId());
+                Map<String, Article> firstIndexArticle = firstIndexHisArticleMap.get(hisArticle.getGhId());
                 if (Objects.nonNull(firstIndexArticle) && firstIndexArticle.containsKey(hisArticle.getAppMsgId())) {
-                    Map<String, Article> firstAppMsgIdArticle = firstIndexArticle.get(hisArticle.getAppMsgId());
-                    if (Objects.nonNull(firstAppMsgIdArticle) && firstAppMsgIdArticle.containsKey(hisArticle.getTitle())) {
-                        Article firstArticle = firstAppMsgIdArticle.get(hisArticle.getTitle());
-                        article.setFirstViewCount(firstArticle.getShowViewCount());
-                        if (Objects.nonNull(firstIndexAvgInfo) && Objects.nonNull(firstIndexAvgInfo.getReadAvg())
-                                && firstIndexAvgInfo.getReadAvg() > 0 && Objects.nonNull(firstArticle.getShowViewCount())) {
-                            article.setFirstViewCountRate((firstArticle.getShowViewCount() * 1.0) / firstIndexAvgInfo.getReadAvg());
-                        }
+                    Article firstArticle = firstIndexArticle.get(hisArticle.getAppMsgId());
+                    article.setFirstViewCount(firstArticle.getShowViewCount());
+                    if (Objects.nonNull(firstIndexAvgInfo) && Objects.nonNull(firstIndexAvgInfo.getReadAvg())
+                            && firstIndexAvgInfo.getReadAvg() > 0 && Objects.nonNull(firstArticle.getShowViewCount())) {
+                        article.setFirstViewCountRate((firstArticle.getShowViewCount() * 1.0) / firstIndexAvgInfo.getReadAvg());
                     }
                 }
                 res.getHisPublishArticleList().add(article);

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

@@ -128,11 +128,13 @@ public class ScoreService implements ApplicationContextAware {
                 || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV13.getStrategy())
                 || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV14.getStrategy())
                 || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV15.getStrategy())
-                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV16.getStrategy())) {
+                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV16.getStrategy())
+                || StringUtils.equals(param.getStrategy(), RankStrategyEnum.ArticleRankV17.getStrategy())) {
             strategies.add(strategyMap.get(ScoreStrategyEnum.CATEGORY.value()));
             strategies.add(strategyMap.get(ScoreStrategyEnum.ACCOUNT_PRE_DISTRIBUTE.value()));
             strategies.add(strategyMap.get(ScoreStrategyEnum.FLOW_CTL_DECREASE.value()));
             strategies.add(strategyMap.get(ScoreStrategyEnum.VIEW_COUNT_RATE.value()));
+            strategies.add(strategyMap.get(ScoreStrategyEnum.VIEW_COUNT_RATE_V2.value()));
             strategies.add(strategyMap.get(ScoreStrategyEnum.VIEW_COUNT_RATE_CORRELATION.value()));
             strategies.add(strategyMap.get(ScoreStrategyEnum.PUBLISH_TIMES.value()));
 //            strategies.add(strategyMap.get(ScoreStrategyEnum.HIS_FISSION_FANS_RATE_RATE.value()));

+ 3 - 3
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/service/recommend/score/strategy/SimilarityStrategy.java

@@ -2,7 +2,7 @@ package com.tzld.longarticle.recommend.server.service.recommend.score.strategy;
 
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.tzld.longarticle.recommend.server.model.dto.kimi.KimiResult;
-import com.tzld.longarticle.recommend.server.remote.KimiApiService;
+import com.tzld.longarticle.recommend.server.remote.DeepSeekApiService;
 import com.tzld.longarticle.recommend.server.remote.NLPRemoteService;
 import com.tzld.longarticle.recommend.server.repository.crawler.ArticleRepository;
 import com.tzld.longarticle.recommend.server.service.recommend.score.Score;
@@ -35,7 +35,7 @@ public class SimilarityStrategy implements ScoreStrategy {
     @Autowired
     private ArticleRepository articleRepository;
     @Autowired
-    private KimiApiService kimiApiService;
+    private DeepSeekApiService deepSeekApiService;
 
     @Autowired
     private RedisTemplate<String, String> redisTemplate;
@@ -67,7 +67,7 @@ public class SimilarityStrategy implements ScoreStrategy {
                 if (!StringUtils.hasText(type)) {
                     String prompt = kimiSimilarityTypePrompt.replace("accountName", param.getAccountName());
                     // 调用kimi判断账号类型
-                    KimiResult kimiResult = kimiApiService.requestOfficialApi(prompt, null, null, false);
+                    KimiResult kimiResult = deepSeekApiService.requestOfficialApi(prompt, null, null, false);
                     if (kimiResult.isSuccess()) {
                         try {
                             type = kimiResult.getResponse().getChoices().get(0).getMessage().getContent();

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

@@ -0,0 +1,161 @@
+package com.tzld.longarticle.recommend.server.service.recommend.score.strategy;
+
+import com.tzld.longarticle.recommend.server.model.dto.Content;
+import com.tzld.longarticle.recommend.server.model.dto.ContentHisPublishArticle;
+import com.tzld.longarticle.recommend.server.model.entity.crawler.AccountAvgInfo;
+import com.tzld.longarticle.recommend.server.repository.crawler.AccountAvgInfoRepository;
+import com.tzld.longarticle.recommend.server.service.recommend.config.AccountContentPoolConfigService;
+import com.tzld.longarticle.recommend.server.service.recommend.config.AccountIndexAvgViewCountService;
+import com.tzld.longarticle.recommend.server.service.recommend.score.Score;
+import com.tzld.longarticle.recommend.server.service.recommend.score.ScoreParam;
+import com.tzld.longarticle.recommend.server.service.recommend.score.ScoreStrategy;
+import com.tzld.longarticle.recommend.server.util.MathUtils;
+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;
+import java.util.Objects;
+
+@Component
+@Slf4j
+public class ViewCountRateV2Strategy 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 readAvgCiUpperSum = 0D;
+                // 头条阅读量之和
+                double showViewCountSumFirst = 0D;
+                // 头条阅读均值置信区间上限之和
+                double readAvgCiUpperSumFirst = 0D;
+                // 次条阅读量之和
+                double showViewCountSumSecond = 0D;
+                // 次条阅读均值置信区间上限之和
+                double readAvgCiUpperSumSecond = 0D;
+                // 最大阅读均值置信区间上限
+                double maxReadAvgCiUpper = 0D;
+                if (CollectionUtils.isEmpty(content.getHisPublishArticleList())) {
+                    continue;
+                }
+                for (ContentHisPublishArticle hisItem : content.getHisPublishArticleList()) {
+                    // 过滤掉发布时间晚于19点数据
+                    if (ScoreStrategy.hisContentLateFilter(hisItem.getPublishTimestamp())) {
+                        continue;
+                    }
+                    if (hisItem.isInnerAccount() && Objects.nonNull(hisItem.getViewCount())
+                            && hisItem.getViewCount() > 0 && Objects.nonNull(hisItem.getReadAvgCiUpper())
+                            && hisItem.getReadAvgCiUpper() > 0) {
+                        maxReadAvgCiUpper = Math.max(maxReadAvgCiUpper, hisItem.getReadAvgCiUpper());
+                        if (hisItem.getItemIndex() == 1) {
+                            showViewCountSumFirst += hisItem.getViewCount();
+                            readAvgCiUpperSumFirst += hisItem.getReadAvgCiUpper();
+                        } else if (hisItem.getItemIndex() == 2) {
+                            if (Objects.nonNull(hisItem.getFirstViewCount()) &&  hisItem.getFirstViewCount() > 0 &&
+                                    Objects.nonNull(hisItem.getFirstViewCountRate()) && hisItem.getFirstViewCountRate() > 0) {
+                                showViewCountSumSecond += hisItem.getViewCount();
+                                if (hisItem.getFirstViewCountRate() > 1) {
+                                    // 对于头条均值倍数大于1的情况,次条均值线性增加,用于debias;
+                                    // TODO: 对于小于1的情况,是否要减去?
+                                    readAvgCiUpperSumSecond += hisItem.getReadAvgCiUpper() * hisItem.getFirstViewCountRate();
+                                } else {
+                                    readAvgCiUpperSumSecond += hisItem.getReadAvgCiUpper();
+                                }
+                            }
+                        } else {
+                            if (Objects.nonNull(hisItem.getFirstViewCount()) && hisItem.getFirstViewCount() > 0
+                                    && Objects.nonNull(hisItem.getFirstViewCountRate()) && hisItem.getFirstViewCountRate() > 0) {
+                                showViewCountSum += hisItem.getViewCount();
+                                if (hisItem.getFirstViewCountRate() > 1) {
+                                    // 对于头条均值倍数大于1的情况,次条均值线性增加,用于debias;
+                                    // TODO: 对于小于1的情况,是否要减去?
+                                    readAvgCiUpperSum += hisItem.getReadAvgCiUpper() * hisItem.getFirstViewCountRate();
+                                } else {
+                                    readAvgCiUpperSum += hisItem.getReadAvgCiUpper();
+                                }
+                            }
+                        }
+                    }
+                }
+                double viewCountRate = 0D; // 设置默认值
+                double bigRateW = 1D;
+                // 如果有头条反馈数据,优先选取头条反馈数据;
+                if (showViewCountSumFirst > 0) {
+                    showViewCountSum = showViewCountSumFirst;
+                    readAvgCiUpperSum = readAvgCiUpperSumFirst;
+                } else if (showViewCountSumSecond > 0) {
+                    showViewCountSum = showViewCountSumSecond;
+                    readAvgCiUpperSum = readAvgCiUpperSumSecond;
+                    // 如果是大号头条,则降权
+                    if (avgViewCountFirst >= 3000 && i == 0) {
+                        bigRateW = 0.001D;
+                    }
+                }
+                // 均值倍数
+                if (readAvgCiUpperSum > 0) {
+                    viewCountRate = showViewCountSum / readAvgCiUpperSum;
+                }
+                // 置信度
+                double viewCountRateW = MathUtils.sigmoid(readAvgCiUpperSum, 0.0002, 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;
+            }
+        }
+        return scores;
+    }
+}

+ 21 - 0
long-article-recommend-service/src/main/java/com/tzld/longarticle/recommend/server/web/recommend/DataDashboardController.java

@@ -85,4 +85,25 @@ public class DataDashboardController {
         }).start();
     }
 
+    @GetMapping("/export/dailyAuditProduce")
+    public void dailyAuditProduce(String dateStr) {
+        new Thread(() -> {
+            service.dailyAuditProduce(dateStr);
+        }).start();
+    }
+
+    @GetMapping("/export/categoryGroupData")
+    public void categoryGroupData(String dateStr) {
+        new Thread(() -> {
+            service.categoryGroupData(dateStr);
+        }).start();
+    }
+
+    @GetMapping("/export/categoryGroupFunnel")
+    public void categoryGroupFunnel(String dateStr) {
+        new Thread(() -> {
+            service.categoryGroupFunnel(dateStr);
+        }).start();
+    }
+
 }

+ 18 - 1
long-article-recommend-service/src/main/resources/mapper/aigc/AigcBaseMapper.xml

@@ -364,9 +364,10 @@
     <select id="getVideoPoolPlatformCrawlerCount" resultType="java.lang.Long">
         select count(1)
         from crawler_content cc
+        join crawler_plan_result_rel cprr on cc.channel_content_id = cprr.channel_source_id
         join crawler_content_video_pool_relation ccvpr on cc.channel_content_id = ccvpr.channel_content_id
         where ccvpr.platform = #{platform}
-        and cc.crawler_timestamp between #{start} and #{end}
+        and cprr.create_timestamp between #{start} and #{end}
     </select>
 
     <select id="getVideoPoolPlatformProduceCount" resultType="java.lang.Long">
@@ -411,4 +412,20 @@
         </foreach>
     </select>
 
+    <select id="getAccountDailyProduceAuditCount"
+            resultType="com.tzld.longarticle.recommend.server.model.vo.DailyAuditProduceExport">
+        select audit.audit_account as name, audit.cnt as auditCount, auditPass.cnt as auditPassCount
+        from (select audit_account, count(1) as cnt
+              from produce_plan_exe_record
+              where audit_timestamp between #{startTimeStamp} and #{endTimeStamp}
+              group by audit_account) audit
+        left join (select audit_account, count(1) as cnt
+                from produce_plan_exe_record
+                where audit_timestamp between #{startTimeStamp} and #{endTimeStamp}
+                  and audit_status = 1
+                group by audit_account) auditPass
+        on audit.audit_account = auditPass.audit_account
+        where audit.audit_account != 'wangyunpeng'
+    </select>
+
 </mapper>

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

@@ -80,7 +80,7 @@
 
     <select id="getAllByGhIdIn"
             resultType="com.tzld.longarticle.recommend.server.model.entity.crawler.AccountAvgInfo">
-        select gh_id, position, update_time, fans, read_avg
+        select gh_id, position, update_time, fans, read_avg, read_avg_ci_upper
         from account_avg_info_v3
         where gh_id in
         <foreach collection="ghIdList" item="item" separator="," open="(" close=")">

+ 13 - 9
long-article-recommend-service/src/main/resources/mapper/longArticle/ArticleAuditMapper.xml

@@ -243,33 +243,37 @@
         limit 1
     </select>
 
-    <select id="articleVideoWatingAuditList"
+    <select id="articleVideoWaitingAuditList"
             resultType="com.tzld.longarticle.recommend.server.model.vo.ArticleVideoAuditListVO">
-        select lata.content_id, lata.status, lat.article_title as title, lat.kimi_title,
-        lata.audit_account, lata.audit_timestamp, lata.flow_pool_level
-        from long_articles_title_audit lata
-        left join long_articles_text lat on lata.content_id = lat.content_id
+        select content_id, status, audit_account, audit_timestamp, flow_pool_level
+        from long_articles_title_audit
         <where>
             <if test="status!= null and status.size() > 0">
-                and lata.status in
+                and status in
                 <foreach collection="status" item="item" separator="," open="(" close=")">
                     #{item}
                 </foreach>
             </if>
             <if test="flowPoolLevels!= null and flowPoolLevels.size() > 0">
-                and lata.flow_pool_level in
+                and flow_pool_level in
                 <foreach collection="flowPoolLevels" item="item" separator="," open="(" close=")">
                     #{item}
                 </foreach>
             </if>
             <if test="excludeContentIds!= null and excludeContentIds.size() > 0">
-                and lata.content_id not in
+                and content_id not in
                 <foreach collection="excludeContentIds" item="item" separator="," open="(" close=")">
                     #{item}
                 </foreach>
             </if>
+            <if test="producePlanIds!= null and producePlanIds.size() > 0">
+                and produce_plan_id in
+                <foreach collection="producePlanIds" item="item" separator="," open="(" close=")">
+                    #{item}
+                </foreach>
+            </if>
         </where>
-        order by lata.content_id desc
+        order by content_id desc
         limit #{size}
     </select>
 

+ 74 - 0
long-article-recommend-service/src/main/resources/mapper/longArticle/LongArticleBaseMapper.xml

@@ -111,6 +111,19 @@
         </foreach>
     </select>
 
+    <select id="getArticlePromotionCandidates"
+            resultType="com.tzld.longarticle.recommend.server.model.entity.longArticle.DatastatSortStrategy">
+        select *
+        from datastat_sort_strategy
+        where type = 9
+        and fans > #{fans}
+        and date_str > #{dateStr}
+        and position in
+        <foreach collection="positions" item="item" open="(" close=")" separator=",">
+            #{item}
+        </foreach>
+    </select>
+
     <insert id="batchInsertLongArticlesRootSourceId">
         INSERT INTO long_articles_root_source_id (root_source_id, account_name, gh_id, article_title, request_time,
         trace_id, push_type, video_id)
@@ -349,4 +362,65 @@
         (#{videoOriginId}, #{title}, #{ossPath})
     </insert>
 
+    <select id="getAccountDailyVideoAuditCount"
+            resultType="com.tzld.longarticle.recommend.server.model.vo.DailyAuditProduceExport">
+        select audit.audit_account as name, audit.cnt as auditCount, auditPass.cnt as auditPassCount,
+               videoAudit.cnt as videoAuditCount, videoAuditPass.cnt as videoAuditPassCount
+        from (select audit_account, count(1) as cnt
+              from long_articles_title_audit
+              where audit_timestamp between #{startTimeStamp} and #{endTimeStamp}
+              group by audit_account) audit
+        left join (select audit_account, count(1) as cnt
+                from long_articles_title_audit
+                where audit_timestamp between #{startTimeStamp} and #{endTimeStamp}
+                  and status = 1
+                group by audit_account) auditPass
+                  on audit.audit_account = auditPass.audit_account
+        left join (select audit_account, count(1) as cnt
+              from long_articles_crawler_videos
+              where audit_timestamp between #{startTimeStamp} and #{endTimeStamp}
+              group by audit_account) videoAudit
+        on audit.audit_account = videoAudit.audit_account
+        left join (select audit_account, count(1) as cnt
+                from long_articles_crawler_videos
+                where audit_timestamp between #{startTimeStamp} and #{endTimeStamp}
+                  and status = 1
+                group by audit_account) videoAuditPass
+        on audit.audit_account = videoAuditPass.audit_account
+    </select>
+
+    <select id="getCategoryMatchCount"
+            resultType="com.tzld.longarticle.recommend.server.model.vo.CategoryGroupFunnelExport">
+        select success.category, success.cnt as matchSuccessCount, total.cnt as matchCount
+        from (select ac.category, count(1) as cnt
+              from long_articles_text lat
+                       join article_category ac on lat.content_id = ac.produce_content_id
+              where lat.start_processing_time between #{start} and #{end}
+                and lat.result_status = 1
+              group by ac.category) success
+        left join (select ac.category, count(1) as cnt
+                   from long_articles_text lat
+                            join article_category ac on lat.content_id = ac.produce_content_id
+                   where lat.start_processing_time between #{start} and #{end}
+                     and lat.result_status in (1, 2)
+                   group by ac.category) total on total.category = success.category
+    </select>
+
+    <select id="getCategoryTitleAuditCount"
+            resultType="com.tzld.longarticle.recommend.server.model.vo.CategoryGroupFunnelExport">
+        select success.category, success.cnt as videoAuditPassCount, total.cnt as videoAuditCount
+        from (select ac.category, count(1) as cnt
+              from long_articles_title_audit lata
+               join article_category ac on lata.content_id = ac.produce_content_id
+              where lata.audit_timestamp between #{start} and #{end}
+                and lata.`status` = 1
+              group by ac.category) success
+        left join (select ac.category, count(1) as cnt
+                from long_articles_title_audit lata
+                 join article_category ac on lata.content_id = ac.produce_content_id
+                where lata.audit_timestamp between #{start} and #{end}
+                  and lata.`status` in (1, 2)
+                group by ac.category) total on total.category = success.category
+    </select>
+
 </mapper>

+ 1 - 1
long-article-recommend-service/src/test/java/com/tzld/longarticle/recommend/server/ArticleVideoAuditTest.java

@@ -65,7 +65,7 @@ public class ArticleVideoAuditTest {
     public void updateTitleAuditFlowPoolLevel() {
         List<LongArticleTitleAudit> list = titleAuditRepository.getByFlowPoolLevelIsNull();
         List<String> contentIds = list.stream().map(LongArticleTitleAudit::getContentId).collect(Collectors.toList());
-        List<ProducePlanExeRecord> contentList = exeRecordRepository.findByPlanExeIdIn(contentIds);
+        List<ProducePlanExeRecord> contentList = exeRecordRepository.getByPlanExeIdIn(contentIds);
         Map<String, String> contentPlanMap = contentList.stream().collect(Collectors.toMap(ProducePlanExeRecord::getPlanExeId, ProducePlanExeRecord::getPlanId));
         List<String> planIds = contentList.stream().map(ProducePlanExeRecord::getPlanId).collect(Collectors.toList());
         List<ProducePlan> planList = producePlanRepository.findByIdIn(planIds);

+ 2 - 1
long-article-recommend-service/src/test/java/com/tzld/longarticle/recommend/server/ScoreStrategyTest.java

@@ -2,6 +2,7 @@ package com.tzld.longarticle.recommend.server;
 
 import com.alibaba.fastjson.JSONObject;
 import com.tzld.longarticle.recommend.server.common.enums.recommend.ArticleTypeEnum;
+import com.tzld.longarticle.recommend.server.common.enums.recommend.RankStrategyEnum;
 import com.tzld.longarticle.recommend.server.model.dto.Content;
 import com.tzld.longarticle.recommend.server.service.recommend.recall.RecallService;
 import com.tzld.longarticle.recommend.server.service.recommend.score.Score;
@@ -52,7 +53,7 @@ public class ScoreStrategyTest {
         content.setCrawlerChannelContentId("0065561559007185d167afdd61b9ac5a");
         contentList.add(content);
         param.setContents(contentList);
-        recallService.setTitleAvgViewCount(contentList, "gh_bff0bcb0694a", ArticleTypeEnum.QUNFA.getVal());
+        recallService.setTitleAvgViewCount(contentList, "gh_bff0bcb0694a", ArticleTypeEnum.QUNFA.getVal(), RankStrategyEnum.ArticleRankV16.getStrategy());
         System.out.println(JSONObject.toJSONString(param.getContents().get(0).getHisPublishArticleList()));
         List<Score> result = viewCountRateStrategy.score(param);
         System.out.println(JSONObject.toJSONString(result));

+ 9 - 0
long-article-recommend-service/src/test/java/com/tzld/longarticle/recommend/server/XxlJobTest.java

@@ -15,6 +15,7 @@ import com.tzld.longarticle.recommend.server.model.vo.ProduceContentCrawlerVO;
 import com.tzld.longarticle.recommend.server.repository.longArticle.ArticleCategoryRepository;
 import com.tzld.longarticle.recommend.server.repository.longArticle.ArticlePoolPromotionSourceRepository;
 import com.tzld.longarticle.recommend.server.service.recommend.ArticleService;
+import com.tzld.longarticle.recommend.server.service.recommend.DataDashboardService;
 import com.tzld.longarticle.recommend.server.util.Md5Util;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
@@ -39,6 +40,9 @@ public class XxlJobTest {
     @Resource
     private ArticleCategoryRepository articleCategoryRepository;
 
+    @Resource
+    private DataDashboardService dataDashboardService;
+
     @ApolloJsonValue("${cold.pool.produce.planId:[\"20240802021606053813696\", \"20240802080355355308981\",\n" +
             "\"20240805154433785506170\", \"20240805154359027876170\", \"20241024100016206421084\", " +
             "\"20241030070010871546586\"]}")
@@ -107,4 +111,9 @@ public class XxlJobTest {
         List<PublishAccountTypeDTO> accountTypeList = publishContentMapper.getAccountTypeList(null);
         System.out.println(JSONObject.toJSONString(accountTypeList));
     }
+
+    @Test
+    public void coverStatisticDataExportTest() {
+        dataDashboardService.coverStatisticTestDataExport("20250413");
+    }
 }