Browse Source

Merge branch 'refs/heads/master' into dev-xym-calibration-dnn1

# Conflicts:
#	ad-engine-commons/pom.xml
#	ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBasic.java
#	ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy679.java
#	ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy680.java
#	ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy683.java
#	ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy688.java
xueyiming 2 days ago
parent
commit
82eb8ce576

+ 10 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/param/LayerParam.java

@@ -0,0 +1,10 @@
+package com.tzld.piaoquan.ad.engine.commons.param;
+
+import lombok.Data;
+import lombok.ToString;
+
+@Data
+@ToString
+public class LayerParam {
+    private String mid;
+}

+ 63 - 5
ad-engine-server/src/main/java/com/tzld/piaoquan/ad/engine/server/controller/AdRecommendController.java

@@ -1,12 +1,18 @@
 package com.tzld.piaoquan.ad.engine.server.controller;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.TypeReference;
 import com.tzld.piaoquan.ad.engine.commons.dto.AdPlatformCreativeDTO;
 import com.tzld.piaoquan.ad.engine.commons.enums.GuaranteedTypeEnum;
+import com.tzld.piaoquan.ad.engine.commons.param.LayerParam;
 import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
+import com.tzld.piaoquan.ad.engine.commons.redis.AlgorithmRedisHelper;
 import com.tzld.piaoquan.ad.engine.service.score.RankService;
 import com.tzld.piaoquan.ad.engine.service.score.deprecated.BidRankRecommendRequestParam;
 import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -15,9 +21,7 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 @RestController
 @RequestMapping("/recommend")
