瀏覽代碼

feat:添加头部视频同品类渠道召回

zhaohaipeng 6 天之前
父節點
當前提交
983e7a0425
共有 13 個文件被更改,包括 775 次插入320 次删除
  1. 2 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/model/RecommendParam.java
  2. 40 1
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/RecommendService.java
  3. 1 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/RankParam.java
  4. 44 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/extractor/ExtractVideoMergeCate.java
  5. 90 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/processor/RankProcessorBoost.java
  6. 25 1
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelBasic.java
  7. 1 85
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV536.java
  8. 1 81
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV564.java
  9. 379 72
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV566.java
  10. 1 80
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV568.java
  11. 1 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallParam.java
  12. 4 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallService.java
  13. 186 0
      recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/HeadCate2AndChannelRovRecallStrategy.java

+ 2 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/model/RecommendParam.java

@@ -47,5 +47,7 @@ public class RecommendParam {
     private Integer userShareDepth;
 
     private int pageNum;
+
+    private String channelName;
 }
 

+ 40 - 1
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/RecommendService.java

@@ -22,6 +22,7 @@ import com.tzld.piaoquan.recommend.server.service.recall.RecallResult;
 import com.tzld.piaoquan.recommend.server.service.recall.RecallService;
 import com.tzld.piaoquan.recommend.server.service.recall.strategy.*;
 import com.tzld.piaoquan.recommend.server.util.CommonCollectionUtils;
+import com.tzld.piaoquan.recommend.server.util.FeatureUtils;
 import com.tzld.piaoquan.recommend.server.util.JSONUtils;
 import com.tzld.piaoquan.recommend.server.util.TraceUtils;
 import lombok.extern.slf4j.Slf4j;
@@ -38,6 +39,7 @@ import javax.annotation.PostConstruct;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
+import java.util.regex.Matcher;
 import java.util.stream.Collectors;
 
 /**
@@ -73,6 +75,8 @@ public class RecommendService {
     @ApolloJsonValue("${risk.scene:[]}")
     private Set<Long> riskScenes;
 
+    public static final String channelGroupPrefix = "alg_recsys_user_channel_group";
+
     private LoadingCache<String, Set<String>> riskUserCache = CacheBuilder.newBuilder()
             .maximumSize(5)
             .refreshAfterWrite(300, TimeUnit.SECONDS)
@@ -302,6 +306,9 @@ public class RecommendService {
         param.setDataKey("data66");
         param.setRankKeyPrefix("rank:score1:");
 
+        String channelName = this.getChannelName(request.getRootSourceId());
+        param.setChannelName(channelName);
+
         // 实验配置
         if (request.getAbExpCodeCount() > 0) {
             Set<String> abExpCodes = request.getAbExpCodeList().stream().collect(Collectors.toSet());
@@ -417,7 +424,7 @@ public class RecommendService {
         recallParam.setClientIp(param.getClientIp());
         recallParam.setRootSourceId(param.getRootSourceId());
         recallParam.setUserShareDepth(param.getUserShareDepth());
-
+        recallParam.setChannelName(param.getChannelName());
         return recallParam;
     }
 
@@ -444,6 +451,7 @@ public class RecommendService {
         rankParam.setRootSourceId(param.getRootSourceId());
         rankParam.setUserShareDepth(param.getUserShareDepth());
         rankParam.setPageNum(param.getPageNum());
+        rankParam.setChannelName(param.getChannelName());
         return rankParam;
     }
 
@@ -502,4 +510,35 @@ public class RecommendService {
         consumer.accept(RegionRelative24HDupRecallStrategy.PUSH_FORM);
     }
 
+    private String getChannelName(String s) {
+        Matcher matcher = FeatureUtils.getChannelMatcher(s);
+        if (null != matcher && matcher.find()) {
+            switch (matcher.group(0)) {
+                case "longArticles_":
+                    return getChannelType(s);
+                case "dyyjs_":
+                    return "公众号代运营-即转";
+                case "touliu_tencent_":
+                    return "小程序投流";
+                case "touliu_tencentgzh_":
+                case "touliu_tencentGzhArticle_":
+                case "GzhTouLiu_Articles_gh":
+                    return "公众号投流";
+                default:
+                    return "";
+            }
+        }
+        return "";
+    }
+
+    private String getChannelType(String s) {
+        String key = String.format("%s:%s", channelGroupPrefix, s);
+        String value = redisTemplate.opsForValue().get(key);
+        if (null != value && value.equals("公众号买号")) {
+            return value;
+        }
+        return "公众号代运营-Daily";
+    }
+
+
 }

+ 1 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/RankParam.java

@@ -34,4 +34,5 @@ public class RankParam {
     private Integer userShareDepth;
     private int pageNum;
 
+    private String channelName;
 }

+ 44 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/extractor/ExtractVideoMergeCate.java

@@ -0,0 +1,44 @@
+package com.tzld.piaoquan.recommend.server.service.rank.extractor;
+
+import com.tzld.piaoquan.recommend.server.model.Video;
+import com.tzld.piaoquan.recommend.server.service.FeatureService;
+import com.tzld.piaoquan.recommend.server.service.ServiceBeanFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ExtractVideoMergeCate {
+
+    public static void addMergeCate(List<Video> rovList, List<Video> flowVideos) {
+
+        FeatureService featureService = ServiceBeanFactory.getBean(FeatureService.class);
+
+        List<String> vids = new ArrayList<>();
+        rovList.stream().map(Video::getVideoId).map(String::valueOf).forEach(vids::add);
+        flowVideos.stream().map(Video::getVideoId).map(String::valueOf).forEach(vids::add);
+        Map<String, Map<String, Map<String, String>>> videoBaseInfo = featureService.getVideoBaseInfo(null, vids);
+
+        for (Video video : rovList) {
+            String vidStr = String.valueOf(video.getVideoId());
+
+            String mergeCate2 = ExtractVideoMergeCate.parseMergeCate2(vidStr, videoBaseInfo);
+            video.getMergeCateList().add(mergeCate2);
+
+        }
+    }
+
+    private static String parseMergeCate2(String vidStr, Map<String, Map<String, Map<String, String>>> featureMap) {
+        if (!featureMap.containsKey(vidStr)) {
+            return "";
+        }
+
+        Map<String, Map<String, String>> vidFeature = featureMap.get(vidStr);
+        if (!vidFeature.containsKey("alg_vid_feature_basic_info")) {
+            return "";
+        }
+
+        Map<String, String> basicInfoMap = vidFeature.get("alg_vid_feature_basic_info");
+        return basicInfoMap.getOrDefault("merge_second_level_cate", "");
+    }
+}

+ 90 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/processor/RankProcessorBoost.java

@@ -1,10 +1,16 @@
 package com.tzld.piaoquan.recommend.server.service.rank.processor;
 
 import com.tzld.piaoquan.recommend.server.model.Video;
+import com.tzld.piaoquan.recommend.server.util.DateUtils;
 import com.tzld.piaoquan.recommend.server.util.ProbabilityCalculator;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
 
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -14,6 +20,7 @@ import java.util.Map;
  *
  * @author sunxy
  */
