Selaa lähdekoodia

20250513-晋级策略2开发

luojunhui 2 kuukautta sitten
vanhempi
commit
4b992b856f

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

@@ -132,22 +132,63 @@ public class ArticlePromotionService {
         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());
+
+            // 计算完整公式
+            double result = readRate * firstReadRate * (numerator / denominator);
+
+            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;
+        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】")) {
-            list = longArticleBaseMapper.getArticlePromotionCandidates(10000, dateStrFilter, positionFilter);
-
-            // 使用阅读均值倍数+阅读量晋级
-            List<DatastatSortStrategy> listStrategy1 = list.stream()
-                    .filter(o -> o.getReadRate() >= 1.33 && o.getViewCount() >= 100)
-                    .collect(Collectors.toList());
-
             // 使用显著性检验晋级
-            List<DatastatSortStrategy> listStrategy2 = list.stream()
+            listStrategyV2 = list.stream()
                     .filter(o -> {
                         try {
                             return isExperimentGroupStatisticallySuperior(o.getAvgViewCount() * 1.1 * 30, o.getFans() * 30,
@@ -159,26 +200,26 @@ public class ArticlePromotionService {
                         }
                     })
                     .collect(Collectors.toList());
-
-            list = Stream.concat(listStrategy1.stream(), listStrategy2.stream())
-                    .collect(Collectors.collectingAndThen(
-                            Collectors.toMap(
-                                    DatastatSortStrategy::getWxSn,
-                                    Function.identity(),
-                                    (existing, replacement) -> existing
-                            ),
-                            map -> new ArrayList<>(map.values())
-                    ));
         } else {
-            list = longArticleBaseMapper.getArticlePromotion(viewCountFilter, viewCountRateFilter,
-                    10000, dateStrFilter, positionFilter);
+            // 使用新规则过滤
+            listStrategyV2 = promotionWithReadOpenFissionRate(0.01, list);
         }
+        listStrategyV2 = listStrategyV2.stream().filter(o -> !StrategyV1WxSn.contains(o.getWxSn())).collect(Collectors.toList());
+
+        //策略 1 晋级
+        filterAndAdd2CrawlerPlan(listStrategyV1, "promotionStrategyV1", accountNickName, tag, pos, way, today);
+
+        //策略 2 晋级
+        filterAndAdd2CrawlerPlan(listStrategyV2, "promotionStrategyV2", 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) {
@@ -217,7 +258,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);
@@ -264,7 +305,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)) {
@@ -274,14 +315,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(),
@@ -292,13 +333,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) {