@@ -28,6 +32,9 @@ public class AdRecommendController {
     @Qualifier("rankServiceImpl")
     RankService rankService;
 
+    @Autowired
+    protected AlgorithmRedisHelper algRedisHelper;
+
     @RequestMapping("/top1/basic")
     public Map<String, Object> adRecommendTop1Basic(@RequestBody RankRecommendRequestParam request) {
         Map<String, Object> map = new HashMap<>();
@@ -45,16 +52,31 @@ public class AdRecommendController {
                 contentMap.put("adId", rankResult.getAdId());
                 contentMap.put("adScore", rankResult.getScore());
                 Double ctcvrScore = rankResult.getScoreMap().get("ctcvrScore");
-                if (ctcvrScore != null) {
-                    contentMap.put("ecpm", ctcvrScore * rankResult.getCpa() * 1000);
+                Double modelCtcvrScore = rankResult.getScoreMap().get("modelCtcvrScore");
+                if (ctcvrScore != null && ctcvrScore > 0) {
+                    contentMap.put("ecpm", modelCtcvrScore * rankResult.getCpa() * 1000);
+                    if (rankResult.getExt().get("correctionFactor") != null) {
+                        contentMap.put("revisedBid", ctcvrScore * rankResult.getCpa() * (double) rankResult.getExt().get("correctionFactor"));
+                    } else {
+                        contentMap.put("revisedBid", ctcvrScore * rankResult.getCpa());
+                    }
+                } else {
+                    contentMap.put("revisedBid", rankResult.getCpm() / 1000);
                 }
+                List<String> participateCompetitionType = new ArrayList<>();
+                participateCompetitionType.add("engine");
                 if (rankResult.getExt().get("isGuaranteed") != null && rankResult.getExt().get("isGuaranteed").equals(true)) {
                     contentMap.put("type", GuaranteedTypeEnum.IS_GUARANTEED.getType());
                 } else if (rankResult.getExt().containsKey("isHotRank") && Boolean.TRUE.equals(rankResult.getExt().get("isHotRank"))) {
                     contentMap.put("type", 2);
+                    participateCompetitionType.add("weight");
                 } else {
                     contentMap.put("type", GuaranteedTypeEnum.NOT_GUARANTEED.getType());
                 }
+                if (rankResult.getExt().containsKey("isGuaranteeType") && (boolean) rankResult.getExt().get("isGuaranteeType")) {
+                    participateCompetitionType.add("guarantee");
+                }
+                contentMap.put("participateCompetitionType", StringUtils.join(participateCompetitionType, ","));
                 map.put("content", contentMap);
                 return map;
             }
@@ -100,4 +122,40 @@ public class AdRecommendController {
         return map;
     }
 
+    @RequestMapping("/layer")
+    public Map<String, Object> getMidWithLayer(@RequestBody LayerParam request) {
+        Map<String, Object> map = new HashMap<>();
+        if (request == null || StringUtils.isEmpty(request.getMid())) {
+            map.put("code", "1");
+            map.put("msg", "mid is null");
+            return map;
+        }
+        map.put("code", "0");
+        map.put("msg", "success");
+        Map<String, Object> result = new HashMap<>();
+        map.put("content", result);
+        String key = String.format("ad:engine:mid:layer:%s", request.getMid());
+        String value = algRedisHelper.get(key);
+        if (StringUtils.isEmpty(value)) {
+            result.put("layer", "无曝光");
+            return map;
+        }
+        try {
+            Map<String, String> layerMap = JSON.parseObject(value, new TypeReference<Map<String, String>>() {
+            });
+            String layer = layerMap.getOrDefault("layer", "无曝光");
+            if (Objects.equals(layer, "已转化")) {
+                layer = "有转化";
+            }
+            result.put("layer", layer);
+            return map;
+        } catch (Exception e) {
+            log.error("getMidWithLayer error. mid: {}, \n", request.getMid(), e);
+            map.put("code", "1");
+            map.put("msg", "get mid layer error");
+            return map;
+        }
+    }
+
+
 }

+ 13 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/entity/CorrectCpaParam.java

@@ -0,0 +1,13 @@
+package com.tzld.piaoquan.ad.engine.service.entity;
+
+import lombok.Data;
+
+@Data
+public class CorrectCpaParam {
+    private Integer view;
+    private Double correctionFactor;
+    private Double realCtcvrHour;
+    private Double pCtcvrHour;
+    private Double realCtcvrDay;
+    private Double pCtcvrDay;
+}

+ 11 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/entity/CorrectCtcvrScoreParam.java

@@ -0,0 +1,11 @@
+package com.tzld.piaoquan.ad.engine.service.entity;
+
+import lombok.Data;
+
+@Data
+public class CorrectCtcvrScoreParam {
+
+    private Integer view;
+
+    private Double realCtcvr;
+}

+ 334 - 6
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBasic.java

@@ -7,11 +7,15 @@ 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;
 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.CalibrationCtcvrData;
 import com.tzld.piaoquan.ad.engine.service.entity.GuaranteeView;
 import com.tzld.piaoquan.ad.engine.service.feature.Feature;
@@ -39,6 +43,7 @@ public abstract class RankStrategyBasic implements RankStrategy {
 
     @Value("${guarantee.exp:742}")
     protected String guaranteeExp;
+
     @ApolloJsonValue("${alpha:1.0}")
     protected Double alpha;
 
@@ -47,6 +52,49 @@ public abstract class RankStrategyBasic implements RankStrategy {
 
     @ApolloJsonValue("${calibration.view.count:5000}")
     protected Integer calibrationViewCount;
+
+    @Value("${correct.cpa.exp.1:}")
+    protected String correctCpaExp1;
+
+    @Value("${correct.cpa.exp.2:}")
+    protected String correctCpaExp2;
+
+    @Value("${correct.cpa.alpha.1:1.0}")
+    protected Double correctCpaAlpha1;
+
+    @Value("${correct.cpa.beta.1:1.0}")
+    protected Double correctCpaBeta1;
+
+    @Value("${correct.cpa.alpha.2:0.7}")
+    protected Double correctCpaAlpha2;
+
+    @Value("${correct.cpa.beta.2:0.2}")
+    protected Double correctCpaBeta2;
+
+    @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;
+
+    @Value("${guarantee.weight:30}")
+    protected Integer guaranteeWeight;
+
+    @Value("${guarantee.switching.time:1754236800000}")
+    protected Long guaranteeSwitchingTime;
+
     @Autowired
     private FeatureService featureService;
     @Autowired
@@ -61,6 +109,17 @@ public abstract class RankStrategyBasic implements RankStrategy {
     String realCtcvrProfessionKey = "ad:platform:real:ctcvr:{model}:{layer}:{class}:{profession}";
 
 
+    String adCustomerLayerHourKey = "ad:platform:customer:hour:{layer}:{clazz}:{customerId}";
+
+    String adVerLayerHourKey = "ad:platform:adver:hour:{layer}:{clazz}:{adverId}";
+
+    String adCustomerLayerDayKey = "ad:platform:customer:day:{layer}:{clazz}:{customerId}";
+
+    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");
         add("GzhTouLiu");
@@ -194,10 +253,49 @@ public abstract class RankStrategyBasic implements RankStrategy {
 
     }
 
-    protected Map<String, GuaranteeView> getGuaranteeViewMap(RankRecommendRequestParam request, ScoreParam scoreParam) {
+    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() == null || e.getCustomerId() != 26L).collect(Collectors.toList());
+            request.setAdIdList(filteredAdList);
+            log.info("targetCrowdExcludeExp filtered request={}", JSONObject.toJSONString(request));
+        }
+    }
+
+
+    protected boolean getIsGuaranteedFlow(ScoreParam scoreParam) {
+        if (System.currentTimeMillis() < guaranteeSwitchingTime) {
+            return scoreParam.getExpCodeSet().contains(guaranteeExp);
+        }
+        Random random = new Random();
+        int i = random.nextInt(100);
+        return i < guaranteeWeight;
+    }
+
+    protected Map<String, GuaranteeView> getGuaranteeViewMap(RankRecommendRequestParam request, boolean isGuaranteedFlow) {
         Map<String, GuaranteeView> map = new HashMap<>();
         try {
-            if (scoreParam.getExpCodeSet().contains(guaranteeExp)) {
+            if (isGuaranteedFlow) {
                 String thatDayDateString = DateUtils.getThatDayDateString();
                 String redisKey = adPlatformGuaranteeKey.replace("{date}", thatDayDateString);
                 List<String> adVerIds = request.getAdIdList().stream().map(AdPlatformCreativeDTO::getAdVerId).distinct()
@@ -221,8 +319,8 @@ public abstract class RankStrategyBasic implements RankStrategy {
         return map;
     }
 
-    protected void setGuaranteeWeight(Map<String, GuaranteeView> map, String adVerId, Map<String, Object> ext) {
-        if (MapUtils.isNotEmpty(map)) {
+    protected void setGuaranteeWeight(Map<String, GuaranteeView> map, String adVerId, Map<String, Object> ext, boolean isGuaranteedFlow) {
+        if (isGuaranteedFlow && MapUtils.isNotEmpty(map)) {
             GuaranteeView guaranteeView = map.get(adVerId);
             if (guaranteeView != null) {
                 double guaranteeWeight = calculateGuaranteedWeight(guaranteeView);
@@ -230,6 +328,7 @@ public abstract class RankStrategyBasic implements RankStrategy {
                 ext.put("guaranteeView", guaranteeView.toString());
                 ext.put("guaranteeWeight", guaranteeWeight);
                 ext.put("isGuaranteed", isGuaranteed);
+                ext.put("isGuaranteedFlow", isGuaranteedFlow);
             }
         }
     }
@@ -272,8 +371,8 @@ public abstract class RankStrategyBasic implements RankStrategy {
                 && guaranteeView.getGuaranteeRate() != null && guaranteeView.getGuaranteeRate() != 0.0;
     }
 
-    protected double getGuaranteeScoreCoefficient(ScoreParam scoreParam, Map<String, Object> ext) {
-        if (scoreParam.getExpCodeSet().contains(guaranteeExp)) {
+    protected double getGuaranteeScoreCoefficient(boolean isGuaranteedFlow, Map<String, Object> ext) {
+        if (isGuaranteedFlow) {
             if (ext.get("guaranteeWeight") == null) {
                 return 1.0;
             } else {
@@ -284,6 +383,235 @@ public abstract class RankStrategyBasic implements RankStrategy {
         }
     }
 
+    protected Map<Long, CorrectCpaParam> getCorrectCpaParamMap(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 (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)
+                    .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();
+                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;
+                    }
+                }
+                correctCpaParam.setCorrectionFactor(correctionFactor);
+                resultMap.put(creativeId, correctCpaParam);
+            }
+        } catch (Exception e) {
+            log.error("getCorrectCpaParamMap error", e);
+        }
+        return resultMap;
+    }
+
+    private <T> Map<T, JSONObject> getRedisData(
+            Collection<T> ids,
+            Function<T, String> hourKeyBuilder,
+            Function<T, String> dayKeyBuilder) {
+
+        Map<T, JSONObject> resultMap = new HashMap<>();
+        if (CollectionUtils.isEmpty(ids)) {
+            return resultMap;
+        }
+
+        // 构建Keys
+        List<String> hourKeys = ids.stream()
+                .map(hourKeyBuilder)
+                .collect(Collectors.toList());
+        List<String> dayKeys = ids.stream()
+                .map(dayKeyBuilder)
+                .collect(Collectors.toList());
+
+        // 批量获取Redis值
+        List<String> hourValues = adRedisHelper.mget(hourKeys);
+        List<String> dayValues = adRedisHelper.mget(dayKeys);
+
+        // 解析数据
+        Iterator<T> idIter = ids.iterator();
+        for (int i = 0; i < ids.size(); i++) {
+            T id = idIter.next();
+            JSONObject jsonObj = new JSONObject();
+
+            // 解析小时数据
+            parseRedisValue(hourValues.get(i), jsonObj, "Hour", "ctcvr", "pCtcvr", "views");
+            // 解析天数据
+            parseRedisValue(dayValues.get(i), jsonObj, "Day", "ctcvr", "pCtcvr", "views");
+
+            resultMap.put(id, jsonObj);
+        }
+        return resultMap;
+    }
+
+    private void parseRedisValue(String value, JSONObject target,
+                                 String suffix, String... keys) {
+        if (StringUtils.isEmpty(value)) return;
+
+        try {
+            JSONObject source = JSONObject.parseObject(value);
+            for (String key : keys) {
+                Object val = source.get(key);
+                if (val != null) {
+                    String newKey = (key.equals("ctcvr") ? "realCtcvr" : key) + suffix;
+                    target.put(newKey, val);
+                }
+            }
+        } catch (Exception e) {
+            log.warn("Parse redis value failed: {}", value, e);
+        }
+    }
+
+    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() * calibrationAlpha + (1 - calibrationAlpha) * realCtcvr;
+            item.getExt().put("correctCtcvrScoreParam", JSONObject.toJSONString(param));
+            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();
         adRankItem.setAdId(dto.getCreativeId());

+ 34 - 9
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy679.java

@@ -1,5 +1,6 @@
 package com.tzld.piaoquan.ad.engine.service.score.strategy;
 
+import com.alibaba.fastjson.JSONObject;
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.tzld.piaoquan.ad.engine.commons.dto.AdPlatformCreativeDTO;
 import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
@@ -7,10 +8,12 @@ import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
 import com.tzld.piaoquan.ad.engine.commons.score.ScorerUtils;
 import com.tzld.piaoquan.ad.engine.commons.thread.ThreadPoolFactory;
 import com.tzld.piaoquan.ad.engine.commons.util.*;
+import com.tzld.piaoquan.ad.engine.service.entity.CorrectCpaParam;
 import com.tzld.piaoquan.ad.engine.service.entity.GuaranteeView;
 import com.tzld.piaoquan.ad.engine.service.feature.Feature;
 import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;
@@ -63,6 +66,8 @@ public class RankStrategyBy679 extends RankStrategyBasic {
         }
 
         long start = System.currentTimeMillis();
+        //过滤创意
+        filterRequestAdList(request, scoreParam);
         // 特征处理
         // feature1
         Feature feature = this.getFeature(scoreParam, request);
@@ -92,7 +97,9 @@ public class RankStrategyBy679 extends RankStrategyBasic {
 
         Map<String, String> sceneFeatureMap = this.handleSceneFeature(ts);
         long time1 = System.currentTimeMillis();
-        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, scoreParam);
+        boolean isGuaranteedFlow = getIsGuaranteedFlow(scoreParam);
+        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, isGuaranteedFlow);
+        Map<Long, CorrectCpaParam> correctCpaMap = getCorrectCpaParamMap(request, scoreParam);
         List<AdRankItem> adRankItems = new ArrayList<>();
         Random random = new Random();
         List<Future<AdRankItem>> futures = new ArrayList<>();
@@ -119,7 +126,9 @@ public class RankStrategyBy679 extends RankStrategyBasic {
                         adRankItem.getExt().put("isApi", "1");
                     }
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
-                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt());
+                    adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getCreativeId())));
+                    adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getCreativeId()).getCorrectionFactor());
+                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow);
                     String cidStr = dto.getCreativeId().toString();
                     Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                     Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());
@@ -213,27 +222,38 @@ public class RankStrategyBy679 extends RankStrategyBasic {
         // getScorerPipeline
         List<AdRankItem> result = ScorerUtils.getScorerPipeline(ScorerUtils.XGBOOST_SCORE_CONF_20241105).scoring(sceneFeatureMap, userFeatureMap, adRankItems);
         long time5 = System.currentTimeMillis();
+        calculateCtcvrScore(result, request, scoreParam);
         // loop
         double cpmCoefficient = weightParam.getOrDefault("cpmCoefficient", 0.9);
-        for (AdRankItem item : result) {
 
+        boolean isGuaranteeType = false;
+        for (AdRankItem item : result) {
+            double bid = item.getCpa();
+            if (scoreParam.getExpCodeSet().contains(correctCpaExp1) || scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
+                Double correctionFactor = (Double) item.getExt().get("correctionFactor");
+                item.getScoreMap().put("correctionFactor", correctionFactor);
+                bid = bid * correctionFactor;
+            }
+            item.getScoreMap().put("ecpm", item.getLrScore() * bid * 1000);
+            if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
+                isGuaranteeType = true;
+            }
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
-            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(scoreParam, item.getExt());
-            item.setScore(item.getLrScore() * scoreCoefficient * item.getCpa() * guaranteeScoreCoefficient);
+            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
+            double score = item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient;
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpm", item.getCpm());
+            item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
-
             // 没有转化回传的广告主,使用后台配置的CPM
             if (noApiAdVerIds.contains(item.getAdVerId())) {
-                item.setScore(item.getCpm() * cpmCoefficient / 1000);
+                score = item.getCpm() * cpmCoefficient / 1000;
             }
-
-            putMetaFeature(item, feature, reqFeature, sceneFeatureMap, request);
+            item.setScore(score);
         }
 
         log.info("cost={}, feature1={}, feature2={}, feature31={}, feature32={}, feature4={}, getScorerPipeline={}, " +
@@ -243,6 +263,11 @@ public class RankStrategyBy679 extends RankStrategyBasic {
 
         result.sort(ComparatorUtil.equalsRandomComparator());
 
+        if (CollectionUtils.isNotEmpty(result)) {
+            AdRankItem top1Item = result.get(0);
+            top1Item.getExt().put("isGuaranteeType", isGuaranteeType);
+            putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
+        }
         return result;
     }
 

+ 27 - 34
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy680.java

@@ -6,6 +6,7 @@ import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
 import com.tzld.piaoquan.ad.engine.commons.score.ScorerUtils;
 import com.tzld.piaoquan.ad.engine.commons.thread.ThreadPoolFactory;
 import com.tzld.piaoquan.ad.engine.commons.util.*;
+import com.tzld.piaoquan.ad.engine.service.entity.CorrectCpaParam;
 import com.tzld.piaoquan.ad.engine.service.entity.GuaranteeView;
 import com.tzld.piaoquan.ad.engine.service.feature.Feature;
 import com.tzld.piaoquan.ad.engine.commons.dto.AdPlatformCreativeDTO;
@@ -75,6 +76,8 @@ public class RankStrategyBy680 extends RankStrategyBasic {
         }
 
         long start = System.currentTimeMillis();
+        //过滤创意
+        filterRequestAdList(request, scoreParam);
         // 特征处理
         // feature1
         Feature feature = this.getFeature(scoreParam, request);
@@ -89,30 +92,6 @@ public class RankStrategyBy680 extends RankStrategyBasic {
 
         Map<String, String> userFeatureMap = new HashMap<>();
         Map<String, String> c1Feature = userFeature.getOrDefault("alg_mid_feature_ad_action", new HashMap<>());
-        //用户历史是否有转化
-        boolean targetCrowd = false;
-        if (c1Feature.containsKey("user_conver_ad_class") && StringUtils.isNotEmpty(c1Feature.get("user_conver_ad_class"))
-                && c1Feature.get("user_conver_ad_class").contains("中医")) {
-            targetCrowd = true;
-        } else {
-            if (c1Feature.containsKey("user_conver_ad_class_history_delta")
-                    && StringUtils.isNotEmpty(c1Feature.get("user_conver_ad_class_history_delta"))
-                    && c1Feature.get("user_conver_ad_class_history_delta").contains("中医")) {
-                targetCrowd = true;
-            }
-        }
-        //满足试验条件&&特定人群
-        if (scoreParam.getExpCodeSet().contains(excludeExp) && targetCrowd) {
-            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));
-        }
 
         List<TupleMapEntry<Tuple5>> midActionList = this.handleC1Feature(c1Feature, userFeatureMap);
 
@@ -130,7 +109,9 @@ public class RankStrategyBy680 extends RankStrategyBasic {
         Map<String, String> sceneFeatureMap = this.handleSceneFeature(ts);
         long time1 = System.currentTimeMillis();
 
-        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, scoreParam);
+        boolean isGuaranteedFlow = getIsGuaranteedFlow(scoreParam);
+        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, isGuaranteedFlow);
+        Map<Long, CorrectCpaParam> correctCpaMap = getCorrectCpaParamMap(request, scoreParam);
         List<AdRankItem> adRankItems = new ArrayList<>();
         Random random = new Random();
         List<Future<AdRankItem>> futures = new ArrayList<>();
@@ -149,7 +130,6 @@ public class RankStrategyBy680 extends RankStrategyBasic {
                     adRankItem.setCpm(ObjUtil.nullOrDefault(dto.getCpm(), 90).doubleValue());
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setCustomerId(dto.getCustomerId());
-                    adRankItem.setProfession(dto.getProfession());
                     adRankItem.setRandom(random.nextInt(1000));
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                         adRankItem.getExt().put("isApi", "0");
@@ -158,7 +138,9 @@ public class RankStrategyBy680 extends RankStrategyBasic {
                     }
 
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
-                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt());
+                    adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getCreativeId())));
+                    adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getCreativeId()).getCorrectionFactor());
+                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow);
                     String cidStr = dto.getCreativeId().toString();
                     Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                     Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());
