|  | @@ -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;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}
 |