|  | @@ -1,16 +1,21 @@
 | 
	
		
			
				|  |  |  package com.tzld.piaoquan.ad.engine.service.predict.model.threshold;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +import com.alibaba.fastjson.JSONObject;
 | 
	
		
			
				|  |  |  import com.tzld.piaoquan.ad.engine.commons.redis.AlgorithmRedisHelper;
 | 
	
		
			
				|  |  |  import com.tzld.piaoquan.ad.engine.service.predict.config.AdOutV1OnlineWeightConfig;
 | 
	
		
			
				|  |  |  import com.tzld.piaoquan.ad.engine.service.predict.constant.RuleRedisKeyConst;
 | 
	
		
			
				|  |  |  import com.tzld.piaoquan.ad.engine.service.predict.containner.ThresholdModelContainer;
 | 
	
		
			
				|  |  |  import com.tzld.piaoquan.ad.engine.service.predict.param.RuleParamHelper;
 | 
	
		
			
				|  |  |  import com.tzld.piaoquan.ad.engine.service.predict.param.ThresholdPredictModelParam;
 | 
	
		
			
				|  |  | +import org.apache.commons.lang3.StringUtils;
 | 
	
		
			
				|  |  |  import org.springframework.beans.factory.annotation.Autowired;
 | 
	
		
			
				|  |  | +import org.springframework.stereotype.Component;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +import javax.annotation.PostConstruct;
 | 
	
		
			
				|  |  |  import java.text.SimpleDateFormat;
 | 
	
		
			
				|  |  |  import java.util.*;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +@Component
 | 
	
		
			
				|  |  |  public class ScoreThresholdPredictModel extends ThresholdPredictModel{
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Autowired
 | 
	
	
		
			
				|  | @@ -34,36 +39,92 @@ public class ScoreThresholdPredictModel extends ThresholdPredictModel{
 | 
	
		
			
				|  |  |          String userScore = redisHelper.get(userKeyName);
 | 
	
		
			
				|  |  |          String itemScore = redisHelper.get(itemKeyName);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        if (userScore == null || itemScore == null) {
 | 
	
		
			
				|  |  | +        //加载配置数据
 | 
	
		
			
				|  |  | +        String configKeyNamePrefix = RuleRedisKeyConst.KEY_NAME_PREFIX_AD_OUT_MODEL_CONFIG
 | 
	
		
			
				|  |  | +                + modelKey + ":" + modelParam.getAbtestId() + ":"
 | 
	
		
			
				|  |  | +//                + modelParam.getAbTestConfigTag();
 | 
	
		
			
				|  |  | +                + modelParam.getAbtestParam().get("abtest_config_tag");
 | 
	
		
			
				|  |  | +        String configKey = configKeyNamePrefix + ":config";
 | 
	
		
			
				|  |  | +        String configStr = redisHelper.get(configKey);
 | 
	
		
			
				|  |  | +        Map<String, Object> configMap = new HashMap<>();
 | 
	
		
			
				|  |  | +        String hitStrategy="model";
 | 
	
		
			
				|  |  | +        if (configStr != null) {
 | 
	
		
			
				|  |  | +            try {
 | 
	
		
			
				|  |  | +                configMap= JSONObject.parseObject(configStr);
 | 
	
		
			
				|  |  | +            } catch (Exception e) {
 | 
	
		
			
				|  |  | +                // 处理 JSON 解析异常
 | 
	
		
			
				|  |  | +                e.printStackTrace();
 | 
	
		
			
				|  |  | +                result = ThresholdModelContainer.
 | 
	
		
			
				|  |  | +                        getBasicPredictModel()
 | 
	
		
			
				|  |  | +                        .predict(modelParam);
 | 
	
		
			
				|  |  | +                hitStrategy="error";
 | 
	
		
			
				|  |  | +                result.put("hit_strategy",hitStrategy);
 | 
	
		
			
				|  |  | +                return result;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        boolean useBackup = Boolean.valueOf((String)configMap.getOrDefault("use_backup_key","false"));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //有数据为空且开启兜底策略
 | 
	
		
			
				|  |  | +        if (
 | 
	
		
			
				|  |  | +                (StringUtils.isBlank(userScore) || StringUtils.isBlank(itemScore))
 | 
	
		
			
				|  |  | +                &&useBackup)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  |              // If offline scores are empty, fall back to baseline logic
 | 
	
		
			
				|  |  |              result = ThresholdModelContainer.
 | 
	
		
			
				|  |  |                      getBasicPredictModel()
 | 
	
		
			
				|  |  |                      .predict(modelParam);
 | 
	
		
			
				|  |  | +            hitStrategy="backup";
 | 
	
		
			
				|  |  | +            result.put("hit_strategy",hitStrategy);
 | 
	
		
			
				|  |  |              return result;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        String configKeyPrefix = RuleRedisKeyConst.KEY_NAME_PREFIX_AD_OUT_MODEL_CONFIG
 | 
	
		
			
				|  |  | -                + modelKey + ":" + modelParam.getAbtestParam().get("abtest_id") + ":"
 | 
	
		
			
				|  |  | -                + modelParam.getAbtestParam().get("abtest_config_tag");
 | 
	
		
			
				|  |  | -        String thresholdKey = configKeyPrefix + ":threshold";
 | 
	
		
			
				|  |  | -        double offlineScore = Double.parseDouble(userScore) + Double.parseDouble(itemScore);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        double offlineScore ;
 | 
	
		
			
				|  |  | +        //处理空值逻辑
 | 
	
		
			
				|  |  | +        double threshold=(double)configMap.getOrDefault("threshold",0);
 | 
	
		
			
				|  |  | +        if(StringUtils.isBlank(userScore)&&StringUtils.isBlank(itemScore)){
 | 
	
		
			
				|  |  | +            itemScore="0";
 | 
	
		
			
				|  |  | +            userScore="0";
 | 
	
		
			
				|  |  | +            threshold=(double)configMap.getOrDefault("miss_threshold", 0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        }else if(StringUtils.isBlank(userScore)||StringUtils.isBlank(itemScore)){
 | 
	
		
			
				|  |  | +            boolean isUserScoreBlank=StringUtils.isBlank(userScore);
 | 
	
		
			
				|  |  | +            userScore=isUserScoreBlank?"0":userScore;
 | 
	
		
			
				|  |  | +            itemScore=isUserScoreBlank?itemScore:"0";
 | 
	
		
			
				|  |  | +            threshold=isUserScoreBlank?
 | 
	
		
			
				|  |  | +                    (double)configMap.getOrDefault("item_threshold", 0):
 | 
	
		
			
				|  |  | +                    (double)configMap.getOrDefault("user_threshold", 0);
 | 
	
		
			
				|  |  | +            hitStrategy=isUserScoreBlank?"item":"user";
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //获取计算参数
 | 
	
		
			
				|  |  |          SimpleDateFormat hourFormat = new SimpleDateFormat("HH");
 | 
	
		
			
				|  |  |          SimpleDateFormat weekDayFormat = new SimpleDateFormat("u");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          Map<String, String> onlineFeatures = new HashMap<>();
 | 
	
		
			
				|  |  |          onlineFeatures.put("ctx_apptype", modelParam.getAppType().toString());
 | 
	
		
			
				|  |  |          onlineFeatures.put("ctx_week", weekDayFormat.format(modelParam.getDate()));
 | 
	
		
			
				|  |  |          onlineFeatures.put("ctx_hour", hourFormat.format(modelParam.getDate()));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        //新模型更新权重计算
 | 
	
		
			
				|  |  | +        double rankScore = 0.0;
 | 
	
		
			
				|  |  | +        if ((Boolean) configMap.getOrDefault("use_rank_score", false)) {
 | 
	
		
			
				|  |  | +            String rankScoreKey = "rank:score1:" + modelParam.getVideoId();
 | 
	
		
			
				|  |  | +            String rankScoreStr = redisHelper.get(rankScoreKey);
 | 
	
		
			
				|  |  | +            if (rankScoreStr != null) {
 | 
	
		
			
				|  |  | +                rankScore = Double.parseDouble(rankScoreStr);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        offlineScore=Double.parseDouble(userScore) + Double.parseDouble(itemScore);
 | 
	
		
			
				|  |  |          double onlineScore=getOlineScore(onlineFeatures);
 | 
	
		
			
				|  |  | +        double rankScoreBias = (double)configMap.getOrDefault("rank_score_bias", 0.0);
 | 
	
		
			
				|  |  |          double finalScore =getFinalScore(onlineScore,offlineScore);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        String thresholdValue = redisHelper.get(thresholdKey);
 | 
	
		
			
				|  |  | -        double threshold = (thresholdValue != null) ? Double.parseDouble(thresholdValue) : 0.0;
 | 
	
		
			
				|  |  | +        double rankScoreW = (double)configMap.getOrDefault("rank_score_w", 1.0);
 | 
	
		
			
				|  |  | +        double finalScoreW = (double)configMap.getOrDefault("final_score_w", 1.0);
 | 
	
		
			
				|  |  | +        double mergeScore = finalScoreW * finalScore + rankScoreW * (rankScore + rankScoreBias);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          int adPredict;
 | 
	
		
			
				|  |  | -        if (finalScore < threshold) {
 | 
	
		
			
				|  |  | +        if (mergeScore < threshold) {
 | 
	
		
			
				|  |  |              // If final score is below threshold, show the ad
 | 
	
		
			
				|  |  |              adPredict = 2;
 | 
	
		
			
				|  |  |          } else {
 | 
	
	
		
			
				|  | @@ -74,10 +135,14 @@ public class ScoreThresholdPredictModel extends ThresholdPredictModel{
 | 
	
		
			
				|  |  |          result.put("user_score", userScore);
 | 
	
		
			
				|  |  |          result.put("item_score", itemScore);
 | 
	
		
			
				|  |  |          result.put("final_score", finalScore);
 | 
	
		
			
				|  |  | +        result.put("merge_score", mergeScore);
 | 
	
		
			
				|  |  | +        result.put("rank_score", rankScore);
 | 
	
		
			
				|  |  | +        result.put("rank_score_bias", rankScoreBias);
 | 
	
		
			
				|  |  |          result.put("online_score", onlineScore);
 | 
	
		
			
				|  |  |          result.put("threshold", threshold);
 | 
	
		
			
				|  |  |          result.put("ad_predict", adPredict);
 | 
	
		
			
				|  |  |          result.put("online_features", onlineFeatures);
 | 
	
		
			
				|  |  | +        result.put("hit_strategy",hitStrategy);
 | 
	
		
			
				|  |  |          return result;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 |