@@ -260,33 +242,44 @@ public class RankStrategyBy680 extends RankStrategyBasic {
             item.getScoreMap().put("ctcvrScore", calibratedScore);
         }
 
+        calculateCtcvrScore(result, request, scoreParam);
         // loop
         double cpmCoefficient = weightParam.getOrDefault("cpmCoefficient", 0.9);
 
+        boolean isGuaranteeType = false;
         for (AdRankItem item : result) {
-
+            double bid = item.getCpa();
+            if (scoreParam.getExpCodeSet().contains(correctCpaExp1) || scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
+                Double correctionFactor = (Double) item.getExt().get("correctionFactor");
+                item.getScoreMap().put("correctionFactor", correctionFactor);
+                bid = bid * correctionFactor;
+            }
+            item.getScoreMap().put("ecpm", item.getLrScore() * bid * 1000);
+            if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
+                isGuaranteeType = true;
+            }
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
-            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(scoreParam, item.getExt());
-            item.setScore(item.getLrScore() * scoreCoefficient * item.getCpa() * guaranteeScoreCoefficient);
+            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
+            double score = item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient;
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpm", item.getCpm());
+            item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
-
             // 没有转化回传的广告主,使用后台配置的CPM
             if (noApiAdVerIds.contains(item.getAdVerId())) {
-                item.setScore(item.getCpm() * cpmCoefficient / 1000);
+                score = item.getCpm() * cpmCoefficient / 1000;
             }
