|
@@ -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;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|