|
@@ -0,0 +1,114 @@
|
|
|
+package com.tzld.piaoquan.recommend.server.service.filter.strategy;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.TypeReference;
|
|
|
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.filter.FilterParam;
|
|
|
+import com.tzld.piaoquan.recommend.server.service.filter.FilterStrategy;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.collections.CollectionUtils;
|
|
|
+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.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+@Slf4j
|
|
|
+@Component
|
|
|
+public class VovLowerStrategy implements FilterStrategy {
|
|
|
+
|
|
|
+ @ApolloJsonValue("${vov.filter.condition:{}}")
|
|
|
+ private Map<String, Double> vovFilterCondition;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ @Qualifier("redisTemplate")
|
|
|
+ private RedisTemplate<String, String> redisTemplate;
|
|
|
+
|
|
|
+ private static final String KEY_FORMAT = "redis:vid_vov_daily4filter:%d";
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Long> filter(FilterParam param) {
|
|
|
+
|
|
|
+ List<Long> result = new ArrayList<>();
|
|
|
+
|
|
|
+ List<Long> videoIds = param.getVideoIds();
|
|
|
+ if (CollectionUtils.isEmpty(videoIds)) {
|
|
|
+ log.info("VOV过滤 -- videoIds为空,跳过");
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<String> redisKeys = videoIds.stream()
|
|
|
+ .map(r -> String.format(KEY_FORMAT, r))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ List<String> vovInfos = redisTemplate.opsForValue().multiGet(redisKeys);
|
|
|
+ if (CollectionUtils.isEmpty(vovInfos) || vovInfos.size() != videoIds.size()) {
|
|
|
+ log.info("VOV过滤 -- 获取到的视频VOV信息为空或长度与videoIds长度不一致,跳过: {}, {}", videoIds.size(), vovInfos.size());
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Long> removeIds = new ArrayList<>();
|
|
|
+
|
|
|
+ for (int i = 0; i < videoIds.size(); i++) {
|
|
|
+ Long videoId = videoIds.get(i);
|
|
|
+ String vovInfo = vovInfos.get(i);
|
|
|
+ if (isFilter(vovInfo)) {
|
|
|
+ removeIds.add(videoId);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ result.add(videoId);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("VOV -- 本次召回的ID列表为: {}, 被过滤掉的ID列表为: {}, 剩下的ID列表为: {}", videoIds, removeIds, result);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean isFilter(String vovInfo) {
|
|
|
+ try {
|
|
|
+
|
|
|
+ if (StringUtils.isEmpty(vovInfo)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Double> vovInfoMap = JSON.parseObject(vovInfo, new TypeReference<Map<String, Double>>() {
|
|
|
+ });
|
|
|
+
|
|
|
+ // 获取阈值
|
|
|
+ double t0ViewPvCondition = vovFilterCondition.getOrDefault("t_0_view_pv", 1000d);
|
|
|
+ double t1ViewPvCondition = vovFilterCondition.getOrDefault("t_1_view_pv", 1000d);
|
|
|
+ double t2ViewPvCondition = vovFilterCondition.getOrDefault("t_2_view_pv", -1d);
|
|
|
+
|
|
|
+ double t0VovCondition = vovFilterCondition.getOrDefault("t_0_vov", 0.34d);
|
|
|
+ double t1VovCondition = vovFilterCondition.getOrDefault("t_1_vov", 0.54d);
|
|
|
+ double t2VovCondition = vovFilterCondition.getOrDefault("t_2_vov", 100000d);
|
|
|
+
|
|
|
+ // 获取视频VOV表现
|
|
|
+ double t0TodayDistViewPv = vovInfoMap.getOrDefault("t_0_today_dist_view_pv", 0d);
|
|
|
+ double t1TodayDistViewPv = vovInfoMap.getOrDefault("t_1_today_dist_view_pv", 0d);
|
|
|
+ double t2TodayDistViewPv = vovInfoMap.getOrDefault("t_2_today_dist_view_pv", 0d);
|
|
|
+
|
|
|
+ double t0AllVov = vovInfoMap.getOrDefault("t_0_all_vov", 0d);
|
|
|
+ double t1AllVov = vovInfoMap.getOrDefault("t_1_all_vov", 0d);
|
|
|
+ double t2AllVov = vovInfoMap.getOrDefault("t_2_all_vov", 0d);
|
|
|
+
|
|
|
+ // 判断曝光是否置信
|
|
|
+ boolean viewResult = (t0TodayDistViewPv > t0ViewPvCondition) && (t1TodayDistViewPv > t1ViewPvCondition) && (t2TodayDistViewPv > t2ViewPvCondition);
|
|
|
+
|
|
|
+ // 判断VOV是否小于阈值
|
|
|
+ boolean vovResult = (t0AllVov < t0VovCondition) && (t1AllVov < t1VovCondition) && (t2AllVov < t2VovCondition);
|
|
|
+
|
|
|
+ // 曝光置信 & VOV小于阈值 & VOV全不为0
|
|
|
+ return viewResult && vovResult;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.info("VOV过滤 -- 异常: ", e);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|