yaodaoseng 6 天之前
父節點
當前提交
8e1cf627d8

+ 53 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/enums/CrowdLayerEnum.java

@@ -0,0 +1,53 @@
+package com.tzld.piaoquan.ad.engine.commons.enums;
+
+/**
+ * 人群分层枚举
+ */
+public enum CrowdLayerEnum {
+
+
+    NO_EXPOSURE(1, "无曝光"),
+    WITH_CONVERSION(2, "有转化"),
+    WITH_EXPOSURE_NO_CONVERSION(3, "有曝光无转化"),
+    ;
+    private Integer code;
+    private String desc;
+
+    CrowdLayerEnum(Integer code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public Integer getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public static CrowdLayerEnum getByCode(Integer code) {
+        if (code == null) {
+            return null;
+        }
+        for (CrowdLayerEnum constants : values()) {
+            if (constants.getCode().equals(code)) {
+                return constants;
+            }
+        }
+        return null;
+    }
+
+    public static CrowdLayerEnum getBytDesc( String desc) {
+        if (desc == null) {
+            return null;
+        }
+        for (CrowdLayerEnum constants : values()) {
+            if (constants.getDesc().equals(desc)) {
+                return constants;
+            }
+        }
+        return null;
+    }
+
+}

+ 6 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/entity/GuaranteeView.java

@@ -16,4 +16,10 @@ public class GuaranteeView {
     private Integer guaranteeNum;
 
     private Double guaranteeRate;
+
+    /**
+     * @see com.tzld.piaoquan.ad.engine.commons.enums.CrowdLayerEnum
+     * CrowdLayerEnum.code,保量人群code 码,(1,2.。。)中间用英文逗号隔开
+      */
+    private String guaranteeCrowdCode;
 }

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

@@ -5,14 +5,13 @@ import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.TypeReference;
 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.CrowdLayerEnum;
 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.CalibrationModelCtcvrData;
 import com.tzld.piaoquan.ad.engine.service.entity.CorrectCpaParam;
@@ -99,6 +98,10 @@ public abstract class RankStrategyBasic implements RankStrategy {
     @Value("${guarantee.switching.time:1754236800000}")
     protected Long guaranteeSwitchingTime;
 
+    // 保量人群加权系数配置
+    @Value("${guarantee.crowd.weight.coefficient:1.0}")
+    protected Double guaranteeCrowdWeightCoefficient;
+
     @Autowired
     private FeatureService featureService;
     @Autowired
@@ -330,11 +333,11 @@ public abstract class RankStrategyBasic implements RankStrategy {
         return map;
     }
 
-    protected void setGuaranteeWeight(Map<String, GuaranteeView> map, String adVerId, Map<String, Object> ext, boolean isGuaranteedFlow) {
+    protected void setGuaranteeWeight(Map<String, GuaranteeView> map, String adVerId, Map<String, Object> ext, boolean isGuaranteedFlow, Map<String, String> reqFeature) {
         if (isGuaranteedFlow && MapUtils.isNotEmpty(map)) {
             GuaranteeView guaranteeView = map.get(adVerId);
             if (guaranteeView != null) {
-                double guaranteeWeight = calculateGuaranteedWeight(guaranteeView);
+                double guaranteeWeight = calculateGuaranteeWeightWithCrowd(guaranteeView, reqFeature);
                 boolean isGuaranteed = isGuaranteed(guaranteeView);
                 ext.put("guaranteeView", guaranteeView.toString());
                 ext.put("guaranteeWeight", guaranteeWeight);
@@ -345,7 +348,70 @@ public abstract class RankStrategyBasic implements RankStrategy {
     }
 
     /**
-     * 计算保量权重系数
+     * 兼容旧版本的setGuaranteeWeight方法(不带reqFeature参数)
+     * 为了保持向后兼容性,保留原有方法签名
+     */
+    protected void setGuaranteeWeight(Map<String, GuaranteeView> map, String adVerId, Map<String, Object> ext, boolean isGuaranteedFlow) {
+        setGuaranteeWeight(map, adVerId, ext, isGuaranteedFlow, new HashMap<>());
+    }
+
+    /**
+     * 根据人群信息计算保量权重系数(新版本)
+     *
+     * @param guaranteeView 保量视图对象
+     * @param reqFeature 请求特征信息,包含用户人群layer信息
+     * @return 保量权重系数
+     */
+    protected double calculateGuaranteeWeightWithCrowd(GuaranteeView guaranteeView, Map<String, String> reqFeature) {
+        // 空值检查:如果保量视图为空,返回默认权重
+        if (guaranteeView == null) {
+            return 1.0;
+        }
+
+        // 判断是否配置了保量人群代码
+        String guaranteeCrowdCode = guaranteeView.getGuaranteeCrowdCode();
+        if (StringUtils.isEmpty(guaranteeCrowdCode)) {
+            // 保量人群代码无值,走原有逻辑
+            return calculateGuaranteedWeight(guaranteeView);
+        }
+        // 判断是否勾选了全部人群
+        if (isFullCrowd(guaranteeCrowdCode)) {
+            // 勾选了全部人群,走原有逻辑
+            return calculateGuaranteedWeight(guaranteeView);
+        }
+
+        // 保量人群代码有值,进行人群匹配判断
+        String userLayer = reqFeature.getOrDefault("layer", "");
+
+        // 获取用户人群对应的CrowdLayerEnum code
+        Integer userCrowdCode = getUserCrowdCode(userLayer);
+        if (userCrowdCode == null) {
+            log.error("RankStrategyBasic calculateGuaranteeWeightWithCrowd 无法获取用户人群代码,userLayer={}", userLayer);
+            return 1.0;
+        }
+
+        // 判断用户人群是否在保量人群范围内
+        boolean isUserInGuaranteeCrowd = isUserInGuaranteeCrowd(guaranteeCrowdCode, userCrowdCode);
+
+        if (isUserInGuaranteeCrowd) {
+            // 到这里,就是用户在保量人群中,且广告主勾选的不是全部人群
+            // 计算保量权重
+            double baseWeight = calculateGuaranteedWeight(guaranteeView);
+            // 额外加权
+            // 对勾选了保量人群,且不是勾选了全部的保量人群的广告进行额外加权
+            double finalWeight = baseWeight * guaranteeCrowdWeightCoefficient;
+            log.debug("RankStrategyBasic 保量人群加权: userLayer={}, guaranteeCrowdCode={}, baseWeight={}, coefficient={}, finalWeight={}",
+                     userLayer, guaranteeCrowdCode, baseWeight, guaranteeCrowdWeightCoefficient, finalWeight);
+            return finalWeight;
+        } else {
+            // 用户不在保量人群范围内,权重设为1
+            log.debug("RankStrategyBasic 用户不在保量人群范围内: userLayer={}, guaranteeCrowdCode={}", userLayer, guaranteeCrowdCode);
+            return 1.0;
+        }
+    }
+
+    /**
+     * 计算保量权重系数(原有逻辑)
      *
      * 保量逻辑说明:
      * 1. 根据广告主的保量配置(保量比例、保量上限)和实际曝光情况计算权重
@@ -778,4 +844,90 @@ public abstract class RankStrategyBasic implements RankStrategy {
     private boolean isValidViews(Integer views, int threshold) {
         return views != null && views >= threshold;
     }
+
+    /**
+     * 根据用户人群layer获取对应的CrowdLayerEnum code
+     *
+     * @param userLayer 用户人群layer,如"无曝光"、"有转化"、"有曝光无转化"
+     * @return 对应的CrowdLayerEnum code,如果无法匹配则返回null
+     */
+    private Integer getUserCrowdCode(String userLayer) {
+        if (StringUtils.isEmpty(userLayer)) {
+            return null;
+        }
+
+        // 处理可能的"-炸"后缀
+        String cleanLayer = userLayer.replace("-炸", "");
+
+        // 使用CrowdLayerEnum提供的方法根据描述获取枚举对象
+        CrowdLayerEnum crowdLayerEnum = CrowdLayerEnum.getBytDesc(cleanLayer);
+        if (crowdLayerEnum != null) {
+            return crowdLayerEnum.getCode();
+        }
+
+        log.warn("无法匹配用户人群layer到CrowdLayerEnum: {}", userLayer);
+        return null;
+    }
+
+    /**
+     * 判断用户人群是否在保量人群范围内
+     *
+     * @param guaranteeCrowdCode 保量人群代码字符串,格式如"1,2,3"
+     * @param userCrowdCode 用户人群代码
+     * @return true表示用户在保量人群范围内
+     */
+    private boolean isUserInGuaranteeCrowd(String guaranteeCrowdCode, Integer userCrowdCode) {
+        if (StringUtils.isEmpty(guaranteeCrowdCode) || userCrowdCode == null) {
+            return false;
+        }
+
+        try {
+            // 将保量人群代码字符串按逗号分割
+            String[] crowdCodes = guaranteeCrowdCode.split(",");
+            for (String code : crowdCodes) {
+                if (String.valueOf(userCrowdCode).equals(code.trim())) {
+                    return true;
+                }
+            }
+        } catch (Exception e) {
+            log.error("解析保量人群代码失败: guaranteeCrowdCode={}", guaranteeCrowdCode, e);
+        }
+
+        return false;
+    }
+
+    /**
+     * 判断是否全部保量人群
+     * @param guaranteeCrowdCode 保量人群代码字符串
+     * @return true表示是全部保量人群
+     */
+    private boolean isFullCrowd(String guaranteeCrowdCode) {
+        if (StringUtils.isEmpty(guaranteeCrowdCode)) {
+            return false;
+        }
+
+        try {
+            // 获取CrowdLayerEnum中的所有code值
+            Set<String> allCrowdCodes = new HashSet<>();
+            for (CrowdLayerEnum crowdLayer : CrowdLayerEnum.values()) {
+                allCrowdCodes.add(String.valueOf(crowdLayer.getCode()));
+            }
+
+            // 解析保量人群代码
+            Set<String> guaranteeCodes = new HashSet<>();
+            String[] codes = guaranteeCrowdCode.split(",");
+            for (String code : codes) {
+                guaranteeCodes.add(code.trim());
+            }
+
+            // 判断保量人群代码是否为全量值(包含所有CrowdLayerEnum的code)
+            boolean isFullCrowd = guaranteeCodes.containsAll(allCrowdCodes);
+            return isFullCrowd;
+
+        } catch (Exception e) {
+            log.error("判断保量人群加权失败: guaranteeCrowdCode={}",
+                     guaranteeCrowdCode, e);
+            return false;
+        }
+    }
 }

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

@@ -128,7 +128,7 @@ public class RankStrategyBy679 extends RankStrategyBasic {
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
-                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow);
+                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     String cidStr = dto.getCreativeId().toString();
                     Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                     Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());

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

@@ -140,7 +140,7 @@ public class RankStrategyBy680 extends RankStrategyBasic {
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
-                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow);
+                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     String cidStr = dto.getCreativeId().toString();
                     Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                     Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());

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

@@ -137,7 +137,7 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
-                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow);
+                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     String cidStr = dto.getCreativeId().toString();
                     Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                     Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());

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

@@ -180,7 +180,7 @@ public class RankStrategyBy688 extends RankStrategyBasic {
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
-                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow);
+                    setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     String cidStr = dto.getCreativeId().toString();
                     Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
                     Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, new HashMap<>());