+            item.setScore(score);
         }
-
-
         result.sort(ComparatorUtil.equalsRandomComparator());
 
         if (CollectionUtils.isNotEmpty(result)) {
             AdRankItem top1Item = result.get(0);
+            top1Item.getExt().put("isGuaranteeType", isGuaranteeType);
             putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
             top1Item.getExt().put("model", "xgb");
         }

+ 28 - 9
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy683.java

@@ -1,10 +1,12 @@
 package com.tzld.piaoquan.ad.engine.service.score.strategy;
 
+import com.alibaba.fastjson.JSONObject;
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
 import com.tzld.piaoquan.ad.engine.commons.score.ScorerUtils;
 import com.tzld.piaoquan.ad.engine.commons.thread.ThreadPoolFactory;
 import com.tzld.piaoquan.ad.engine.commons.util.*;
+import com.tzld.piaoquan.ad.engine.service.entity.CorrectCpaParam;
 import com.tzld.piaoquan.ad.engine.service.entity.GuaranteeView;
 import com.tzld.piaoquan.ad.engine.service.feature.Feature;
 import com.tzld.piaoquan.ad.engine.commons.dto.AdPlatformCreativeDTO;
@@ -72,6 +74,8 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         }
 
         long start = System.currentTimeMillis();
+        //过滤创意
+        filterRequestAdList(request, scoreParam);
         // 特征处理
         // feature1
         Feature feature = this.getFeature(scoreParam, request);
