|  | @@ -64,8 +64,11 @@ public abstract class RankStrategyBasic implements RankStrategy {
 | 
	
		
			
				|  |  |      @Value("${correct.cpa.beta.2:0.2}")
 | 
	
		
			
				|  |  |      protected Double correctCpaBeta2;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    @Value("${correct.cpa.view:1000}")
 | 
	
		
			
				|  |  | -    protected Integer correctCpaView;
 | 
	
		
			
				|  |  | +    @Value("${correct.cpa.view.hour:300}")
 | 
	
		
			
				|  |  | +    protected Integer correctCpaViewHour;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Value("${correct.cpa.view.day:300}")
 | 
	
		
			
				|  |  | +    protected Integer correctCpaViewDay;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Value("${exclude.exp:772}")
 | 
	
		
			
				|  |  |      protected String excludeExp;
 | 
	
	
		
			
				|  | @@ -97,15 +100,15 @@ public abstract class RankStrategyBasic implements RankStrategy {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      String adPlatformGuaranteeKey = "ad:platform:guarantee:data:{date}:{adverId}";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    String adCustomerLayerHourKey = "ad:platform:customer:hour:{layer}:{clazz}:{customerId}";
 | 
	
		
			
				|  |  | +    String adLayerHourKey = "ad:platform:ad:hour:{layer}:{clazz}:{adId}";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    String adVerLayerHourKey = "ad:platform:adver:hour:{layer}:{clazz}:{adverId}";
 | 
	
		
			
				|  |  | +    String adLayerDayKey = "ad:platform:ad:day:{layer}:{clazz}:{adId}";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    String adCustomerLayerDayKey = "ad:platform:customer:day:{layer}:{clazz}:{customerId}";
 | 
	
		
			
				|  |  | +    String cidLayerKey = "ad:engine:cid:layer:info:{cid}:{userLayer}";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    String adVerLayerDayKey = "ad:platform:adver:day:{layer}:{clazz}:{adverId}";
 | 
	
		
			
				|  |  | +    private static final double DEFAULT_CORRECTION = 1.0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    String cidLayerKey = "ad:engine:cid:layer:info:{cid}:{userLayer}";
 | 
	
		
			
				|  |  | +    private static final double MIN_POSITIVE = 0.000001;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      protected static final List<String> hasChannelScenes = new ArrayList<String>() {{
 | 
	
	
		
			
				|  | @@ -382,94 +385,69 @@ public abstract class RankStrategyBasic implements RankStrategy {
 | 
	
		
			
				|  |  |          if (request.getIsFilterUser()) {
 | 
	
		
			
				|  |  |              layer = layer + "-炸";
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        String redisAdCustomerLayerHourKey = adCustomerLayerHourKey.replace("{layer}", layer).replace("{clazz}", clazz);
 | 
	
		
			
				|  |  | -        String redisAdVerLayerHourKey = adVerLayerHourKey.replace("{layer}", layer).replace("{clazz}", clazz);
 | 
	
		
			
				|  |  | -        String redisAadCustomerLayerDayKey = adCustomerLayerDayKey.replace("{layer}", layer).replace("{clazz}", clazz);
 | 
	
		
			
				|  |  | -        String redisAdVerLayerDayKey = adVerLayerDayKey.replace("{layer}", layer).replace("{clazz}", clazz);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          Map<Long, CorrectCpaParam> resultMap = new HashMap<>();
 | 
	
		
			
				|  |  |          try {
 | 
	
		
			
				|  |  |              if (CollectionUtils.isEmpty(request.getAdIdList())) {
 | 
	
		
			
				|  |  |                  return resultMap;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // 抽取公共方法获取数据
 | 
	
		
			
				|  |  | -            List<Long> customerIds = request.getAdIdList().stream()
 | 
	
		
			
				|  |  | -                    .map(AdPlatformCreativeDTO::getCustomerId)
 | 
	
		
			
				|  |  | +            String redisAdLayerHourKey = adLayerHourKey.replace("{layer}", layer).replace("{clazz}", clazz);
 | 
	
		
			
				|  |  | +            String redisAdLayerDayKey = adLayerDayKey.replace("{layer}", layer).replace("{clazz}", clazz);
 | 
	
		
			
				|  |  | +            Function<Long, String> hourKeyFunc = id -> redisAdLayerHourKey.replace("{adId}", String.valueOf(id));
 | 
	
		
			
				|  |  | +            Function<Long, String> dayKeyFunc = id -> redisAdLayerDayKey.replace("{adId}", String.valueOf(id));
 | 
	
		
			
				|  |  | +            List<Long> adIds = request.getAdIdList().stream()
 | 
	
		
			
				|  |  | +                    .map(AdPlatformCreativeDTO::getAdId)
 | 
	
		
			
				|  |  |                      .distinct()
 | 
	
		
			
				|  |  |                      .collect(Collectors.toList());
 | 
	
		
			
				|  |  | -            Map<Long, JSONObject> customerLayerMap = getRedisData(
 | 
	
		
			
				|  |  | -                    customerIds,
 | 
	
		
			
				|  |  | -                    id -> redisAdCustomerLayerHourKey.replace("{customerId}", String.valueOf(id)),
 | 
	
		
			
				|  |  | -                    id -> redisAadCustomerLayerDayKey.replace("{customerId}", String.valueOf(id))
 | 
	
		
			
				|  |  | -            );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            List<String> adVerIds = request.getAdIdList().stream()
 | 
	
		
			
				|  |  | -                    .map(AdPlatformCreativeDTO::getAdVerId)
 | 
	
		
			
				|  |  | -                    .distinct()
 | 
	
		
			
				|  |  | -                    .collect(Collectors.toList());
 | 
	
		
			
				|  |  | -            Map<String, JSONObject> adVerLayerMap = getRedisData(
 | 
	
		
			
				|  |  | -                    adVerIds,
 | 
	
		
			
				|  |  | -                    id -> redisAdVerLayerHourKey.replace("{adverId}", id),
 | 
	
		
			
				|  |  | -                    id -> redisAdVerLayerDayKey.replace("{adverId}", id)
 | 
	
		
			
				|  |  | -            );
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            for (AdPlatformCreativeDTO adPlatformCreativeDTO : request.getAdIdList()) {
 | 
	
		
			
				|  |  | -                Long creativeId = adPlatformCreativeDTO.getCreativeId();
 | 
	
		
			
				|  |  | -                Long customerId = adPlatformCreativeDTO.getCustomerId();
 | 
	
		
			
				|  |  | -                JSONObject jsonObject;
 | 
	
		
			
				|  |  | -                if (customerId != null && customerId != 0 && customerLayerMap.containsKey(customerId)) {
 | 
	
		
			
				|  |  | -                    jsonObject = customerLayerMap.get(customerId);
 | 
	
		
			
				|  |  | -                } else {
 | 
	
		
			
				|  |  | -                    jsonObject = adVerLayerMap.get(adPlatformCreativeDTO.getAdVerId());
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                CorrectCpaParam correctCpaParam = new CorrectCpaParam();
 | 
	
		
			
				|  |  | +            Map<Long, JSONObject> adLayerMap = getRedisData(adIds, hourKeyFunc, dayKeyFunc);
 | 
	
		
			
				|  |  | +            adIds.forEach(adId -> {
 | 
	
		
			
				|  |  | +                JSONObject jsonObject = adLayerMap.get(adId);
 | 
	
		
			
				|  |  | +                CorrectCpaParam param = new CorrectCpaParam();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |                  if (jsonObject == null) {
 | 
	
		
			
				|  |  | -                    correctCpaParam.setCorrectionFactor(1.0);
 | 
	
		
			
				|  |  | -                    resultMap.put(creativeId, correctCpaParam);
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                Integer views = jsonObject.getInteger("viewsHour");
 | 
	
		
			
				|  |  | -                Double realCtcvrHour = jsonObject.getDouble("realCtcvrHour");
 | 
	
		
			
				|  |  | -                Double pCtcvrHour = jsonObject.getDouble("pCtcvrHour");
 | 
	
		
			
				|  |  | -                Double realCtcvrDay = jsonObject.getDouble("realCtcvrDay");
 | 
	
		
			
				|  |  | -                Double pCtcvrDay = jsonObject.getDouble("pCtcvrDay");
 | 
	
		
			
				|  |  | -                correctCpaParam.setRealCtcvrHour(realCtcvrHour);
 | 
	
		
			
				|  |  | -                correctCpaParam.setPCtcvrHour(pCtcvrHour);
 | 
	
		
			
				|  |  | -                correctCpaParam.setRealCtcvrDay(realCtcvrDay);
 | 
	
		
			
				|  |  | -                correctCpaParam.setPCtcvrDay(pCtcvrDay);
 | 
	
		
			
				|  |  | -                correctCpaParam.setView(views);
 | 
	
		
			
				|  |  | -                double correctionFactor = 1.0;
 | 
	
		
			
				|  |  | -                //曝光数小于目标曝光数,不进行修正
 | 
	
		
			
				|  |  | -                if (views == null || views < correctCpaView) {
 | 
	
		
			
				|  |  | -                    correctCpaParam.setCorrectionFactor(correctionFactor);
 | 
	
		
			
				|  |  | -                    resultMap.put(creativeId, correctCpaParam);
 | 
	
		
			
				|  |  | -                    continue;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                if (realCtcvrHour != null && pCtcvrHour != null && realCtcvrDay != null && pCtcvrDay != null) {
 | 
	
		
			
				|  |  | -                    if (scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
 | 
	
		
			
				|  |  | -                        correctionFactor = (1 - correctCpaAlpha2 - correctCpaBeta2) +
 | 
	
		
			
				|  |  | -                                NumUtil.div(realCtcvrHour, pCtcvrHour) * correctCpaAlpha2 +
 | 
	
		
			
				|  |  | -                                NumUtil.div(realCtcvrDay, pCtcvrDay) * correctCpaBeta2;
 | 
	
		
			
				|  |  | -                    } else {
 | 
	
		
			
				|  |  | -                        correctionFactor = Math.pow(NumUtil.div(realCtcvrHour, pCtcvrHour), correctCpaAlpha1) *
 | 
	
		
			
				|  |  | -                                Math.pow(NumUtil.div(realCtcvrDay, pCtcvrDay), correctCpaBeta1);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    if (correctionFactor <= 0) {
 | 
	
		
			
				|  |  | -                        correctionFactor = 1;
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | +                    param.setCorrectionFactor(DEFAULT_CORRECTION);
 | 
	
		
			
				|  |  | +                } else {
 | 
	
		
			
				|  |  | +                    // 设置基础参数
 | 
	
		
			
				|  |  | +                    param.setViewsHour(jsonObject.getInteger("viewsHour"));
 | 
	
		
			
				|  |  | +                    param.setViewsDay(jsonObject.getInteger("viewsDay"));
 | 
	
		
			
				|  |  | +                    param.setRealCtcvrHour(jsonObject.getDouble("realCtcvrHour"));
 | 
	
		
			
				|  |  | +                    param.setPCtcvrHour(jsonObject.getDouble("pCtcvrHour"));
 | 
	
		
			
				|  |  | +                    param.setRealCtcvrDay(jsonObject.getDouble("realCtcvrDay"));
 | 
	
		
			
				|  |  | +                    param.setPCtcvrDay(jsonObject.getDouble("pCtcvrDay"));
 | 
	
		
			
				|  |  | +                    // 计算校正因子
 | 
	
		
			
				|  |  | +                    double factor = calculateCorrection(jsonObject, scoreParam);
 | 
	
		
			
				|  |  | +                    param.setCorrectionFactor(factor > 0 ? factor : DEFAULT_CORRECTION);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -                correctCpaParam.setCorrectionFactor(correctionFactor);
 | 
	
		
			
				|  |  | -                resultMap.put(creativeId, correctCpaParam);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +                resultMap.put(adId, param);
 | 
	
		
			
				|  |  | +            });
 | 
	
		
			
				|  |  |          } catch (Exception e) {
 | 
	
		
			
				|  |  |              log.error("getCorrectCpaParamMap error", e);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          return resultMap;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private double calculateCorrection(JSONObject jsonObject, ScoreParam scoreParam) {
 | 
	
		
			
				|  |  | +        // 安全获取值
 | 
	
		
			
				|  |  | +        Integer viewsHour = jsonObject.getInteger("viewsHour");
 | 
	
		
			
				|  |  | +        Integer viewsDay = jsonObject.getInteger("viewsDay");
 | 
	
		
			
				|  |  | +        double realCtcvrHour = safeDouble(jsonObject.getDouble("realCtcvrHour"));
 | 
	
		
			
				|  |  | +        double pCtcvrHour = safeDouble(jsonObject.getDouble("pCtcvrHour"));
 | 
	
		
			
				|  |  | +        double realCtcvrDay = safeDouble(jsonObject.getDouble("realCtcvrDay"));
 | 
	
		
			
				|  |  | +        double pCtcvrDay = safeDouble(jsonObject.getDouble("pCtcvrDay"));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if (scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
 | 
	
		
			
				|  |  | +            double alpha = isValidViews(viewsHour, correctCpaViewHour) ? correctCpaAlpha2 : 0;
 | 
	
		
			
				|  |  | +            double beta = isValidViews(viewsDay, correctCpaViewDay) ? correctCpaBeta2 : 0;
 | 
	
		
			
				|  |  | +            return (1 - alpha - beta) +
 | 
	
		
			
				|  |  | +                    (realCtcvrHour / pCtcvrHour) * alpha +
 | 
	
		
			
				|  |  | +                    (realCtcvrDay / pCtcvrDay) * beta;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +            double alpha = isValidViews(viewsHour, correctCpaViewHour) ? correctCpaAlpha1 : 0;
 | 
	
		
			
				|  |  | +            double beta = isValidViews(viewsDay, correctCpaViewDay) ? correctCpaBeta1 : 0;
 | 
	
		
			
				|  |  | +            return Math.pow(realCtcvrHour / pCtcvrHour, alpha) *
 | 
	
		
			
				|  |  | +                    Math.pow(realCtcvrDay / pCtcvrDay, beta);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      private <T> Map<T, JSONObject> getRedisData(
 | 
	
		
			
				|  |  |              Collection<T> ids,
 | 
	
		
			
				|  |  |              Function<T, String> hourKeyBuilder,
 | 
	
	
		
			
				|  | @@ -695,4 +673,14 @@ public abstract class RankStrategyBasic implements RankStrategy {
 | 
	
		
			
				|  |  |              return new HashMap<>();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 安全的数值转换
 | 
	
		
			
				|  |  | +    private double safeDouble(Double value) {
 | 
	
		
			
				|  |  | +        return (value == null || value == 0) ? MIN_POSITIVE : value;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // 视图数校验封装
 | 
	
		
			
				|  |  | +    private boolean isValidViews(Integer views, int threshold) {
 | 
	
		
			
				|  |  | +        return views != null && views >= threshold;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  }
 |