|
@@ -12,6 +12,7 @@ import com.tzld.piaoquan.ad.engine.commons.redis.AlgorithmRedisHelper;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.util.DateUtils;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.util.ObjUtil;
|
|
|
+import com.tzld.piaoquan.ad.engine.service.entity.CalibrationCtcvrData;
|
|
|
import com.tzld.piaoquan.ad.engine.service.entity.GuaranteeView;
|
|
|
import com.tzld.piaoquan.ad.engine.service.feature.Feature;
|
|
|
import com.tzld.piaoquan.ad.engine.service.feature.FeatureService;
|
|
@@ -24,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
|
import java.util.*;
|
|
|
+import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@Slf4j
|
|
@@ -39,6 +41,9 @@ public abstract class RankStrategyBasic implements RankStrategy {
|
|
|
protected String guaranteeExp;
|
|
|
@ApolloJsonValue("${alpha:1.0}")
|
|
|
protected Double alpha;
|
|
|
+
|
|
|
+ @ApolloJsonValue("${calibration.view.count:5000}")
|
|
|
+ protected Integer calibrationViewCount;
|
|
|
@Autowired
|
|
|
private FeatureService featureService;
|
|
|
@Autowired
|
|
@@ -48,6 +53,11 @@ public abstract class RankStrategyBasic implements RankStrategy {
|
|
|
|
|
|
String adPlatformGuaranteeKey = "ad:platform:guarantee:data:{date}:{adverId}";
|
|
|
|
|
|
+ String realCtcvrCustomerKey = "ad:platform:real:ctcvr:{model}:{layer}:{class}:{profession}:{customerId}";
|
|
|
+
|
|
|
+ String realCtcvrProfessionKey = "ad:platform:real:ctcvr:{model}:{layer}:{class}:{profession}";
|
|
|
+
|
|
|
+
|
|
|
protected static final List<String> hasChannelScenes = new ArrayList<String>() {{
|
|
|
add("DaiTou");
|
|
|
add("GzhTouLiu");
|
|
@@ -366,4 +376,109 @@ public abstract class RankStrategyBasic implements RankStrategy {
|
|
|
return new HashMap<>();
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ protected void calibrationCtcvrScore(List<AdRankItem> adRankItems, String mid, boolean isFilterUser, String modelName) {
|
|
|
+ // 1. 获取用户分层信息
|
|
|
+ Map<String, String> userLayer = getUserLayer(mid);
|
|
|
+ String layer = userLayer.getOrDefault("layer", "无曝光");
|
|
|
+ String clazz = userLayer.getOrDefault("class", "近期未出现");
|
|
|
+ if (isFilterUser) {
|
|
|
+ layer += "-炸";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 构建Key模板
|
|
|
+ String customerKeyTemplate = realCtcvrCustomerKey
|
|
|
+ .replace("{model}", modelName)
|
|
|
+ .replace("{layer}", layer)
|
|
|
+ .replace("{class}", clazz);
|
|
|
+
|
|
|
+ String professionKeyTemplate = realCtcvrProfessionKey
|
|
|
+ .replace("{model}", modelName)
|
|
|
+ .replace("{layer}", layer)
|
|
|
+ .replace("{class}", clazz);
|
|
|
+
|
|
|
+ // 3. 定义Key生成器
|
|
|
+ Function<AdRankItem, String> customerKeyFunc = e ->
|
|
|
+ (e.getProfession() != null && e.getCustomerId() != null)
|
|
|
+ ? customerKeyTemplate.replace("{profession}", e.getProfession())
|
|
|
+ .replace("{customerId}", String.valueOf(e.getCustomerId()))
|
|
|
+ : null;
|
|
|
+
|
|
|
+ Function<AdRankItem, String> professionKeyFunc = e ->
|
|
|
+ (e.getProfession() != null)
|
|
|
+ ? professionKeyTemplate.replace("{profession}", e.getProfession())
|
|
|
+ : null;
|
|
|
+
|
|
|
+ // 4. 获取校准数据
|
|
|
+ Map<String, CalibrationCtcvrData> customerMap = getCtcvrMap(adRankItems, customerKeyFunc);
|
|
|
+ Map<String, CalibrationCtcvrData> professionMap = getCtcvrMap(adRankItems, professionKeyFunc);
|
|
|
+ // 5. 校准分数
|
|
|
+ for (AdRankItem item : adRankItems) {
|
|
|
+ CalibrationCtcvrData data = null;
|
|
|
+ String customerKey = customerKeyFunc.apply(item);
|
|
|
+ String professionKey = professionKeyFunc.apply(item);
|
|
|
+
|
|
|
+ if (customerKey != null) {
|
|
|
+ data = customerMap.get(customerKey);
|
|
|
+ }
|
|
|
+ if (data == null && professionKey != null) {
|
|
|
+ data = professionMap.get(professionKey);
|
|
|
+ }
|
|
|
+ item.getExt().put("calibrationCtcvrData", JSONObject.toJSONString(data));
|
|
|
+ if (data == null || data.getView() == null || data.getView() < calibrationViewCount) continue;
|
|
|
+
|
|
|
+ Map<String, Double> scoreMap = item.getScoreMap();
|
|
|
+ Double pCtcvr = data.getPCtcvr();
|
|
|
+ Double realCtcvr = data.getRealCtcvr();
|
|
|
+
|
|
|
+ if (pCtcvr != null && pCtcvr != 0.0 && realCtcvr != null && realCtcvr != 0.0) {
|
|
|
+ double diff = realCtcvr / pCtcvr;
|
|
|
+ if (Math.abs(diff - 1) >= 0.1) {
|
|
|
+ Double ctcvrScore = scoreMap.get("ctcvrScore");
|
|
|
+ if (ctcvrScore == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ scoreMap.put("calibrationCtcvrScore", ctcvrScore * diff);
|
|
|
+ item.setLrScore(ctcvrScore * diff);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通用CTR数据获取方法
|
|
|
+ private Map<String, CalibrationCtcvrData> getCtcvrMap(
|
|
|
+ List<AdRankItem> items,
|
|
|
+ Function<AdRankItem, String> keyGenerator
|
|
|
+ ) {
|
|
|
+ // 生成有效Key集合
|
|
|
+ List<String> redisKeys = items.stream()
|
|
|
+ .map(keyGenerator)
|
|
|
+ .filter(Objects::nonNull)
|
|
|
+ .distinct()
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ // 批量查询Redis
|
|
|
+ List<String> redisValues = algRedisHelper.mget(redisKeys);
|
|
|
+ Map<String, CalibrationCtcvrData> resultMap = new HashMap<>();
|
|
|
+
|
|
|
+ for (int i = 0; i < redisKeys.size(); i++) {
|
|
|
+ String val = redisValues.get(i);
|
|
|
+ if (StringUtils.isEmpty(val)) continue;
|
|
|
+
|
|
|
+ try {
|
|
|
+ JSONObject json = JSONObject.parseObject(val);
|
|
|
+ Integer view = json.getInteger("view");
|
|
|
+ Double ctcvr = json.getDouble("ctcvr");
|
|
|
+ Double pCtcvr = json.getDouble("pCtcvr");
|
|
|
+ CalibrationCtcvrData data = new CalibrationCtcvrData();
|
|
|
+ data.setView(view);
|
|
|
+ data.setRealCtcvr(ctcvr);
|
|
|
+ data.setPCtcvr(pCtcvr);
|
|
|
+ resultMap.put(redisKeys.get(i), data);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("getCtcvrMap error", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return resultMap;
|
|
|
+ }
|
|
|
}
|