@@ -102,7 +106,9 @@ public class RankStrategyBy683 extends RankStrategyBasic {
         Map<String, String> sceneFeatureMap = this.handleSceneFeature(ts);
         long time1 = System.currentTimeMillis();
 
-        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, scoreParam);
+        boolean isGuaranteedFlow = getIsGuaranteedFlow(scoreParam);
+        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, isGuaranteedFlow);
+        Map<Long, CorrectCpaParam> correctCpaMap = getCorrectCpaParamMap(request, scoreParam);
         List<AdRankItem> adRankItems = new ArrayList<>();
         Random random = new Random();
         List<Future<AdRankItem>> futures = new ArrayList<>();
@@ -121,7 +127,6 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                     adRankItem.setCpm(ObjUtil.nullOrDefault(dto.getCpm(), 90).doubleValue());
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setCustomerId(dto.getCustomerId());
-                    adRankItem.setProfession(dto.getProfession());
                     adRankItem.setRandom(random.nextInt(1000));
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                         adRankItem.getExt().put("isApi", "0");
@@ -130,7 +135,9 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                     }
 
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
-                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt());
+                    adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getCreativeId())));
+                    adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getCreativeId()).getCorrectionFactor());
+                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow);
                     String cidStr = dto.getCreativeId().toString();
                     Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                     Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());
