|
@@ -7,6 +7,7 @@ import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.dto.AdPlatformCreativeDTO;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.enums.RedisPrefixEnum;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
|
|
|
+import com.tzld.piaoquan.ad.engine.commons.param.RecommendRequestParam;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.redis.AdRedisHelper;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.redis.AlgorithmRedisHelper;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
|
|
@@ -14,6 +15,7 @@ import com.tzld.piaoquan.ad.engine.commons.util.DateUtils;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.util.NumUtil;
|
|
|
import com.tzld.piaoquan.ad.engine.commons.util.ObjUtil;
|
|
|
import com.tzld.piaoquan.ad.engine.service.entity.CorrectCpaParam;
|
|
|
+import com.tzld.piaoquan.ad.engine.service.entity.CorrectCtcvrScoreParam;
|
|
|
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;
|
|
@@ -65,6 +67,21 @@ public abstract class RankStrategyBasic implements RankStrategy {
|
|
|
@Value("${correct.cpa.view:1000}")
|
|
|
protected Integer correctCpaView;
|
|
|
|
|
|
+ @Value("${exclude.exp:772}")
|
|
|
+ protected String excludeExp;
|
|
|
+
|
|
|
+ @Value("${target.crowd.exclude.exp:785}")
|
|
|
+ protected String targetCrowdExcludeExp;
|
|
|
+
|
|
|
+ @Value("${calibration.coefficient.exp:786}")
|
|
|
+ protected String calibrationCoefficientExp;
|
|
|
+
|
|
|
+ @Value("${calibration.view:2000}")
|
|
|
+ protected Integer calibrationView;
|
|
|
+
|
|
|
+ @Value("${calibration.alpha:0.5}")
|
|
|
+ protected Double calibrationAlpha;
|
|
|
+
|
|
|
@Autowired
|
|
|
private FeatureService featureService;
|
|
|
@Autowired
|
|
@@ -82,6 +99,8 @@ public abstract class RankStrategyBasic implements RankStrategy {
|
|
|
|
|
|
String adVerLayerDayKey = "ad:platform:adver:day:{layer}:{clazz}:{adverId}";
|
|
|
|
|
|
+ String cidLayerKey = "ad:engine:cid:layer:info:{cid}:{userLayer}";
|
|
|
+
|
|
|
|
|
|
protected static final List<String> hasChannelScenes = new ArrayList<String>() {{
|
|
|
add("DaiTou");
|
|
@@ -216,6 +235,36 @@ public abstract class RankStrategyBasic implements RankStrategy {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ protected void filterRequestAdList(RankRecommendRequestParam request, ScoreParam scoreParam) {
|
|
|
+ Map<String, String> userLayer = this.getUserLayer(request.getMid());
|
|
|
+ String layer = userLayer.getOrDefault("layer", "无曝光");
|
|
|
+ String clazz = userLayer.getOrDefault("class", "近期未出现");
|
|
|
+ if (StringUtils.isNotEmpty(layer) && layer.equals("已转化")) {
|
|
|
+ layer = "有转化";
|
|
|
+ }
|
|
|
+ //有转化中医层,中医和兴趣教育同时存在过滤兴趣教育行业
|
|
|
+ if (Objects.equals(layer, "有转化") && Objects.equals(clazz, "中医") && scoreParam.getExpCodeSet().contains(excludeExp)) {
|
|
|
+ List<AdPlatformCreativeDTO> adIdList = request.getAdIdList();
|
|
|
+ Set<String> professions = adIdList.stream().map(AdPlatformCreativeDTO::getProfession).collect(Collectors.toSet());
|
|
|
+ //同时存在中医行业和兴趣教育行业
|
|
|
+ if (professions.contains("中医") && professions.contains("兴趣教育")) {
|
|
|
+ List<AdPlatformCreativeDTO> filteredAdList = adIdList.stream()
|
|
|
+ .filter(e -> !Objects.equals(e.getProfession(), "兴趣教育")).collect(Collectors.toList());
|
|
|
+ request.setAdIdList(filteredAdList);
|
|
|
+ }
|
|
|
+ log.info("excludeExp filtered request={}", JSONObject.toJSONString(request));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 有曝光无转化-其他 过滤德瑞骅客户 客户id 26
|
|
|
+ if (Objects.equals(layer, "有曝光无转化") && Objects.equals(clazz, "其他") && scoreParam.getExpCodeSet().contains(targetCrowdExcludeExp)) {
|
|
|
+ List<AdPlatformCreativeDTO> adIdList = request.getAdIdList();
|
|
|
+ List<AdPlatformCreativeDTO> filteredAdList = adIdList.stream().filter(e -> e.getCustomerId() != 26L).collect(Collectors.toList());
|
|
|
+ request.setAdIdList(filteredAdList);
|
|
|
+ log.info("targetCrowdExcludeExp filtered request={}", JSONObject.toJSONString(request));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
protected Map<String, GuaranteeView> getGuaranteeViewMap(RankRecommendRequestParam request, ScoreParam scoreParam) {
|
|
|
Map<String, GuaranteeView> map = new HashMap<>();
|
|
|
try {
|
|
@@ -461,6 +510,79 @@ public abstract class RankStrategyBasic implements RankStrategy {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ protected void calculateCtcvrScore(List<AdRankItem> items, RankRecommendRequestParam request, ScoreParam scoreParam) {
|
|
|
+ if (scoreParam.getExpCodeSet().contains(calibrationCoefficientExp)) {
|
|
|
+ calibrationCtcvrScore(items, request);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void calibrationCtcvrScore(List<AdRankItem> items, RankRecommendRequestParam request) {
|
|
|
+ Map<String, String> userLayer = this.getUserLayer(request.getMid());
|
|
|
+ String layer = userLayer.getOrDefault("layer", "无曝光");
|
|
|
+ String clazz = userLayer.getOrDefault("class", "近期未出现");
|
|
|
+ layer = "已转化".equals(layer) ? "有转化" : layer;
|
|
|
+ String userLayerClass = buildUserLayerClass(request.getIsFilterUser(), layer, clazz);
|
|
|
+ String cidLayerClassKey = cidLayerKey.replace("{userLayer}", userLayerClass);
|
|
|
+
|
|
|
+ // 3. 批量查询Redis
|
|
|
+ List<Long> cidList = items.stream().map(AdRankItem::getAdId).collect(Collectors.toList());
|
|
|
+ List<String> redisKeys = cidList.stream()
|
|
|
+ .map(cid -> cidLayerClassKey.replace("{cid}", cid.toString()))
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ List<String> redisValues = algRedisHelper.mget(redisKeys);
|
|
|
+ Map<Long, CorrectCtcvrScoreParam> calibrationMap = parseRedisValues(cidList, redisValues);
|
|
|
+
|
|
|
+ // 4. 应用校准逻辑
|
|
|
+ applyCalibration(items, calibrationMap);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析Redis返回值到Map
|
|
|
+ private Map<Long, CorrectCtcvrScoreParam> parseRedisValues(List<Long> cidList, List<String> values) {
|
|
|
+ Map<Long, CorrectCtcvrScoreParam> map = new HashMap<>();
|
|
|
+ for (int i = 0; i < cidList.size(); i++) {
|
|
|
+ String value = values.get(i);
|
|
|
+ if (StringUtils.isEmpty(value)) continue;
|
|
|
+
|
|
|
+ try {
|
|
|
+ JSONObject json = JSONObject.parseObject(value);
|
|
|
+ Integer view = json.getInteger("view");
|
|
|
+ Double ctcvr = json.getDouble("ctcvr");
|
|
|
+ if (view != null && ctcvr != null) {
|
|
|
+ CorrectCtcvrScoreParam param = new CorrectCtcvrScoreParam();
|
|
|
+ param.setView(view);
|
|
|
+ param.setRealCtcvr(ctcvr);
|
|
|
+ map.put(cidList.get(i), param);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("Failed to parse calibration data for cid={}", cidList.get(i), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return map;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 应用校准到广告项
|
|
|
+ private void applyCalibration(List<AdRankItem> items, Map<Long, CorrectCtcvrScoreParam> calibrationMap) {
|
|
|
+ for (AdRankItem item : items) {
|
|
|
+ CorrectCtcvrScoreParam param = calibrationMap.get(item.getAdId());
|
|
|
+ if (param == null || param.getView() == null || param.getView() < calibrationView) {
|
|
|
+ continue; // 跳过无效数据
|
|
|
+ }
|
|
|
+
|
|
|
+ double realCtcvr = Optional.ofNullable(param.getRealCtcvr()).orElse(0.0);
|
|
|
+ double calibratedScore = item.getLrScore() * alpha + (1 - alpha) * realCtcvr;
|
|
|
+
|
|
|
+ item.getScoreMap().put("ctcvrScore", calibratedScore);
|
|
|
+ item.getScoreMap().put("realCtcvr", realCtcvr);
|
|
|
+ item.setLrScore(calibratedScore);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildUserLayerClass(boolean isFilterUser, String layer, String clazz) {
|
|
|
+ if (Objects.equals(layer, "已转化")) layer = "有转化";
|
|
|
+ return isFilterUser ? layer + "-炸-" + clazz : layer + "-" + clazz;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
protected AdRankItem creativeCovertRankItem(AdPlatformCreativeDTO dto, RankRecommendRequestParam request, Set<String> noApiAdVerIds) {
|
|
|
AdRankItem adRankItem = new AdRankItem();
|