+@Slf4j
 public class RankProcessorBoost {
 
     public static void boostByTag(List<Video> rovList, Map<String, Map<String, String>> rulesMap) {
@@ -51,4 +58,87 @@ public class RankProcessorBoost {
 
     }
 
+    public static void boostByMergeCate(List<Video> rovList, Map<String, List<Map<String, String>>> rulesMap) {
+        for (Video video : rovList) {
+            if (CollectionUtils.isEmpty(video.getMergeCateList())) {
+                continue;
+            }
+
+            // 遍历所有品类【目前只有一个品类】,找到第一个需要提权或降权的品类
+            for (String mergeCate : video.getMergeCateList()) {
+                double reduceCoefficient = RankProcessorBoost.parseReduceCoefficient(mergeCate, rulesMap);
+                double originScore = video.getScore();
+
+                video.setScore(originScore * reduceCoefficient);
+
+                video.getScoresMap().put("reduceCoefficient", reduceCoefficient);
+            }
+
+        }
+
+        rovList.sort((o1, o2) -> Double.compare(o2.getSortScore(), o1.getSortScore()));
+
+    }
+
+    private static double parseReduceCoefficient(String mergeCate2, Map<String, List<Map<String, String>>> reduceConfigMap) {
+        if (StringUtils.isBlank(mergeCate2) || MapUtils.isEmpty(reduceConfigMap)) {
+            return 1d;
+        }
+
+        List<Map<String, String>> configMaps = reduceConfigMap.getOrDefault(mergeCate2, new ArrayList<>());
+        for (Map<String, String> configMap : configMaps) {
+            boolean currentHourIsNeedReduce = RankProcessorBoost.currentHourIsNeedReduce(configMap);
+            boolean currentDateIsNeedReduce = RankProcessorBoost.currentDateIsNeedReduce(configMap);
+            if (currentHourIsNeedReduce || currentDateIsNeedReduce) {
+                return Double.parseDouble(configMap.getOrDefault("reduce_coefficient", "1"));
+            }
+        }
+
+        return 1d;
+    }
+
+
+    /**
+     * 当前小时是否需要降权
+     */
+    private static boolean currentHourIsNeedReduce(Map<String, String> configMap) {
+        if (!configMap.containsKey("reduce_hour")) {
+            return false;
+        }
+        try {
+
+            int currentHour = LocalDateTime.now().getHour();
+            String[] reduceHours = configMap.get("reduce_hour").split(",");
+            for (String hourRange : reduceHours) {
+                String[] split = hourRange.split("-");
+                int h1 = Integer.parseInt(split[0]);
+                int h2 = (split.length == 2) ? Integer.parseInt(split[1]) : h1;
+
+                if (currentHour >= h1 && currentHour <= h2) {
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            log.error("564 error parse reduce hour config error. config content: {}, \n", configMap, e);
+        }
+        return false;
+    }
+
+    /**
+     * 当前时间段是否需要降权
+     */
+    private static boolean currentDateIsNeedReduce(Map<String, String> configMap) {
+        if (!configMap.containsKey("reduce_date")) {
+            return false;
+        }
+
+        String[] reduceDates = configMap.get("reduce_date").split(",");
+        for (String s : reduceDates) {
+            if (DateUtils.ifTimeRangeInNow(s)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }

+ 25 - 1
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelBasic.java

@@ -7,12 +7,14 @@ import com.tzld.piaoquan.recommend.server.model.Video;
 import com.tzld.piaoquan.recommend.server.service.rank.RankParam;
 import com.tzld.piaoquan.recommend.server.service.rank.RankResult;
 import com.tzld.piaoquan.recommend.server.service.rank.RankService;
+import com.tzld.piaoquan.recommend.server.service.rank.extractor.ExtractVideoMergeCate;
 import com.tzld.piaoquan.recommend.server.service.rank.extractor.ExtractorUtils;
 import com.tzld.piaoquan.recommend.server.service.rank.extractor.RankExtractorItemTags;
 import com.tzld.piaoquan.recommend.server.service.rank.processor.*;
 import com.tzld.piaoquan.recommend.server.util.JSONUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
 import org.apache.commons.lang3.RandomUtils;
 import org.springframework.stereotype.Service;
 
@@ -32,6 +34,10 @@ import java.util.stream.Collectors;
 public abstract class RankStrategy4RegionMergeModelBasic extends RankService {
     @ApolloJsonValue("${RankStrategy4DensityFilterV2:}")
     private Map<String, Map<String, Map<String, String>>> filterRules = new HashMap<>();
+
+    @ApolloJsonValue("${RankReduceByMergeCateConfig:{}}")
+    private Map<String, Map<String, List<Map<String, String>>>> rankReduceConfig = new HashMap<>();
+
     String CLASS_NAME = this.getClass().getSimpleName();
 
     public void duplicate(Set<Long> setVideo, List<Video> videos) {
@@ -61,6 +67,9 @@ public abstract class RankStrategy4RegionMergeModelBasic extends RankService {
         // 2 根据实验号解析阿波罗参数。
         Set<String> abExpCodes = param.getAbExpCodes();
         Map<String, Map<String, String>> rulesMap = Collections.emptyMap();
+
+        Map<String, List<Map<String, String>>> rankReduceRulesMap = Collections.emptyMap();
+
         if (CollectionUtils.isNotEmpty(abExpCodes)) {
             for (Map.Entry<String, Map<String, Map<String, String>>> entry : this.filterRules.entrySet()) {
                 if (abExpCodes.contains(entry.getKey())) {
@@ -68,6 +77,13 @@ public abstract class RankStrategy4RegionMergeModelBasic extends RankService {
                     break;
                 }
             }
+
+            for (Map.Entry<String, Map<String, List<Map<String, String>>>> entry : this.rankReduceConfig.entrySet()) {
+                if (abExpCodes.contains(entry.getKey())) {
+                    rankReduceRulesMap = entry.getValue();
+                    break;
+                }
+            }
         }
 
 
@@ -81,12 +97,20 @@ public abstract class RankStrategy4RegionMergeModelBasic extends RankService {
             RankProcessorTagFilter.processor(rovVideos, flowVideos, rulesMap);
         }
 
+
+        if (MapUtils.isNotEmpty(rankReduceRulesMap)) {
+            ExtractVideoMergeCate.addMergeCate(rovVideos, flowVideos);
+        }
+
         // 4 rov池提权功能
         RankProcessorBoost.boostByTag(rovVideos, rulesMap);
 
         // 5 rov池强插功能
         RankProcessorInsert.insertByTag(param, rovVideos, rulesMap);
 
+        // 6.品类降权
+        RankProcessorBoost.boostByMergeCate(rovVideos, rankReduceRulesMap);
+
         // 7 流量池按比例强插
         List<Video> result = new ArrayList<>();
         for (int i = 0; i < param.getTopK() && i < rovVideos.size(); i++) {
@@ -322,7 +346,7 @@ public abstract class RankStrategy4RegionMergeModelBasic extends RankService {
             double vorPower = mergeWeight.getOrDefault("vor_power", 0d);
             item.getScoresMap().put("vorPower", vorPower);
             vor = Math.pow(originVor, vorPower);
-        }else if (calcVorMode == 4d){
+        } else if (calcVorMode == 4d) {
             vor = 1.0;
         }
 

+ 1 - 85
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV536.java

@@ -339,18 +339,12 @@ public class RankStrategy4RegionMergeModelV536 extends RankStrategy4RegionMergeM
             double vor = Double.parseDouble(vid2MapFeature.getOrDefault(item.getVideoId() + "", new HashMap<>()).getOrDefault("vor", "0"));
             item.getScoresMap().put("vor", vor);
 
-            String vidStr = String.valueOf(item.getVideo().getVideoId());
-            String mergeCate2 = this.parseMergeCate2(vidStr, featureOriginVideo);
-            double reduceCoefficient = this.parseReduceCoefficient(mergeCate2, reduceConfigMap);
-            item.getScoresMap().put("reduceCoefficient", reduceCoefficient);
-
-            score = fmRov * (0.1 + hasReturnRovScore) * (0.1 + vor) * reduceCoefficient;
+            score = fmRov * (0.1 + hasReturnRovScore) * (0.1 + vor) ;
             Video video = item.getVideo();
             video.setScore(score);
             video.setSortScore(score);
             video.setScoresMap(item.getScoresMap());
             video.setAllFeatureMap(item.getAllFeatureMap());
-            video.getMergeCateList().add(mergeCate2);
             if (MapUtils.isNotEmpty(feature.getVideoFeature()) && MapUtils.isNotEmpty(feature.getVideoFeature().get(item.getVideoId() + ""))) {
                 video.getMetaFeatureMap().putAll(feature.getVideoFeature().get(item.getVideoId() + ""));
             }
@@ -362,82 +356,4 @@ public class RankStrategy4RegionMergeModelV536 extends RankStrategy4RegionMergeM
         result.sort(Comparator.comparingDouble(o -> -o.getSortScore()));
         return result;
     }
-
-
-    public double parseReduceCoefficient(String mergeCate2, Map<String, List<Map<String, String>>> reduceConfigMap) {
-        if (StringUtils.isBlank(mergeCate2) || MapUtils.isEmpty(reduceConfigMap)) {
-            return 1d;
-        }
-
-        List<Map<String, String>> configMaps = reduceConfigMap.getOrDefault(mergeCate2, new ArrayList<>());
-        for (Map<String, String> configMap : configMaps) {
-            boolean currentHourIsNeedReduce = currentHourIsNeedReduce(configMap);
-            boolean currentDateIsNeedReduce = currentDateIsNeedReduce(configMap);
-            if (currentHourIsNeedReduce || currentDateIsNeedReduce) {
-                return Double.parseDouble(configMap.getOrDefault("reduce_coefficient", "1"));
-            }
-        }
-
-        return 1d;
-    }
-
-
-    /**
-     * 当前小时是否需要降权
-     */
-    private boolean currentHourIsNeedReduce(Map<String, String> configMap) {
-        if (!configMap.containsKey("reduce_hour")) {
-            return false;
-        }
-        try {
-
-            int currentHour = LocalDateTime.now().getHour();
-            String[] reduceHours = configMap.get("reduce_hour").split(",");
-            for (String hourRange : reduceHours) {
-                String[] split = hourRange.split("-");
-                int h1 = Integer.parseInt(split[0]);
-                int h2 = (split.length == 2) ? Integer.parseInt(split[1]) : h1;
-
-                if (currentHour >= h1 && currentHour <= h2) {
-                    return true;
-                }
-            }
-        } catch (Exception e) {
-            log.error("536 error parse reduce hour config error. config content: {}, \n", configMap, e);
-        }
-        return false;
-    }
-
-    /**
-     * 当前时间段是否需要降权
-     */
-    private boolean currentDateIsNeedReduce(Map<String, String> configMap) {
-        if (!configMap.containsKey("reduce_date")) {
-            return false;
-        }
-
-        String[] reduceDates = configMap.get("reduce_date").split(",");
-        for (String s : reduceDates) {
-            if (DateUtils.ifTimeRangeInNow(s)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
-    private String parseMergeCate2(String vidStr, Map<String, Map<String, Map<String, String>>> featureMap) {
-        if (!featureMap.containsKey(vidStr)) {
-            return "";
-        }
-
-        Map<String, Map<String, String>> vidFeature = featureMap.get(vidStr);
-        if (!vidFeature.containsKey("alg_vid_feature_basic_info")) {
-            return "";
-        }
-
-        Map<String, String> basicInfoMap = vidFeature.get("alg_vid_feature_basic_info");
-        return basicInfoMap.getOrDefault("merge_second_level_cate", "");
-    }
 }

+ 1 - 81
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV564.java

@@ -154,10 +154,7 @@ public class RankStrategy4RegionMergeModelV564 extends RankStrategy4RegionMergeM
             double vor = Double.parseDouble(vid2MapFeature.getOrDefault(item.getVideoId() + "", new HashMap<>()).getOrDefault("vor", "0"));
             item.getScoresMap().put("vor", vor);
 
-            String vidStr = String.valueOf(item.getVideo().getVideoId());
-            String mergeCate2 = this.parseMergeCate2(vidStr, featureOriginVideo);
-            double reduceCoefficient = this.parseReduceCoefficient(mergeCate2, reduceConfigMap);
-            score = fmRov * (0.1 + newNorXGBScore) * (0.1 + vor) * reduceCoefficient;
+            score = fmRov * (0.1 + newNorXGBScore) * (0.1 + vor);
 
             Video video = item.getVideo();
             video.setScore(score);
@@ -357,81 +354,4 @@ public class RankStrategy4RegionMergeModelV564 extends RankStrategy4RegionMergeM
             setVideo.addAll(list.stream().map(Video::getVideoId).collect(Collectors.toSet()));
         }
     }
-
-    public double parseReduceCoefficient(String mergeCate2, Map<String, List<Map<String, String>>> reduceConfigMap) {
-        if (StringUtils.isBlank(mergeCate2) || MapUtils.isEmpty(reduceConfigMap)) {
-            return 1d;
-        }
-
-        List<Map<String, String>> configMaps = reduceConfigMap.getOrDefault(mergeCate2, new ArrayList<>());
-        for (Map<String, String> configMap : configMaps) {
-            boolean currentHourIsNeedReduce = currentHourIsNeedReduce(configMap);
-            boolean currentDateIsNeedReduce = currentDateIsNeedReduce(configMap);
-            if (currentHourIsNeedReduce || currentDateIsNeedReduce) {
-                return Double.parseDouble(configMap.getOrDefault("reduce_coefficient", "1"));
-            }
-        }
-
-        return 1d;
-    }
-
-
-    /**
-     * 当前小时是否需要降权
-     */
-    private boolean currentHourIsNeedReduce(Map<String, String> configMap) {
-        if (!configMap.containsKey("reduce_hour")) {
-            return false;
-        }
-        try {
-
-            int currentHour = LocalDateTime.now().getHour();
-            String[] reduceHours = configMap.get("reduce_hour").split(",");
-            for (String hourRange : reduceHours) {
-                String[] split = hourRange.split("-");
-                int h1 = Integer.parseInt(split[0]);
-                int h2 = (split.length == 2) ? Integer.parseInt(split[1]) : h1;
-
-                if (currentHour >= h1 && currentHour <= h2) {
-                    return true;
-                }
-            }
-        } catch (Exception e) {
-            log.error("564 error parse reduce hour config error. config content: {}, \n", configMap, e);
-        }
-        return false;
-    }
-
-    /**
-     * 当前时间段是否需要降权
-     */
-    private boolean currentDateIsNeedReduce(Map<String, String> configMap) {
-        if (!configMap.containsKey("reduce_date")) {
-            return false;
-        }
-
-        String[] reduceDates = configMap.get("reduce_date").split(",");
-        for (String s : reduceDates) {
-            if (DateUtils.ifTimeRangeInNow(s)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
-    private String parseMergeCate2(String vidStr, Map<String, Map<String, Map<String, String>>> featureMap) {
-        if (!featureMap.containsKey(vidStr)) {
-            return "";
-        }
-
-        Map<String, Map<String, String>> vidFeature = featureMap.get(vidStr);
-        if (!vidFeature.containsKey("alg_vid_feature_basic_info")) {
-            return "";
-        }
-
-        Map<String, String> basicInfoMap = vidFeature.get("alg_vid_feature_basic_info");
-        return basicInfoMap.getOrDefault("merge_second_level_cate", "");
-    }
 }

+ 379 - 72
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV566.java

@@ -1,115 +1,422 @@
 package com.tzld.piaoquan.recommend.server.service.rank.strategy;
 
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
+import com.tzld.piaoquan.recommend.server.common.ThreadPoolFactory;
+import com.tzld.piaoquan.recommend.server.common.base.RankItem;
 import com.tzld.piaoquan.recommend.server.model.Video;
 import com.tzld.piaoquan.recommend.server.service.FeatureService;
 import com.tzld.piaoquan.recommend.server.service.rank.RankParam;
+import com.tzld.piaoquan.recommend.server.service.rank.extractor.ExtractorUtils;
+import com.tzld.piaoquan.recommend.server.service.recall.strategy.*;
+import com.tzld.piaoquan.recommend.server.service.score.ScorerUtils;
+import com.tzld.piaoquan.recommend.server.util.CommonCollectionUtils;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.math3.util.Pair;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 @Service
 @Slf4j
 public class RankStrategy4RegionMergeModelV566 extends RankStrategy4RegionMergeModelBasic {
     @ApolloJsonValue("${rank.score.merge.weightv566:}")
     private Map<String, Double> mergeWeight;
-
-    @Autowired
-    private RankStrategy4RegionMergeModelV563 modelV563Service;
-
     @Autowired
     private FeatureService featureService;
 
     @Override
     public List<Video> mergeAndRankRovRecall(RankParam param) {
         Map<String, Double> mergeWeight = this.mergeWeight != null ? this.mergeWeight : new HashMap<>(0);
+        //-------------------融-------------------
+        //-------------------合-------------------
+        //-------------------逻-------------------
+        //-------------------辑-------------------
+
+        List<Video> oldRovs = new ArrayList<>();
+        oldRovs.addAll(extractAndSort(param, RegionHRecallStrategy.PUSH_FORM));
+        oldRovs.addAll(extractAndSort(param, RegionHDupRecallStrategy.PUSH_FORM));
+        oldRovs.addAll(extractAndSort(param, Region24HRecallStrategy.PUSH_FORM));
+        oldRovs.addAll(extractAndSort(param, RegionRelative24HRecallStrategy.PUSH_FORM));
+        oldRovs.addAll(extractAndSort(param, RegionRelative24HDupRecallStrategy.PUSH_FORM));
+        removeDuplicate(oldRovs);
+        int sizeReturn = param.getSize();
+        List<Video> v0 = oldRovs.size() <= sizeReturn
+                ? oldRovs
+                : oldRovs.subList(0, sizeReturn);
+        Set<Long> setVideo = new HashSet<>();
+        this.duplicate(setVideo, v0);
+        setVideo.addAll(v0.stream().map(Video::getVideoId).collect(Collectors.toSet()));
+        List<Video> rovRecallRank = new ArrayList<>(v0);
+        //-------------------return相似召回------------------
+        List<Video> v6 = extractAndSort(param, ReturnVideoRecallStrategy.PUSH_FORM);
+        v6 = v6.stream().filter(r -> !setVideo.contains(r.getVideoId())).collect(Collectors.toList());
+        v6 = v6.subList(0, Math.min(mergeWeight.getOrDefault("v6", 5.0).intValue(), v6.size()));
+        rovRecallRank.addAll(v6);
+        setVideo.addAll(v6.stream().map(Video::getVideoId).collect(Collectors.toSet()));
+        //-------------------新地域召回------------------
+        List<Video> v1 = extractAndSort(param, RegionRealtimeRecallStrategyV1.PUSH_FORM);
+        v1 = v1.stream().filter(r -> !setVideo.contains(r.getVideoId())).collect(Collectors.toList());
+        v1 = v1.subList(0, Math.min(mergeWeight.getOrDefault("v1", 5.0).intValue(), v1.size()));
+        rovRecallRank.addAll(v1);
+        setVideo.addAll(v1.stream().map(Video::getVideoId).collect(Collectors.toSet()));
+        //-------------------scene cf rovn------------------
+        List<Video> sceneCFRovn = extractAndSort(param, SceneCFRovnRecallStrategy.PUSH_FORM);
+        sceneCFRovn = sceneCFRovn.stream().filter(r -> !setVideo.contains(r.getVideoId())).collect(Collectors.toList());
+        sceneCFRovn = sceneCFRovn.subList(0, Math.min(mergeWeight.getOrDefault("sceneCFRovn", 5.0).intValue(), sceneCFRovn.size()));
+        rovRecallRank.addAll(sceneCFRovn);
+        setVideo.addAll(sceneCFRovn.stream().map(Video::getVideoId).collect(Collectors.toSet()));
+        //-------------------scene cf rosn------------------
+        List<Video> sceneCFRosn = extractAndSort(param, SceneCFRosnRecallStrategy.PUSH_FORM);
+        sceneCFRosn = sceneCFRosn.stream().filter(r -> !setVideo.contains(r.getVideoId())).collect(Collectors.toList());
+        sceneCFRosn = sceneCFRosn.subList(0, Math.min(mergeWeight.getOrDefault("sceneCFRosn", 5.0).intValue(), sceneCFRosn.size()));
+        rovRecallRank.addAll(sceneCFRosn);
+        setVideo.addAll(sceneCFRosn.stream().map(Video::getVideoId).collect(Collectors.toSet()));
+
+        //-------------------head cate2 of rovn------------------
+        List<Video> headCate2Rov = extractAndSort(param, HeadCate2AndChannelRovRecallStrategy.PUSH_FROM);
+        // 视频去重
+        removeDuplicate(headCate2Rov);
+        headCate2Rov = headCate2Rov.stream().filter(o -> !setVideo.contains(o.getVideoId())).collect(Collectors.toList());
+        headCate2Rov = headCate2Rov.subList(0, Math.min(mergeWeight.getOrDefault("headCate2AndChannelRov", 5.0).intValue(), headCate2Rov.size()));
+        rovRecallRank.addAll(headCate2Rov);
+        setVideo.addAll(headCate2Rov.stream().map(Video::getVideoId).collect(Collectors.toSet()));
+
+        //-------------------排-------------------
+        //-------------------序-------------------
+        //-------------------逻-------------------
+        //-------------------辑-------------------
+
+        // 1 批量获取特征  省份参数要对齐  headvid  要传递过来!
+        List<String> vids = CommonCollectionUtils.toListDistinct(rovRecallRank, v -> String.valueOf(v.getVideoId()));
+
+        // k1:视频、k2:表、k3:特征、v:特征值
+        String provinceCn = param.getProvince().replaceAll("省$", "");
+        String headVid = String.valueOf(param.getHeadVid());
+        FeatureService.Feature feature = featureService.getFeature(param.getMid(), vids,
+                String.valueOf(param.getAppType()), provinceCn, headVid);
+        Map<String, Map<String, String>> featureOriginUser = feature.getUserFeature();
+        Map<String, Map<String, Map<String, String>>> featureOriginVideo = feature.getVideoFeature();
+
 
-        List<Video> result = modelV563Service.mergeAndRankRovRecall(param);
-        if (null != result && !result.isEmpty()) {
-            try {
-                Integer versionCode = param.getVersionCode();
-                int orderVersionCode = mergeWeight.getOrDefault("orderVersionCode", 1500D).intValue();
-                if (null != versionCode && versionCode == orderVersionCode) {
-                    int minBid = mergeWeight.getOrDefault("minBid", 39D).intValue();
-                    int keepPageNum = mergeWeight.getOrDefault("keepPageNum", 1D).intValue();
-                    if (param.getPageNum() > keepPageNum && highlyActiveUser(param.getMid(), minBid)) {
-                        int group1Size = mergeWeight.getOrDefault("group1Size", 4D).intValue();
-                        int group2Size = mergeWeight.getOrDefault("group2Size", 8D).intValue();
-                        int group3Size = mergeWeight.getOrDefault("group3Size", 0D).intValue();
-                        return resort(group1Size, group2Size, group3Size, result);
+        // 2 特征处理
+        Map<String, Double> userFeatureMapDouble = new HashMap<>();
+        String mid = param.getMid();
+        Map<String, String> c1 = featureOriginUser.getOrDefault("alg_mid_feature_play", new HashMap<>());
+        Map<String, String> c2 = featureOriginUser.getOrDefault("alg_mid_feature_share_and_return", new HashMap<>());
+        Map<String, String> c3 = featureOriginUser.getOrDefault("alg_mid_feature_play_tags", new HashMap<>());
+        Map<String, String> c4 = featureOriginUser.getOrDefault("alg_mid_feature_return_tags", new HashMap<>());
+        Map<String, String> c5 = featureOriginUser.getOrDefault("alg_mid_feature_share_tags", new HashMap<>());
+        Map<String, String> c6 = featureOriginUser.getOrDefault("alg_mid_feature_feed_exp_share_tags_v2", new HashMap<>());
+        Map<String, String> c7 = featureOriginUser.getOrDefault("alg_mid_feature_feed_exp_return_tags_v2", new HashMap<>());
+        Map<String, String> c8 = featureOriginUser.getOrDefault("alg_mid_feature_sharecf", new HashMap<>());
+        Map<String, String> c9 = featureOriginUser.getOrDefault("alg_mid_feature_returncf", new HashMap<>());
+
+        if (!c1.isEmpty()) {
+            userFeatureMapDouble.put("playcnt_6h", Double.parseDouble(c1.getOrDefault("playcnt_6h", "0")));
+            userFeatureMapDouble.put("playcnt_1d", Double.parseDouble(c1.getOrDefault("playcnt_1d", "0")));
+            userFeatureMapDouble.put("playcnt_3d", Double.parseDouble(c1.getOrDefault("playcnt_3d", "0")));
+            userFeatureMapDouble.put("playcnt_7d", Double.parseDouble(c1.getOrDefault("playcnt_7d", "0")));
+        }
+        if (!c2.isEmpty()) {
+            userFeatureMapDouble.put("share_pv_12h", Double.parseDouble(c2.getOrDefault("share_pv_12h", "0")));
+            userFeatureMapDouble.put("share_pv_1d", Double.parseDouble(c2.getOrDefault("share_pv_1d", "0")));
+            userFeatureMapDouble.put("share_pv_3d", Double.parseDouble(c2.getOrDefault("share_pv_3d", "0")));
+            userFeatureMapDouble.put("share_pv_7d", Double.parseDouble(c2.getOrDefault("share_pv_7d", "0")));
+            userFeatureMapDouble.put("return_uv_12h", Double.parseDouble(c2.getOrDefault("return_uv_12h", "0")));
+            userFeatureMapDouble.put("return_uv_1d", Double.parseDouble(c2.getOrDefault("return_uv_1d", "0")));
+            userFeatureMapDouble.put("return_uv_3d", Double.parseDouble(c2.getOrDefault("return_uv_3d", "0")));
+            userFeatureMapDouble.put("return_uv_7d", Double.parseDouble(c2.getOrDefault("return_uv_7d", "0")));
+        }
+
+        Map<String, String> c34567Map = new HashMap<>(15);
+        List<Tuple2> tmpList0 = Arrays.asList(
+                new Tuple2(c3, "c3_feature"),
+                new Tuple2(c4, "c4_feature"),
+                new Tuple2(c5, "c5_feature"),
+                new Tuple2(c6, "c6_feature"),
+                new Tuple2(c7, "c7_feature")
+        );
+        for (Tuple2 tuple2 : tmpList0) {
+            for (String key_time : Arrays.asList("tags_1d", "tags_3d", "tags_7d")) {
+                String tags = tuple2.first.getOrDefault(key_time, "");
+                if (!tags.isEmpty()) {
+                    c34567Map.put(tuple2.name + "_" + key_time, tags);
+                }
+            }
+        }
+
+        Map<String, Map<String, String[]>> c89Map = new HashMap<>(4);
+        List<Tuple2> tmpList1 = Arrays.asList(
+                new Tuple2(c8, "c8_feature"),
+                new Tuple2(c9, "c9_feature")
+        );
+        for (Tuple2 tuple2 : tmpList1) {
+            for (String key_action : Arrays.asList("share", "return")) {
+                String cfListStr = tuple2.first.getOrDefault(key_action, "");
+                if (!cfListStr.isEmpty()) {
+                    Map<String, String[]> cfMap = new HashMap<>();
+                    String[] entries = cfListStr.split(",");
+                    for (String entry : entries) {
+                        String[] rList = entry.split(":");
+                        if (rList.length >= 4) { // 确保分割后有四个元素
+                            String key = rList[0];
+                            String value1 = rList[1];
+                            String value2 = rList[2];
+                            String value3 = rList[3];
+                            String[] strs = {value1, value2, value3};
+                            cfMap.put(key, strs);
+                        }
                     }
+                    c89Map.put(tuple2.name + "_" + key_action, cfMap);
                 }
-            } catch (Exception e) {
-                log.error("diversity resort error", e);
             }
         }
-        return result;
-    }
 
-    private List<Video> resort(int group1Size, int group2Size, int group3Size, List<Video> videos) {
-        // split
-        List<Video> group1 = new ArrayList<>();
-        List<Video> group2 = new ArrayList<>();
-        List<Video> group3 = new ArrayList<>();
-        List<Video> group4 = new ArrayList<>();
-        for (int i = 0; i < videos.size(); i++) {
-            if (i < group1Size) {
-                group1.add(videos.get(i));
-            } else if (i < group2Size) {
-                group2.add(videos.get(i));
-            } else if (i < group3Size) {
-                group3.add(videos.get(i));
-            } else {
-                group4.add(videos.get(i));
+
+        List<RankItem> rankItems = CommonCollectionUtils.toList(rovRecallRank, RankItem::new);
+        for (RankItem item : rankItems) {
+            Map<String, Double> featureMap = new HashMap<>();
+            String vid = item.getVideoId() + "";
+            Map<String, String> b1 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_all_exp_v2", new HashMap<>());
+            Map<String, String> b2 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_all_share", new HashMap<>());
+            Map<String, String> b3 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_all_return", new HashMap<>());
+            Map<String, String> b6 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_exp2share_v2", new HashMap<>());
+            Map<String, String> b7 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_share2return", new HashMap<>());
+
+            Map<String, String> b8 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_noflow_exp_v2", new HashMap<>());
+            Map<String, String> b9 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_noflow_root_share_v2", new HashMap<>());
+            Map<String, String> b10 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_noflow_root_return_v2", new HashMap<>());
+            Map<String, String> b11 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_flow_exp_v2", new HashMap<>());
+            Map<String, String> b12 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_flow_root_share_v2", new HashMap<>());
+            Map<String, String> b13 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_flow_root_return_v2", new HashMap<>());
+            Map<String, String> b17 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_province_exp_v2", new HashMap<>());
+            Map<String, String> b18 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_province_root_share_v2", new HashMap<>());
+            Map<String, String> b19 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_feed_province_root_return_v2", new HashMap<>());
+
+            List<Tuple4> originData = Arrays.asList(
+                    new Tuple4(b1, b2, b3, "b123"),
+                    new Tuple4(b1, b6, b7, "b167"),
+                    new Tuple4(b8, b9, b10, "b8910"),
+                    new Tuple4(b11, b12, b13, "b111213"),
+                    new Tuple4(b17, b18, b19, "b171819")
+            );
+
+            for (Tuple4 tuple4 : originData) {
+                for (String prefix2 : Arrays.asList("1h", "2h", "3h", "4h", "12h", "1d", "3d", "7d")) {
+                    double exp = tuple4.first.isEmpty() ? 0 : Double.parseDouble(tuple4.first.getOrDefault("exp_pv_" + prefix2, "0.0"));
+                    double share = tuple4.second.isEmpty() ? 0 : Double.parseDouble(tuple4.second.getOrDefault("share_pv_" + prefix2, "0.0"));
+                    double returns = tuple4.third.isEmpty() ? 0 : Double.parseDouble(tuple4.third.getOrDefault("return_uv_" + prefix2, "0.0"));
+
+                    double f1 = ExtractorUtils.calDiv(share, exp);
+                    double f2 = ExtractorUtils.calLog(share);
+                    double f3 = ExtractorUtils.calDiv(returns, exp);
+                    double f4 = ExtractorUtils.calLog(returns);
+                    double f5 = f3 * f4;
+
+                    String key1 = tuple4.name + "_" + prefix2 + "_" + "STR";
+                    String key2 = tuple4.name + "_" + prefix2 + "_" + "log(share)";
+                    String key3 = tuple4.name + "_" + prefix2 + "_" + "ROV";
+                    String key4 = tuple4.name + "_" + prefix2 + "_" + "log(return)";
+                    String key5 = tuple4.name + "_" + prefix2 + "_" + "ROV*log(return)";
+
+                    featureMap.put(key1, f1);
+                    featureMap.put(key2, f2);
+                    featureMap.put(key3, f3);
+                    featureMap.put(key4, f4);
+                    featureMap.put(key5, f5);
+                }
             }
+
+            Map<String, String> videoInfo = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_basic_info", new HashMap<>());
+            featureMap.put("total_time", Double.parseDouble(videoInfo.getOrDefault("total_time", "0")));
+            featureMap.put("bit_rate", Double.parseDouble(videoInfo.getOrDefault("bit_rate", "0")));
+
+            String title = videoInfo.getOrDefault("title", "");
+            if (!title.isEmpty()) {
+                List<Future<Pair<String, Double[]>>> futures = new ArrayList<>();
+                for (String name : Arrays.asList("c3_feature", "c4_feature", "c5_feature", "c6_feature", "c7_feature")) {
+                    for (String key_time : Arrays.asList("tags_1d", "tags_3d", "tags_7d")) {
+                        String key = name + "_" + key_time;
+                        String tags = c34567Map.getOrDefault(key, "");
+                        if (!tags.isEmpty()) {
+                            Future<Pair<String, Double[]>> future = ThreadPoolFactory.defaultPool().submit(() -> {
+                                Double[] doubles = ExtractorUtils.funcC34567ForTagsNew(tags, title);
+                                return Pair.create(key, doubles);
+                            });
+                            futures.add(future);
+                        }
+                    }
+                }
+                try {
+                    for (Future<Pair<String, Double[]>> future : futures) {
+                        Pair<String, Double[]> pair = future.get(1000, TimeUnit.MILLISECONDS);
+                        featureMap.put(pair.getFirst() + "_matchnum", pair.getSecond()[0]);
+                        featureMap.put(pair.getFirst() + "_maxscore", pair.getSecond()[1]);
+                        featureMap.put(pair.getFirst() + "_avgscore", pair.getSecond()[2]);
+                    }
+                } catch (Exception e) {
+                    log.error("concurrent similarity error", e);
+                }
+            }
+
+            if (!vid.isEmpty()) {
+                for (String key_feature : Arrays.asList("c8_feature", "c9_feature")) {
+                    for (String key_action : Arrays.asList("share", "return")) {
+                        Map<String, String[]> cfMap = c89Map.getOrDefault(key_feature + "_" + key_action, new HashMap<>());
+                        if (cfMap.containsKey(vid)) {
+                            String[] scores = cfMap.get(vid);
+                            Double score1 = Double.parseDouble(scores[0]);
+                            Double score2 = Double.parseDouble(scores[1]);
+                            Double score3 = Double.parseDouble(scores[2]) <= 0 ? 0D : 1.0 / Double.parseDouble(scores[2]);
+                            featureMap.put(key_feature + "_" + key_action + "_score", score1);
+                            featureMap.put(key_feature + "_" + key_action + "_num", score2);
+                            featureMap.put(key_feature + "_" + key_action + "_rank", score3);
+                        }
+                    }
+                }
+            }
+            Map<String, String> d1 = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_recsys_feature_cf_i2i_new_v2", new HashMap<>());
+            if (!d1.isEmpty()) {
+                featureMap.put("d1_exp", Double.parseDouble(d1.getOrDefault("exp", "0")));
+                featureMap.put("d1_return_n", Double.parseDouble(d1.getOrDefault("return_n", "0")));
+                featureMap.put("d1_rovn", Double.parseDouble(d1.getOrDefault("rovn", "0")));
+            }
+            item.featureMapDouble = featureMap;
         }
 
-        // merge
-        List<List<Video>> groupList = new ArrayList<>();
-        groupList.add(group1);
-        groupList.add(group2);
-        groupList.add(group3);
-        List<Video> result = fillVideo(groupList);
-        result.addAll(group4);
-        return result;
-    }
+        // 3 连续值特征分桶
+        readBucketFile();
+        Map<String, String> userFeatureMap = new HashMap<>(userFeatureMapDouble.size());
+        for (Map.Entry<String, Double> entry : userFeatureMapDouble.entrySet()) {
+            String name = entry.getKey();
+            Double score = entry.getValue();
+            // 注意:0值、不在分桶文件中的特征,会被过滤掉。
+            if (score > 1E-8 && this.bucketsLen.containsKey(name) && this.bucketsMap.containsKey(name)) {
+                Double bucketNum = this.bucketsLen.get(name);
+                double[] buckets = this.bucketsMap.get(name);
+                Double scoreNew = 1.0 / bucketNum * (ExtractorUtils.findInsertPosition(buckets, score) + 1.0);
+                userFeatureMap.put(name, String.valueOf(scoreNew));
+            }
+        }
+        for (RankItem item : rankItems) {
+            Map<String, String> featureMap = new HashMap<>();
+            Map<String, Double> featureMapDouble = item.featureMapDouble;
 
-    private List<Video> fillVideo(List<List<Video>> groupList) {
-        List<Video> result = new ArrayList<>();
-        int maxSize = getMaxSize(groupList);
-        for (int i = 0; i < maxSize; i++) {
-            for (List<Video> group : groupList) {
-                if (null != group && i < group.size()) {
-                    result.add(group.get(i));
+            for (Map.Entry<String, Double> entry : featureMapDouble.entrySet()) {
+                String name = entry.getKey();
+                Double score = entry.getValue();
+                // 注意:0值、不在分桶文件中的特征,会被过滤掉。
+                if (score > 1E-8 && this.bucketsLen.containsKey(name) && this.bucketsMap.containsKey(name)) {
+                    Double bucketNum = this.bucketsLen.get(name);
+                    double[] buckets = this.bucketsMap.get(name);
+                    Double scoreNew = 1.0 / bucketNum * (ExtractorUtils.findInsertPosition(buckets, score) + 1.0);
+                    featureMap.put(name, String.valueOf(scoreNew));
                 }
             }
+            item.featureMap = featureMap;
+        }
+        // 4 排序模型计算
+        Map<String, String> sceneFeatureMap = new HashMap<>(0);
+        List<RankItem> items = ScorerUtils.getScorerPipeline("feeds_score_config_20240807.conf").scoring(sceneFeatureMap, userFeatureMap, rankItems);
+        // 5 排序公式特征
+        Map<String, Map<String, String>> vid2MapFeature = this.getVideoRedisFeature(vids, "redis:vid_hasreturn_vor:");
+
+        // 获取权重
+        Map<String, Double> cate2Coefficient = new HashMap<>();
+        double cate2CoefficientFunc = mergeWeight.getOrDefault("cate2CoefficientFunc", 0d);
+        if (cate2CoefficientFunc == 1d) {
+            String headVidStr = String.valueOf(param.getHeadVid());
+            String mergeCate2 = this.findVideoMergeCate2(featureOriginVideo, headVidStr);
+            Double length = mergeWeight.getOrDefault("cate2CoefficientLength", 10000d);
+            Map<String, Double> simCateScore = this.findSimCateScore(mergeCate2, param.getChannelName(), length.intValue());
+            cate2Coefficient.putAll(simCateScore);
+        }
+
+        Double cate2CoefficientDenominator = mergeWeight.getOrDefault("cate2CoefficientDenominator", 1d);
+
+        List<Video> result = new ArrayList<>();
+        for (RankItem item : items) {
+            double score;
+            double fmRovOrigin = item.getScoreRov();
+            item.getScoresMap().put("fmRovOrigin", fmRovOrigin);
+            double fmRov = restoreScore(fmRovOrigin);
+            item.getScoresMap().put("fmRov", fmRov);
+            double hasReturnRovScore = Double.parseDouble(vid2MapFeature.getOrDefault(item.getVideoId() + "", new HashMap<>()).getOrDefault("rov", "0"));
+            item.getScoresMap().put("hasReturnRovScore", hasReturnRovScore);
+            double vor = Double.parseDouble(vid2MapFeature.getOrDefault(item.getVideoId() + "", new HashMap<>()).getOrDefault("vor", "0"));
+            item.getScoresMap().put("vor", vor);
+
+            String vidMergeCate2 = this.findVideoMergeCate2(featureOriginVideo, String.valueOf(item.getVideoId()));
+            Double scoreCoefficient = cate2Coefficient.getOrDefault(vidMergeCate2, 0d);
+            item.getScoresMap().put("scoreCoefficient", scoreCoefficient);
+            item.getScoresMap().put("cate2CoefficientDenominator", cate2CoefficientDenominator);
+
+            score = fmRov * (0.1 + hasReturnRovScore) * (0.1 + vor) * (1 + scoreCoefficient / cate2CoefficientDenominator);
+
+            Video video = item.getVideo();
+            video.setScore(score);
+            video.setSortScore(score);
+            video.setScoresMap(item.getScoresMap());
+            video.setAllFeatureMap(item.getAllFeatureMap());
+            if (MapUtils.isNotEmpty(feature.getVideoFeature()) && MapUtils.isNotEmpty(feature.getVideoFeature().get(item.getVideoId() + ""))) {
+                video.getMetaFeatureMap().putAll(feature.getVideoFeature().get(item.getVideoId() + ""));
+            }
+            if (MapUtils.isNotEmpty(feature.getUserFeature())) {
+                video.getMetaFeatureMap().putAll(feature.getUserFeature());
+            }
+            result.add(video);
         }
+        result.sort(Comparator.comparingDouble(o -> -o.getSortScore()));
         return result;
     }
 
-    private int getMaxSize(List<List<Video>> groupList) {
-        int maxSize = 0;
-        if (null != groupList) {
-            for (List<Video> group : groupList) {
-                if (null != group) {
-                    maxSize = Math.max(maxSize, group.size());
-                }
-            }
+    private Map<String, Double> findSimCateScore(String headCate2, String channelName, int length) {
+        if (StringUtils.isBlank(headCate2)) {
+            return new HashMap<>();
         }
-        return maxSize;
+
+        String redisKey = String.format("alg_recsys_channel_good_cate_pair_list:%s:%s", channelName, headCate2);
+        String cate2Value = redisTemplate.opsForValue().get(redisKey);
+        if (StringUtils.isEmpty(cate2Value)) {
+            return new HashMap<>();
+        }
+
+        return this.parsePair(cate2Value, length);
     }
 
-    private boolean highlyActiveUser(String mid, int minBid) {
-        Map<String, String> userInfo = featureService.getUserInfo("alg_recsys_feature_user_active_level", mid);
-        if (null != userInfo && userInfo.containsKey("bid")) {
-            int bid = Integer.parseInt(userInfo.get("bid"));
-            return bid >= minBid;
+    private Map<String, Double> parsePair(String value, int length) {
+        if (StringUtils.isBlank(value)) {
+            return new HashMap<>();
         }
-        return false;
+
+        String[] split = value.split("\t");
+        if (split.length != 2) {
+            return new HashMap<>();
+        }
+
+        String[] valueList = split[0].trim().split(",");
+        String[] scoreList = split[1].trim().split(",");
+        if (valueList.length != scoreList.length) {
+            return new HashMap<>();
+        }
+
+        int minLength = Math.min(length, valueList.length);
+        Map<String, Double> resultMap = new HashMap<>();
+        for (int i = 0; i < minLength; i++) {
+            resultMap.put(valueList[i].trim(), Double.parseDouble(scoreList[i].trim()));
+        }
+
+        return resultMap;
+    }
+
+    private String findVideoMergeCate2(Map<String, Map<String, Map<String, String>>> featureOriginVideo, String vid) {
+        Map<String, String> videoInfo = featureOriginVideo.getOrDefault(vid, new HashMap<>()).getOrDefault("alg_vid_feature_basic_info", new HashMap<>());
+        return videoInfo.get("merge_second_level_cate");
     }
-}
+}

+ 1 - 80
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/strategy/RankStrategy4RegionMergeModelV568.java

@@ -154,10 +154,7 @@ public class RankStrategy4RegionMergeModelV568 extends RankStrategy4RegionMergeM
             double vor = Double.parseDouble(vid2MapFeature.getOrDefault(item.getVideoId() + "", new HashMap<>()).getOrDefault("vor", "0"));
             item.getScoresMap().put("vor", vor);
 
-            String vidStr = String.valueOf(item.getVideo().getVideoId());
-            String mergeCate2 = this.parseMergeCate2(vidStr, featureOriginVideo);
-            double reduceCoefficient = this.parseReduceCoefficient(mergeCate2, reduceConfigMap);
-            score = fmRov * (0.1 + hasReturnRovScore) * (0.1 + vor) * reduceCoefficient;
+            score = fmRov * (0.1 + hasReturnRovScore) * (0.1 + vor);
 
             Video video = item.getVideo();
             video.setScore(score);
@@ -358,80 +355,4 @@ public class RankStrategy4RegionMergeModelV568 extends RankStrategy4RegionMergeM
         }
     }
 
-    public double parseReduceCoefficient(String mergeCate2, Map<String, List<Map<String, String>>> reduceConfigMap) {
-        if (StringUtils.isBlank(mergeCate2) || MapUtils.isEmpty(reduceConfigMap)) {
-            return 1d;
-        }
-
-        List<Map<String, String>> configMaps = reduceConfigMap.getOrDefault(mergeCate2, new ArrayList<>());
-        for (Map<String, String> configMap : configMaps) {
-            boolean currentHourIsNeedReduce = currentHourIsNeedReduce(configMap);
-            boolean currentDateIsNeedReduce = currentDateIsNeedReduce(configMap);
-            if (currentHourIsNeedReduce || currentDateIsNeedReduce) {
-                return Double.parseDouble(configMap.getOrDefault("reduce_coefficient", "1"));
-            }
-        }
-
-        return 1d;
-    }
-
-
-    /**
-     * 当前小时是否需要降权
-     */
-    private boolean currentHourIsNeedReduce(Map<String, String> configMap) {
-        if (!configMap.containsKey("reduce_hour")) {
-            return false;
-        }
-        try {
-
-            int currentHour = LocalDateTime.now().getHour();
-            String[] reduceHours = configMap.get("reduce_hour").split(",");
-            for (String hourRange : reduceHours) {
-                String[] split = hourRange.split("-");
-                int h1 = Integer.parseInt(split[0]);
-                int h2 = (split.length == 2) ? Integer.parseInt(split[1]) : h1;
-
-                if (currentHour >= h1 && currentHour <= h2) {
-                    return true;
-                }
-            }
-        } catch (Exception e) {
-            log.error("564 error parse reduce hour config error. config content: {}, \n", configMap, e);
-        }
-        return false;
-    }
-
-    /**
-     * 当前时间段是否需要降权
-     */
-    private boolean currentDateIsNeedReduce(Map<String, String> configMap) {
-        if (!configMap.containsKey("reduce_date")) {
-            return false;
-        }
-
-        String[] reduceDates = configMap.get("reduce_date").split(",");
-        for (String s : reduceDates) {
-            if (DateUtils.ifTimeRangeInNow(s)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-
-    private String parseMergeCate2(String vidStr, Map<String, Map<String, Map<String, String>>> featureMap) {
-        if (!featureMap.containsKey(vidStr)) {
-            return "";
-        }
-
-        Map<String, Map<String, String>> vidFeature = featureMap.get(vidStr);
-        if (!vidFeature.containsKey("alg_vid_feature_basic_info")) {
-            return "";
-        }
-
-        Map<String, String> basicInfoMap = vidFeature.get("alg_vid_feature_basic_info");
-        return basicInfoMap.getOrDefault("merge_second_level_cate", "");
-    }
 }

+ 1 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallParam.java

@@ -34,4 +34,5 @@ public class RecallParam {
     private String rootSourceId;
     private Integer userShareDepth;
 
+    private String channelName;
 }

+ 4 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallService.java

@@ -126,6 +126,10 @@ public class RecallService implements ApplicationContextAware {
             strategies.add(strategyMap.get(HeadCate2RovRecallStrategy.class.getSimpleName()));
         }
 
+        if (CollectionUtils.isNotEmpty(abExpCodes) && abExpCodes.contains("566")) {
+            strategies.add(strategyMap.get(HeadCate2AndChannelRovRecallStrategy.class.getSimpleName()));
+        }
+
         // 命中用户黑名单不走流量池
         if (!param.isRiskUser()) {
             strategies.add(strategyMap.get(QuickFlowPoolWithLevelRecallStrategy.class.getSimpleName()));

+ 186 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/strategy/HeadCate2AndChannelRovRecallStrategy.java

@@ -0,0 +1,186 @@
+package com.tzld.piaoquan.recommend.server.service.recall.strategy;
+
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
+import com.tzld.piaoquan.recommend.server.model.Video;
+import com.tzld.piaoquan.recommend.server.service.FeatureService;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterParam;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterResult;
+import com.tzld.piaoquan.recommend.server.service.filter.FilterService;
+import com.tzld.piaoquan.recommend.server.service.recall.FilterParamFactory;
+import com.tzld.piaoquan.recommend.server.service.recall.RecallParam;
+import com.tzld.piaoquan.recommend.server.service.recall.RecallStrategy;
+import com.tzld.piaoquan.recommend.server.util.FeatureUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.stream.Collectors;
+
+/**
+ * 头部视频,类似品类召回
+ */
+@Slf4j
+@Component
+public class HeadCate2AndChannelRovRecallStrategy implements RecallStrategy {
+
+    @Autowired
+    private FilterService filterService;
+    @Autowired
+    @Qualifier("redisTemplate")
+    public RedisTemplate<String, String> redisTemplate;
+
+    @Autowired
+    private FeatureService featureService;
+
+    @ApolloJsonValue("${head.cate2.channel.rov.recall.param:{}}")
+    private Map<String, String> paramJson = new HashMap<>();
+
+    public static final String PUSH_FROM = "recall_strategy_head_cate2_channel_rov";
+
+    private static final String SIM_MERGE_CATE2_KEY_FORMAT = "alg_recsys_channel_good_cate_pair_list:%s:%s";
+    private static final String MERGE_CATE2_VIDEO_LIST_KEY_FORMAT = "alg_recsys_recall_channel_good_cate_pair_rovn:%s:%s";
+
+    @Override
+    public List<Video> recall(RecallParam param) {
+        if (Objects.isNull(param.getVideoId()) || StringUtils.isBlank(param.getChannelName())) {
+            return Collections.emptyList();
+        }
+
+        String channelName = param.getChannelName();
+
+        // 获取头部视频基础信息
+        String vidStr = String.valueOf(param.getVideoId());
+        Map<String, String> headVideoInfo = featureService.getHeadVideoInfo(vidStr);
+        if (MapUtils.isEmpty(headVideoInfo)) {
+            return Collections.emptyList();
+        }
+
+        // 不存在品类或无效品类
+        String mergeCate2 = headVideoInfo.get("merge_second_level_cate");
+        if (StringUtils.isBlank(mergeCate2) || "unknown".equals(mergeCate2)) {
+            return Collections.emptyList();
+        }
+
+        // 获取相似品类
+        String simCate2Key = String.format(SIM_MERGE_CATE2_KEY_FORMAT, channelName, mergeCate2);
+        String simCate2List = redisTemplate.opsForValue().get(simCate2Key);
+        if (Objects.isNull(simCate2List)) {
+            return Collections.emptyList();
+        }
+
+        List<Video> videoResult = new ArrayList<>();
+
+        int simCateLength = Integer.parseInt(paramJson.getOrDefault("sim_cate_length", "10000"));
+        Map<String, Double> mergeCate2Pair = this.parsePair(simCate2List, simCateLength);
+
+        Map<String, Map<String, Double>> recallVideoMap = this.cate2Recall(new ArrayList<>(mergeCate2Pair.keySet()), channelName);
+
+        // 过滤
+        List<Long> allVid = recallVideoMap.values().stream()
+                .map(Map::keySet)
+                .flatMap(Collection::stream)
+                .map(Long::parseLong)
+                .collect(Collectors.toList());
+
+        FilterParam filterParam = FilterParamFactory.create(param, allVid);
+        FilterResult filterResult = filterService.filter(filterParam);
+        Set<Long> filterVids = new HashSet<>(filterResult.getVideoIds());
+        filterVids.remove(param.getVideoId());
+
+        for (Map.Entry<String, Double> entry : mergeCate2Pair.entrySet()) {
+            String cate = entry.getKey();
+            Double cateScore = entry.getValue();
+
+            Map<String, Double> videoMap = recallVideoMap.getOrDefault(cate, new HashMap<>());
+            for (Map.Entry<String, Double> videoEntry : videoMap.entrySet()) {
+                long vid = Long.parseLong(videoEntry.getKey());
+
+                // 过滤之后不存在的视频,过滤掉
+                if (!filterVids.contains(vid)) {
+                    continue;
+                }
+
+                Double videoScore = videoEntry.getValue();
+
+                Video video = new Video();
+                video.setVideoId(vid);
+                video.setRovScore(cateScore * videoScore);
+                video.setPushFrom(PUSH_FROM);
+                videoResult.add(video);
+            }
+
+        }
+
+        videoResult.sort(Comparator.comparingDouble(o -> -o.getRovScore()));
+
+        return videoResult;
+    }
+
+    private Map<String, Double> parsePair(String value, int length) {
+        if (StringUtils.isBlank(value)) {
+            return new HashMap<>();
+        }
+
+        String[] split = value.split("\t");
+        if (split.length != 2) {
+            return new HashMap<>();
+        }
+
+        String[] valueList = split[0].trim().split(",");
+        String[] scoreList = split[1].trim().split(",");
+        if (valueList.length != scoreList.length) {
+            return new HashMap<>();
+        }
+
+        int minLength = Math.min(length, valueList.length);
+        Map<String, Double> resultMap = new HashMap<>();
+        for (int i = 0; i < minLength; i++) {
+            resultMap.put(valueList[i].trim(), Double.parseDouble(scoreList[i].trim()));
+        }
+
+        return resultMap;
+    }
+
+    private Map<String, Map<String, Double>> cate2Recall(List<String> mergeCate2List, String channelName) {
+
+
+        List<String> redisKeys = mergeCate2List.stream().map(i -> String.format(MERGE_CATE2_VIDEO_LIST_KEY_FORMAT, channelName, i)).collect(Collectors.toList());
+        List<String> values = redisTemplate.opsForValue().multiGet(redisKeys);
+        if (CollectionUtils.isEmpty(values)) {
+            return new HashMap<>();
+        }
+
+        Map<String, Map<String, Double>> resultMap = new HashMap<>();
+
+        int recallVidLength = Integer.parseInt(paramJson.getOrDefault("recall_vid_length", "10000"));
+        for (int i = 0; i < mergeCate2List.size(); i++) {
+            String mergeCate2 = mergeCate2List.get(i);
+
+            String value = values.get(i);
+            if (StringUtils.isBlank(value)) {
+                continue;
+            }
+
+            Map<String, Double> recallVideoMap = this.parsePair(value, recallVidLength);
+            if (MapUtils.isEmpty(recallVideoMap)) {
+                continue;
+            }
+
+            resultMap.put(mergeCate2, recallVideoMap);
+        }
+
+        return resultMap;
+    }
+
+    @Override
+    public String pushFrom() {
+        return PUSH_FROM;
+    }
+}