@@ -232,26 +239,37 @@ public class RankStrategyBy683 extends RankStrategyBasic {
             item.getScoreMap().put("ctcvrScore", calibratedScore);
         }
 
+        calculateCtcvrScore(result, request, scoreParam);
         // loop
         double cpmCoefficient = weightParam.getOrDefault("cpmCoefficient", 0.9);
-
+        boolean isGuaranteeType = false;
         for (AdRankItem item : result) {
-
+            if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
+                isGuaranteeType = true;
+            }
+            double bid = item.getCpa();
+            if (scoreParam.getExpCodeSet().contains(correctCpaExp1) || scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
+                Double correctionFactor = (Double) item.getExt().get("correctionFactor");
+                item.getScoreMap().put("correctionFactor", correctionFactor);
+                bid = bid * correctionFactor;
+            }
+            item.getScoreMap().put("ecpm", item.getLrScore() * bid * 1000);
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
-            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(scoreParam, item.getExt());
-            item.setScore(item.getLrScore() * scoreCoefficient * item.getCpa() * guaranteeScoreCoefficient);
+            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
+            double score = item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient;
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpm", item.getCpm());
+            item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
-
             // 没有转化回传的广告主,使用后台配置的CPM
             if (noApiAdVerIds.contains(item.getAdVerId())) {
-                item.setScore(item.getCpm() * cpmCoefficient / 1000);
+                score = item.getCpm() * cpmCoefficient / 1000;
             }
+            item.setScore(score);
         }
 
 
@@ -259,6 +277,7 @@ public class RankStrategyBy683 extends RankStrategyBasic {
 
         if (CollectionUtils.isNotEmpty(result)) {
             AdRankItem top1Item = result.get(0);
+            top1Item.getExt().put("isGuaranteeType", isGuaranteeType);
             putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
         }
         long time6 = System.currentTimeMillis();

+ 8 - 4
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy687.java

@@ -104,8 +104,7 @@ public class RankStrategyBy687 extends RankStrategyBasic {
         Map<String, String> reqFeature = this.getReqFeature(scoreParam, request);
 
         List<AdRankItem> result = new ArrayList<>(rankItems.size());
-        for (int i = 1; i < rankItems.size(); i++) {
-            AdRankItem rankItem = rankItems.get(i);
+        for (AdRankItem rankItem : rankItems) {
             // 补充特征信息
             this.fullMetaFeature(rankItem, userLayer, allCustomerFeatureMap, allCreativeFeatureInfoMap, feature);
             this.putMetaFeature(rankItem, feature, reqFeature, sceneFeature, request);
@@ -126,7 +125,9 @@ public class RankStrategyBy687 extends RankStrategyBasic {
 
         String layer = userLayer.getOrDefault("layer", "无曝光");
         String clazz = userLayer.getOrDefault("class", "近期未出现");
-
+        if (Objects.equals(layer, "已转化")) {
+            layer = "有转化";
+        }
         if (request.getIsFilterUser()) {
             return String.join("-", layer, "炸", clazz);
         } else {
@@ -242,7 +243,10 @@ public class RankStrategyBy687 extends RankStrategyBasic {
             HotRankFeatureInfo prev = rankItem.get(i - 1);
             double currWeight1 = prev.getWeight() * (1 - (1 - curr.getCpm() / prev.getCpm()) * hotRankCalcWeightCoefficient);
             double currWeight2 = prev.getWeight() / hotRankCalcWeightCoefficient;
-            double currWeight = Math.max(currWeight1, currWeight2);
+            double currWeight = Math.min(currWeight1, currWeight2);
+            if (currWeight <= 1) {
+                currWeight = Math.max(currWeight1, currWeight2);
+            }
             curr.setWeight(Math.max(1, currWeight));
         }
     }

+ 27 - 39
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy688.java

@@ -9,6 +9,7 @@ import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
 import com.tzld.piaoquan.ad.engine.commons.score.ScorerUtils;
 import com.tzld.piaoquan.ad.engine.commons.thread.ThreadPoolFactory;
 import com.tzld.piaoquan.ad.engine.commons.util.*;
+import com.tzld.piaoquan.ad.engine.service.entity.CorrectCpaParam;
 import com.tzld.piaoquan.ad.engine.service.entity.GuaranteeView;
 import com.tzld.piaoquan.ad.engine.service.feature.Feature;
 import com.tzld.piaoquan.recommend.feature.domain.ad.base.AdRankItem;
@@ -45,9 +46,6 @@ public class RankStrategyBy688 extends RankStrategyBasic {
     @Value("${word2vec.exp:694}")
     private String word2vecExp;
 
-    @Value("${swap.exp:772}")
-    protected String excludeExp;
-
 
     // FIXME(zhoutian): 可能需要独立配置
     @ApolloJsonValue("${rank.score.weight.680:{}}")
@@ -82,6 +80,8 @@ public class RankStrategyBy688 extends RankStrategyBasic {
         }
 
         long start = System.currentTimeMillis();
+        //过滤创意
+        filterRequestAdList(request, scoreParam);
         // 特征处理
         // feature1
         Feature feature = this.getFeature(scoreParam, request);
@@ -96,31 +96,6 @@ public class RankStrategyBy688 extends RankStrategyBasic {
 
         Map<String, String> userFeatureMap = new HashMap<>();
         Map<String, String> c1Feature = userFeature.getOrDefault("alg_mid_feature_ad_action", new HashMap<>());
-        //用户历史是否有转化
-        boolean targetCrowd = false;
-        if (c1Feature.containsKey("user_conver_ad_class") && StringUtils.isNotEmpty(c1Feature.get("user_conver_ad_class"))
-                && c1Feature.get("user_conver_ad_class").contains("中医")) {
-            targetCrowd = true;
-        } else {
-            if (c1Feature.containsKey("user_conver_ad_class_history_delta")
-                    && StringUtils.isNotEmpty(c1Feature.get("user_conver_ad_class_history_delta"))
-                    && c1Feature.get("user_conver_ad_class_history_delta").contains("中医")) {
-                targetCrowd = true;
-            }
-        }
-        //满足试验条件&&特定人群
-        if (scoreParam.getExpCodeSet().contains(excludeExp) && targetCrowd) {
-            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));
-        }
-
         List<TupleMapEntry<Tuple5>> midActionList = this.handleC1Feature(c1Feature, userFeatureMap);
 
         Map<String, Double> midTimeDiffMap = this.parseC1FeatureListToTimeDiffMap(midActionList, ts);
@@ -167,7 +142,9 @@ public class RankStrategyBy688 extends RankStrategyBasic {
         Map<String, String> sceneFeatureMap = this.handleSceneFeature(ts);
         long time1 = System.currentTimeMillis();
 
-        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, scoreParam);
+        boolean isGuaranteedFlow = getIsGuaranteedFlow(scoreParam);
+        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, isGuaranteedFlow);
+        Map<Long, CorrectCpaParam> correctCpaMap = getCorrectCpaParamMap(request, scoreParam);
         List<AdRankItem> adRankItems = new ArrayList<>();
         Random random = new Random();
         List<Future<AdRankItem>> futures = new ArrayList<>();
@@ -186,7 +163,6 @@ public class RankStrategyBy688 extends RankStrategyBasic {
                     adRankItem.setCpm(ObjUtil.nullOrDefault(dto.getCpm(), 90).doubleValue());
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setCustomerId(dto.getCustomerId());
-                    adRankItem.setProfession(dto.getProfession());
                     adRankItem.setRandom(random.nextInt(1000));
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                         adRankItem.getExt().put("isApi", "0");
@@ -194,7 +170,9 @@ public class RankStrategyBy688 extends RankStrategyBasic {
                         adRankItem.getExt().put("isApi", "1");
                     }
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
-                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt());
+                    adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getCreativeId())));
+                    adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getCreativeId()).getCorrectionFactor());
+                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow);
                     String cidStr = dto.getCreativeId().toString();
                     Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                     Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());
@@ -315,20 +293,29 @@ public class RankStrategyBy688 extends RankStrategyBasic {
             item.getScoreMap().put("modelCtcvrScore", calibratedScore);
             item.getScoreMap().put("ctcvrScore", calibratedScore);
         }
-        //校准Ctcvr
-        calibrationCtcvrScore(scoreParam, result, request.getMid(), request.getIsFilterUser(), "dnn");
 
+        calculateCtcvrScore(result, request, scoreParam);
         // loop
         double cpmCoefficient = weightParam.getOrDefault("cpmCoefficient", 0.9);
-
+        boolean isGuaranteeType = false;
         for (AdRankItem item : result) {
-
+            double bid = item.getCpa();
+            if (scoreParam.getExpCodeSet().contains(correctCpaExp1) || scoreParam.getExpCodeSet().contains(correctCpaExp2)) {
+                Double correctionFactor = (Double) item.getExt().get("correctionFactor");
+                item.getScoreMap().put("correctionFactor", correctionFactor);
+                bid = bid * correctionFactor;
+            }
+            item.getScoreMap().put("ecpm", item.getLrScore() * bid * 1000);
+            if (isGuaranteedFlow && item.getExt().get("isGuaranteed") != null && (boolean) item.getExt().get("isGuaranteed")) {
+                isGuaranteeType = true;
+            }
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
-            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(scoreParam, item.getExt());
-            item.setScore(item.getLrScore() * scoreCoefficient * item.getCpa() * guaranteeScoreCoefficient);
+            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
+            double score = item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient;
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpm", item.getCpm());
+            item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
             item.getFeatureMap().putAll(userFeatureMap);
@@ -336,8 +323,9 @@ public class RankStrategyBy688 extends RankStrategyBasic {
 
             // 没有转化回传的广告主,使用后台配置的CPM
             if (noApiAdVerIds.contains(item.getAdVerId())) {
-                item.setScore(item.getCpm() * cpmCoefficient / 1000);
+                score = item.getCpm() * cpmCoefficient / 1000;
             }
+            item.setScore(score);
         }
 
 
@@ -345,8 +333,8 @@ public class RankStrategyBy688 extends RankStrategyBasic {
 
         if (CollectionUtils.isNotEmpty(result)) {
             AdRankItem top1Item = result.get(0);
+            top1Item.getExt().put("isGuaranteeType", isGuaranteeType);
             putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
-            top1Item.getExt().put("model", "dnn");
         }
         long time6 = System.currentTimeMillis();
         log.info("cost={}, getFeature={}, handleFeature={},  similar={}, bucketFeature={}, getScorerPipeline={}, " +