47 Commity a78fca9264 ... c0e576f82f

Autor SHA1 Wiadomość Data
  jiandong.liu c0e576f82f Merge branch 'ljd/feature-eureka-fix' into test 1 miesiąc temu
  jiandong.liu 90bd5b34fe Add graceful shutdown and lifecycle timeout configuration 1 miesiąc temu
  fanjinyang 692a91367a Merge branch '20260327_feature_fjy_ecpm_expirement_last_2' of algorithm/ad-engine into master 1 miesiąc temu
  yaodaoseng a43e93de4b 尾号ecpm小流量实验尾号修改 1 miesiąc temu
  jiachanghui 89a97fcda7 Merge branch 'h5_agile_control' of algorithm/ad-engine into master 1 miesiąc temu
  jch 04c9149bc6 敏捷控制H5 1 miesiąc temu
  jiachanghui c7b0b2da8e Merge branch 'h5_agile_control' of algorithm/ad-engine into master 1 miesiąc temu
  jch 6919776cb9 敏捷控制H5 1 miesiąc temu
  liujiandong 763b371d70 Merge branch 'ljd/feature-eureka-fix' of algorithm/ad-engine into master 1 miesiąc temu
  jiandong.liu f0c9150386 Update Eureka instance ID format and fix xgboost path configuration 1 miesiąc temu
  jiandong.liu fbb424b2e9 Update Eureka instance ID format and fix xgboost path configuration 1 miesiąc temu
  jiachanghui ffc4bc333d Merge branch 'punish_anomalous_cid' of algorithm/ad-engine into master 1 miesiąc temu
  jch 883a5b5597 打压ctcvr异常cid 1 miesiąc temu
  jiachanghui eafac6596f Merge branch 'punish_anomalous_cid' of algorithm/ad-engine into master 1 miesiąc temu
  jch 6236a97322 打压ctcvr异常cid 1 miesiąc temu
  jiandong.liu 625763e954 Merge branch 'ljd/feature-new-sku-20260309' 1 miesiąc temu
  jiandong.liu 013b24aa92 feat: 添加广告职业ID、类别ID和SKU ID到多个排名策略 1 miesiąc temu
  jiandong.liu 5802cc502b Merge branch 'ljd/feature-new-sku-20260309' 1 miesiąc temu
  jiandong.liu e41036566c feat: 添加广告SKU名称到多个排名策略 1 miesiąc temu
  jiandong.liu a7f560a6be Merge branch 'ljd/feature-new-sku-20260309' 1 miesiąc temu
  jiandong.liu 8d1fe0036b feat: 添加广告职业名称、类别名称和SKU代码到多个排名策略 1 miesiąc temu
  jiandong.liu a1d1153f8a Merge branch 'ljd/feature-new-sku-20260309' 1 miesiąc temu
  jiandong.liu 5fc96c04d7 Update recommend-feature-client to version 1.1.35 1 miesiąc temu
  liujiandong 3b12e49070 Merge branch 'ljd/feature-new-sku-20260309' of algorithm/ad-engine into master 1 miesiąc temu
  jiandong.liu 3f2693aa9f Refactor: Remove unused normalizeAdProfessionName method and calls 1 miesiąc temu
  jiandong.liu e1e72c328b feat: 添加广告职业ID和名称到特征映射 1 miesiąc temu
  jiachanghui 5eaddd6533 Merge branch 'h5_less_exposure_min' of algorithm/ad-engine into master 1 miesiąc temu
  jch 100c0d4dce h5和曝光少,取最小score 1 miesiąc temu
  jiachanghui f19b9a99aa Merge branch 'h5_less_exposure_min' of algorithm/ad-engine into master 1 miesiąc temu
  jch 138c128927 h5和曝光少,取最小score 1 miesiąc temu
  jiachanghui 6547bb712c Merge branch 'h5_less_exposure_min' of algorithm/ad-engine into master 1 miesiąc temu
  jch 3776bea3ca h5和曝光少,取最小score 1 miesiąc temu
  jiachanghui e7f3a14814 Merge branch 'h5_less_exposure_min' of algorithm/ad-engine into master 1 miesiąc temu
  jch 290e66c03e h5和曝光少,取最小score 1 miesiąc temu
  jiandong.liu 02ef894339 feat: 添加行业信息到广告排名项 1 miesiąc temu
  jiandong.liu 4dddc948a0 Refactor: Normalize ad profession name in RankStrategyBasic and subclasses 1 miesiąc temu
  jiandong.liu f63219cbcf feat: 添加广告扩展 1 miesiąc temu
  jiachanghui 7672c6cef1 Merge branch 'h5_less_exposure_min' of algorithm/ad-engine into master 1 miesiąc temu
  jch 35cb90ecd6 h5和曝光少,取最小score 1 miesiąc temu
  jiachanghui e716729e43 Merge branch 'h5_less_exposure_min' of algorithm/ad-engine into master 2 miesięcy temu
  jch fe4adc3b28 h5和曝光少,取最小score 2 miesięcy temu
  jiandong.liu 2fdbc6265d fix: 优化H5权重计算逻辑 2 miesięcy temu
  jiachanghui 553284340d Merge branch 'h5_cid_suppress' of algorithm/ad-engine into master 2 miesięcy temu
  jch f3eb10e411 h5降权 2 miesięcy temu
  jch 932b74482a h5降权 2 miesięcy temu
  jiandong.liu 7c416543ee feat: 透传落地页类型 2 miesięcy temu
  jiandong.liu 05e55d85ca 下线5-6点实验 2 miesięcy temu
21 zmienionych plików z 2390 dodań i 20 usunięć
  1. 1 1
      ad-engine-commons/pom.xml
  2. 16 0
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/dto/AdPlatformCreativeDTO.java
  3. 63 0
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/helper/AnomalousCidDataHelper.java
  4. 1 1
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/util/AbUtil.java
  5. 64 0
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/util/RootSessionIdUtil.java
  6. 5 2
      ad-engine-server/src/main/resources/application.yml
  7. 12 7
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/predict/impl/PredictModelServiceImpl.java
  8. 6 1
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/predict/v2/PredictStrategyByAppTypeTail.java
  9. 4 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/impl/RankServiceImpl.java
  10. 1 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/FeaturePrinterStrategy.java
  11. 71 5
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBasic.java
  12. 1 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy679.java
  13. 1 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy680.java
  14. 15 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy683.java
  15. 21 1
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy688.java
  16. 21 1
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy833.java
  17. 15 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy834.java
  18. 21 1
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy840.java
  19. 1038 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy843.java
  20. 1012 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy847.java
  21. 1 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyByWeight.java

+ 1 - 1
ad-engine-commons/pom.xml

@@ -26,7 +26,7 @@
         <dependency>
         <dependency>
             <groupId>com.tzld.piaoquan</groupId>
             <groupId>com.tzld.piaoquan</groupId>
             <artifactId>recommend-feature-client</artifactId>
             <artifactId>recommend-feature-client</artifactId>
-            <version>1.1.32</version>
+            <version>1.1.35</version>
         </dependency>
         </dependency>
         <dependency>
         <dependency>
             <groupId>com.tzld.piaoquan</groupId>
             <groupId>com.tzld.piaoquan</groupId>

+ 16 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/dto/AdPlatformCreativeDTO.java

@@ -47,6 +47,8 @@ public class AdPlatformCreativeDTO {
     // 广告主行业
     // 广告主行业
     private String profession;
     private String profession;
 
 
+    private Integer landingPageType;
+
     private Long skuId;
     private Long skuId;
 
 
     private String customer;
     private String customer;
@@ -56,4 +58,18 @@ public class AdPlatformCreativeDTO {
     private String categoryName;
     private String categoryName;
 
 
     private String materialMd5;
     private String materialMd5;
+
+    private Long adProfessionId;
+
+    private String adProfessionName;
+
+    private Long adCategoryId;
+
+    private String adCategoryName;
+
+    private Long adSkuId;
+
+    private String adSkuCode;
+
+    private String adSkuName;
 }
 }

+ 63 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/helper/AnomalousCidDataHelper.java

@@ -0,0 +1,63 @@
+package com.tzld.piaoquan.ad.engine.commons.helper;
+
+import com.tzld.piaoquan.ad.engine.commons.redis.AlgorithmRedisHelper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+@Slf4j
+@Component
+public class AnomalousCidDataHelper {
+    @Autowired
+    protected AlgorithmRedisHelper algRedisHelper;
+
+    private static final String redisKey = "ad:engine:strategy:control:cid:anomalous_ctcvr";
+
+    private volatile static Set<Long> cidSet = Collections.emptySet();
+
+    // 服务启动时初始化数据
+    @PostConstruct
+    public void init() {
+        Set<Long> cidSet1 = updateCidSet();
+        cidSet = Collections.unmodifiableSet(cidSet1);
+    }
+
+    // 每5分钟更新一次数据
+    @Scheduled(fixedRate = 5 * 60 * 1000)
+    public void scheduledUpdate() {
+        Set<Long> cidSet1 = updateCidSet();
+        cidSet = Collections.unmodifiableSet(cidSet1);
+    }
+
+    public static boolean contains(long cid) {
+        if (CollectionUtils.isNotEmpty(cidSet)) {
+            return cidSet.contains(cid);
+        }
+        return false;
+    }
+
+    private Set<Long> updateCidSet() {
+        try {
+            Set<Long> tmpCidSet = new HashSet<>();
+            String value = algRedisHelper.get(redisKey);
+            if (null != value && !value.isEmpty()) {
+                String[] cells = value.split(",");
+                for (String cell : cells) {
+                    tmpCidSet.add(Long.valueOf(cell));
+                }
+            }
+            log.info("anomalous cid set init success size={}", tmpCidSet.size());
+            return tmpCidSet;
+        } catch (Exception e) {
+            log.error("update anomalous cid error", e);
+        }
+        return Collections.emptySet();
+    }
+}

+ 1 - 1
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/util/AbUtil.java

@@ -8,7 +8,7 @@ import java.util.stream.Collectors;
 
 
 public class AbUtil {
 public class AbUtil {
 
 
-    public static final List<String> adAlgExpCode = Arrays.asList("679", "680", "683", "687", "688", "833", "834", "840");
+    public static final List<String> adAlgExpCode = Arrays.asList("679", "680", "683", "687", "688", "833", "834", "840", "843", "847");
 
 
     public static Set<String> unfoldAllExpCode(List<Map<String, String>> adAbMap) {
     public static Set<String> unfoldAllExpCode(List<Map<String, String>> adAbMap) {
         if (CollectionUtils.isEmpty(adAbMap)) {
         if (CollectionUtils.isEmpty(adAbMap)) {

+ 64 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/util/RootSessionIdUtil.java

@@ -0,0 +1,64 @@
+package com.tzld.piaoquan.ad.engine.commons.util;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * RootSessionId 工具类
+ * 用于获取 rootSessionId 的尾号,支持从 Apollo 配置中读取倒数第几位
+ *
+ * @author fanjinyang
+ */
+@Component
+public class RootSessionIdUtil {
+
+    /**
+     * 倒数 rootSessionId 的第几个尾号做广告分组,从 0 开始
+     * 0 表示最后一位,1 表示倒数第二位,以此类推
+     * 默认值为 0(最后一位)
+     */
+    @Value("${ad.rootsessionid.tail.number:0}")
+    private Integer tailNumber;
+
+    /**
+     * 获取 rootSessionId 的尾号
+     * 根据配置的 tailNumber 从后往前取字符
+     *
+     * @param rootSessionId 会话ID
+     * @return 尾号字符,如果 rootSessionId 为空或长度不足则返回 null
+     */
+    public String getTail(String rootSessionId) {
+        if (StringUtils.isBlank(rootSessionId)) {
+            return null;
+        }
+
+        // 确保 tailNumber 不为 null
+        int index = (tailNumber != null) ? tailNumber : 0;
+
+        // 确保索引不为负数
+        if (index < 0) {
+            index = 0;
+        }
+
+        // 计算实际位置:从后往前数,0 表示最后一位
+        int position = rootSessionId.length() - 1 - index;
+
+        // 检查位置是否有效
+        if (position < 0 || position >= rootSessionId.length()) {
+            return null;
+        }
+
+        return String.valueOf(rootSessionId.charAt(position));
+    }
+
+    /**
+     * 获取配置的尾号位置
+     *
+     * @return 倒数第几位(从 0 开始)
+     */
+    public Integer getTailNumber() {
+        return tailNumber;
+    }
+}
+

+ 5 - 2
ad-engine-server/src/main/resources/application.yml

@@ -1,7 +1,7 @@
 eureka:
 eureka:
   instance:
   instance:
     prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
     prefer-ip-address: true #是否优先使用IP地址作为主机名的标识,默认false
-    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port} #注册到eureka上的唯一实例ID
+    instance-id: ${spring.application.name}:${POD_NAME}:${server.port} #注册到eureka上的唯一实例ID
     lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
     lease-renewal-interval-in-seconds: 10 #表示eureka client发送心跳给server端的频率,默认30
     lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
     lease-expiration-duration-in-seconds: 30 #表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance,默认90
   client:
   client:
@@ -31,6 +31,8 @@ spring:
             flow-rules-key: sentinel.flowRules
             flow-rules-key: sentinel.flowRules
             default-flow-rule-value: [ ]
             default-flow-rule-value: [ ]
             rule-type: flow
             rule-type: flow
+  lifecycle:
+    timeout-per-shutdown-phase: 30s
 
 
   profiles:
   profiles:
     active: dev
     active: dev
@@ -56,6 +58,7 @@ server:
     context-path: /ad-engine
     context-path: /ad-engine
     session:
     session:
       timeout: 60
       timeout: 60
+  shutdown: graceful
 pagehelper:
 pagehelper:
   helper-dialect: mysql
   helper-dialect: mysql
 
 
@@ -143,4 +146,4 @@ grpc:
 model:
 model:
   xgboost:
   xgboost:
     path: xgboost
     path: xgboost
-    path351: xgboost351
+    path351: xgboost351

+ 12 - 7
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/predict/impl/PredictModelServiceImpl.java

@@ -134,6 +134,9 @@ public class PredictModelServiceImpl implements PredictModelService {
     @Value("${experiment.morning56.ad.hour:5,6}")
     @Value("${experiment.morning56.ad.hour:5,6}")
     private String experimentMorning56WithAdHour;
     private String experimentMorning56WithAdHour;
 
 
+    @Value("${experiment.morning56.switch:false}")
+    private String experimentMorning56Switch;
+
     @Autowired
     @Autowired
     private AdRedisHelper adRedisHelper;
     private AdRedisHelper adRedisHelper;
 
 
@@ -204,15 +207,17 @@ public class PredictModelServiceImpl implements PredictModelService {
             if (0 <= hourOfDay && hourOfDay < 8 && !isAdvanceShowAd()) {
             if (0 <= hourOfDay && hourOfDay < 8 && !isAdvanceShowAd()) {
                 // 0点到8点 && 不是节日
                 // 0点到8点 && 不是节日
 
 
-                // 判断是否在morning56时间范围(5-6点)
                 boolean isInMorning56Time = false;
                 boolean isInMorning56Time = false;
-                try{
-                    String[] split = experimentMorning56WithAdHour.split(",");
-                    if ( Integer.parseInt(split[0]) <= hourOfDay && hourOfDay < Integer.parseInt(split[1]) ) {
-                        isInMorning56Time = true;
+                if (StringUtils.isNotBlank(experimentMorning56Switch) && Objects.equals("true", experimentMorning56Switch)) {
+                    // 判断是否在morning56时间范围(5-6点)
+                    try {
+                        String[] split = experimentMorning56WithAdHour.split(",");
+                        if (Integer.parseInt(split[0]) <= hourOfDay && hourOfDay < Integer.parseInt(split[1])) {
+                            isInMorning56Time = true;
+                        }
+                    } catch (Exception e) {
+                        log.error("experimentMorning56WithAdHour配置异常", e);
                     }
                     }
-                }catch (Exception e){
-                    log.error("experimentMorning56WithAdHour配置异常", e);
                 }
                 }
 
 
                 // 判断是否在817时间范围
                 // 判断是否在817时间范围

+ 6 - 1
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/predict/v2/PredictStrategyByAppTypeTail.java

@@ -2,9 +2,11 @@ package com.tzld.piaoquan.ad.engine.service.predict.v2;
 
 
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.JSONObject;
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
+import com.tzld.piaoquan.ad.engine.commons.util.RootSessionIdUtil;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import java.util.HashMap;
 import java.util.HashMap;
@@ -49,6 +51,9 @@ public class PredictStrategyByAppTypeTail extends BasicPredict {
     @ApolloJsonValue("${experiment.app.type.tail.ecpm.config:[]}")
     @ApolloJsonValue("${experiment.app.type.tail.ecpm.config:[]}")
     private List<RootSessionIdTailConfigItem> configItems;
     private List<RootSessionIdTailConfigItem> configItems;
 
 
+
+    @Autowired
+    private RootSessionIdUtil rootSessionIdUtil;
     /**
     /**
      * 策略名称标识
      * 策略名称标识
      */
      */
@@ -77,7 +82,7 @@ public class PredictStrategyByAppTypeTail extends BasicPredict {
             }
             }
 
 
             // 提取 rootSessionId 的最后一个字符作为尾号
             // 提取 rootSessionId 的最后一个字符作为尾号
-            String tail = rootSessionId.substring(rootSessionId.length() - 1);
+            String tail = rootSessionIdUtil.getTail(rootSessionId);
 
 
             // 遍历配置项,查找同时匹配 appType 和尾号的配置
             // 遍历配置项,查找同时匹配 appType 和尾号的配置
             for (RootSessionIdTailConfigItem item : configItems) {
             for (RootSessionIdTailConfigItem item : configItems) {

+ 4 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/impl/RankServiceImpl.java

@@ -116,6 +116,10 @@ public class RankServiceImpl implements RankService {
                 return ServiceBeanFactory.getBean(RankStrategyBy834.class);
                 return ServiceBeanFactory.getBean(RankStrategyBy834.class);
             case "840":
             case "840":
                 return ServiceBeanFactory.getBean(RankStrategyBy840.class);
                 return ServiceBeanFactory.getBean(RankStrategyBy840.class);
+            case "843":
+                return ServiceBeanFactory.getBean(RankStrategyBy843.class);
+            case "847":
+                return ServiceBeanFactory.getBean(RankStrategyBy847.class);
             default:
             default:
                 return ServiceBeanFactory.getBean(RankStrategyByWeight.class);
                 return ServiceBeanFactory.getBean(RankStrategyByWeight.class);
         }
         }

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

@@ -47,6 +47,7 @@ public class FeaturePrinterStrategy extends RankStrategyBasic {
             adRankItem.getExt().put("isApi", "1");
             adRankItem.getExt().put("isApi", "1");
         }
         }
         adRankItem.getExt().put("recallsources", dto.getRecallSources());
         adRankItem.getExt().put("recallsources", dto.getRecallSources());
+        fillAdRankItemExt(adRankItem, dto);
         putMetaFeature(adRankItem, feature, reqFeature, sceneFeatureMap, request);
         putMetaFeature(adRankItem, feature, reqFeature, sceneFeatureMap, request);
         return Collections.singletonList(adRankItem);
         return Collections.singletonList(adRankItem);
     }
     }

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

@@ -42,6 +42,9 @@ public abstract class RankStrategyBasic implements RankStrategy {
     @ApolloJsonValue("${creative.model.score.coefficient:{}}")
     @ApolloJsonValue("${creative.model.score.coefficient:{}}")
     private Map<Long, Double> creativeScoreCoefficient;
     private Map<Long, Double> creativeScoreCoefficient;
 
 
+    @ApolloJsonValue("${h5.score.suppress:{}}")
+    private Map<String, Double> h5ScoreSuppress;
+
     @Value("${guarantee.exp:742}")
     @Value("${guarantee.exp:742}")
     protected String guaranteeExp;
     protected String guaranteeExp;
 
 
@@ -428,14 +431,13 @@ public abstract class RankStrategyBasic implements RankStrategy {
         }
         }
     }
     }
 
 
-
     protected boolean getIsGuaranteedFlow(ScoreParam scoreParam) {
     protected boolean getIsGuaranteedFlow(ScoreParam scoreParam) {
         // 817实验不要保量
         // 817实验不要保量
-        if( scoreParam.getEngineInfo() != null && "817".equals(scoreParam.getEngineInfo().get("model"))){
+        if (scoreParam.getEngineInfo() != null && "817".equals(scoreParam.getEngineInfo().get("model"))) {
             return false;
             return false;
         }
         }
         // 819实验不要保量
         // 819实验不要保量
-        if( valid819Ab(scoreParam)){
+        if (valid819Ab(scoreParam)) {
             return false;
             return false;
         }
         }
         if (System.currentTimeMillis() < guaranteeSwitchingTime) {
         if (System.currentTimeMillis() < guaranteeSwitchingTime) {
@@ -446,7 +448,7 @@ public abstract class RankStrategyBasic implements RankStrategy {
         return i < guaranteeWeight;
         return i < guaranteeWeight;
     }
     }
 
 
-    private boolean valid819Ab(ScoreParam scoreParam){
+    private boolean valid819Ab(ScoreParam scoreParam) {
         // 819实验判断
         // 819实验判断
         boolean in819Ab = scoreParam.getEngineInfo() != null
         boolean in819Ab = scoreParam.getEngineInfo() != null
                 && "819".equals(scoreParam.getEngineInfo().get("ecpm_model"))
                 && "819".equals(scoreParam.getEngineInfo().get("ecpm_model"))
@@ -899,7 +901,9 @@ public abstract class RankStrategyBasic implements RankStrategy {
         adRankItem.setCampaignId(dto.getCampaignId());
         adRankItem.setCampaignId(dto.getCampaignId());
         adRankItem.setCpm(ObjUtil.nullOrDefault(dto.getCpm(), 90).doubleValue());
         adRankItem.setCpm(ObjUtil.nullOrDefault(dto.getCpm(), 90).doubleValue());
         adRankItem.setSkuId(dto.getSkuId());
         adRankItem.setSkuId(dto.getSkuId());
+        adRankItem.setProfession(dto.getProfession());
         adRankItem.getExt().put("recallsources", dto.getRecallSources());
         adRankItem.getExt().put("recallsources", dto.getRecallSources());
+        fillAdRankItemExt(adRankItem, dto);
         adRankItem.setRandom(new Random().nextInt(1000));
         adRankItem.setRandom(new Random().nextInt(1000));
         if (CollectionUtils.isNotEmpty(noApiAdVerIds)) {
         if (CollectionUtils.isNotEmpty(noApiAdVerIds)) {
             if (noApiAdVerIds.contains(dto.getAdVerId())) {
             if (noApiAdVerIds.contains(dto.getAdVerId())) {
@@ -912,6 +916,23 @@ public abstract class RankStrategyBasic implements RankStrategy {
         return adRankItem;
         return adRankItem;
     }
     }
 
 
+    protected void fillAdRankItemExt(AdRankItem adRankItem, AdPlatformCreativeDTO dto) {
+        adRankItem.setAdProfessionName(dto.getAdProfessionName());
+        adRankItem.setAdCategoryName(dto.getAdCategoryName());
+        adRankItem.setAdCategoryId(dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : null);
+        adRankItem.setAdSkuId(dto.getAdSkuId());
+        adRankItem.setAdSkuCode(dto.getAdSkuCode());
+        adRankItem.setAdSkuName(dto.getAdSkuName());
+        adRankItem.getExt().put("categoryName", StringUtils.defaultString(dto.getCategoryName()));
+        adRankItem.getExt().put("adProfessionId", dto.getAdProfessionId() != null ? String.valueOf(dto.getAdProfessionId()) : "");
+        adRankItem.getExt().put("adProfessionName", StringUtils.defaultString(dto.getAdProfessionName()));
+        adRankItem.getExt().put("adCategoryName", StringUtils.defaultString(dto.getAdCategoryName()));
+        adRankItem.getExt().put("adCategoryId", dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : "");
+        adRankItem.getExt().put("adSkuId", dto.getAdSkuId() != null ? String.valueOf(dto.getAdSkuId()) : "");
+        adRankItem.getExt().put("adSkuCode", StringUtils.defaultString(dto.getAdSkuCode()));
+        adRankItem.getExt().put("adSkuName", StringUtils.defaultString(dto.getAdSkuName()));
+    }
+
     protected void putMetaFeature(AdRankItem adRankItem, Feature feature, Map<String, String> reqFeature,
     protected void putMetaFeature(AdRankItem adRankItem, Feature feature, Map<String, String> reqFeature,
                                   Map<String, String> sceneFeatureMap, RankRecommendRequestParam request) {
                                   Map<String, String> sceneFeatureMap, RankRecommendRequestParam request) {
         if (feature == null) {
         if (feature == null) {
@@ -968,8 +989,16 @@ public abstract class RankStrategyBasic implements RankStrategy {
             reqFeature.put("adid", String.valueOf(adPlatformCreativeDTO.getAdId()));
             reqFeature.put("adid", String.valueOf(adPlatformCreativeDTO.getAdId()));
             reqFeature.put("adverid", String.valueOf(adPlatformCreativeDTO.getAdVerId()));
             reqFeature.put("adverid", String.valueOf(adPlatformCreativeDTO.getAdVerId()));
             reqFeature.put("profession", adPlatformCreativeDTO.getProfession());
             reqFeature.put("profession", adPlatformCreativeDTO.getProfession());
+            reqFeature.put("landingPageType", String.valueOf(adPlatformCreativeDTO.getLandingPageType()));
             reqFeature.put("category_name", adPlatformCreativeDTO.getCategoryName());
             reqFeature.put("category_name", adPlatformCreativeDTO.getCategoryName());
             reqFeature.put("material_md5", adPlatformCreativeDTO.getMaterialMd5());
             reqFeature.put("material_md5", adPlatformCreativeDTO.getMaterialMd5());
+            reqFeature.put("ad_profession_id", adPlatformCreativeDTO.getAdProfessionId() != null ? String.valueOf(adPlatformCreativeDTO.getAdProfessionId()) : "");
+            reqFeature.put("ad_profession_name", adPlatformCreativeDTO.getAdProfessionName() != null ? adPlatformCreativeDTO.getAdProfessionName() : "");
+            reqFeature.put("ad_category_name", adPlatformCreativeDTO.getAdCategoryName());
+            reqFeature.put("ad_category_id", adPlatformCreativeDTO.getAdCategoryId() != null ? String.valueOf(adPlatformCreativeDTO.getAdCategoryId()) : "");
+            reqFeature.put("ad_sku_id", adPlatformCreativeDTO.getAdSkuId() != null ? String.valueOf(adPlatformCreativeDTO.getAdSkuId()) : "");
+            reqFeature.put("ad_sku_code", adPlatformCreativeDTO.getAdSkuCode() != null ? adPlatformCreativeDTO.getAdSkuCode() : "");
+            reqFeature.put("ad_sku_name", adPlatformCreativeDTO.getAdSkuName() != null ? adPlatformCreativeDTO.getAdSkuName() : "");
         }
         }
         adRankItem.getMetaFeatureMap().put("reqFeature", reqFeature);
         adRankItem.getMetaFeatureMap().put("reqFeature", reqFeature);
         adRankItem.getMetaFeatureMap().put("sceneFeature", sceneFeatureMap);
         adRankItem.getMetaFeatureMap().put("sceneFeature", sceneFeatureMap);
@@ -1043,6 +1072,43 @@ public abstract class RankStrategyBasic implements RankStrategy {
         }
         }
     }
     }
 
 
+    // h5 权重
+    protected double getH5SuppressWeight(AdRankItem item) {
+        if (org.springframework.util.CollectionUtils.isEmpty(h5ScoreSuppress) || Objects.isNull(item)) {
+            return 1.0;
+        }
+        try {
+            Integer landingPageType = item.getLandingPageType();
+            String profession = item.getProfession();
+            if (StringUtils.isBlank(profession) || Objects.isNull(landingPageType)) {
+                return 1.0;
+            }
+            if (Objects.equals(2, landingPageType) && h5ScoreSuppress.containsKey(profession)) {
+                return h5ScoreSuppress.get(profession);
+            }
+        } catch (Exception e) {
+            log.error("获取h5权重失败", e);
+        }
+        return 1.0;
+    }
+
+
+    protected boolean landingH5Page(AdRankItem item) {
+        if (Objects.isNull(item)) {
+            return false;
+        }
+        try {
+            Integer landingPageType = item.getLandingPageType();
+            if (Objects.isNull(landingPageType)) {
+                return false;
+            }
+            return Objects.equals(2, landingPageType);
+        } catch (Exception e) {
+            log.error("h5判断失败", e);
+        }
+        return false;
+    }
+
     // 安全的数值转换
     // 安全的数值转换
     private double safeDouble(Double value, Double min) {
     private double safeDouble(Double value, Double min) {
         return (value == null || value == 0) ? min : value;
         return (value == null || value == 0) ? min : value;
@@ -1139,4 +1205,4 @@ public abstract class RankStrategyBasic implements RankStrategy {
             return false;
             return false;
         }
         }
     }
     }
-}
+}

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

@@ -130,6 +130,7 @@ public class RankStrategyBy679 extends RankStrategyBasic {
                         adRankItem.getExt().put("isApi", "1");
                         adRankItem.getExt().put("isApi", "1");
                     }
                     }
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);

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

@@ -143,6 +143,7 @@ public class RankStrategyBy680 extends RankStrategyBasic {
                     }
                     }
 
 
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);

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

@@ -180,6 +180,7 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                         adRankItem.getExt().put("isApi", "1");
                         adRankItem.getExt().put("isApi", "1");
                     }
                     }
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
@@ -205,6 +206,13 @@ public class RankStrategyBy683 extends RankStrategyBasic {
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
+                    cidFeatureMap.put("ad_profession_id", dto.getAdProfessionId() != null ? String.valueOf(dto.getAdProfessionId()) : "");
+                    cidFeatureMap.put("ad_profession_name", dto.getAdProfessionName() != null ? dto.getAdProfessionName() : "");
+                    cidFeatureMap.put("ad_category_name", dto.getAdCategoryName() != null ? dto.getAdCategoryName() : "");
+                    cidFeatureMap.put("ad_category_id", dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : "");
+                    cidFeatureMap.put("ad_sku_id", dto.getAdSkuId() != null ? String.valueOf(dto.getAdSkuId()) : "");
+                    cidFeatureMap.put("ad_sku_code", dto.getAdSkuCode() != null ? dto.getAdSkuCode() : "");
+                    cidFeatureMap.put("ad_sku_name", dto.getAdSkuName() != null ? dto.getAdSkuName() : "");
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
                         cidFeatureMap.put("cid", "");
                         cidFeatureMap.put("cid", "");
@@ -866,6 +874,13 @@ public class RankStrategyBy683 extends RankStrategyBasic {
             add("user_conver_ad_class");
             add("user_conver_ad_class");
             add("category_name");
             add("category_name");
             add("material_md5");
             add("material_md5");
+            add("ad_profession_id");
+            add("ad_profession_name");
+            add("ad_category_id");
+            add("ad_category_name");
+            add("ad_sku_id");
+            add("ad_sku_code");
+            add("ad_sku_name");
         }};
         }};
     }
     }
 
 

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

@@ -179,6 +179,7 @@ public class RankStrategyBy688 extends RankStrategyBasic {
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setCustomerId(dto.getCustomerId());
                     adRankItem.setCustomerId(dto.getCustomerId());
                     adRankItem.setProfession(dto.getProfession());
                     adRankItem.setProfession(dto.getProfession());
+                    adRankItem.setLandingPageType(dto.getLandingPageType());
                     adRankItem.setRandom(random.nextInt(1000));
                     adRankItem.setRandom(random.nextInt(1000));
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                         adRankItem.getExt().put("isApi", "0");
                         adRankItem.getExt().put("isApi", "0");
@@ -186,6 +187,7 @@ public class RankStrategyBy688 extends RankStrategyBasic {
                         adRankItem.getExt().put("isApi", "1");
                         adRankItem.getExt().put("isApi", "1");
                     }
                     }
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
@@ -211,6 +213,13 @@ public class RankStrategyBy688 extends RankStrategyBasic {
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
+                    cidFeatureMap.put("ad_profession_id", dto.getAdProfessionId() != null ? String.valueOf(dto.getAdProfessionId()) : "");
+                    cidFeatureMap.put("ad_profession_name", dto.getAdProfessionName() != null ? dto.getAdProfessionName() : "");
+                    cidFeatureMap.put("ad_category_name", dto.getAdCategoryName() != null ? dto.getAdCategoryName() : "");
+                    cidFeatureMap.put("ad_category_id", dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : "");
+                    cidFeatureMap.put("ad_sku_id", dto.getAdSkuId() != null ? String.valueOf(dto.getAdSkuId()) : "");
+                    cidFeatureMap.put("ad_sku_code", dto.getAdSkuCode() != null ? dto.getAdSkuCode() : "");
+                    cidFeatureMap.put("ad_sku_name", dto.getAdSkuName() != null ? dto.getAdSkuName() : "");
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSet()) && !DnnCidDataHelper.getCidSet().contains(adRankItem.getAdId())) {
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSet()) && !DnnCidDataHelper.getCidSet().contains(adRankItem.getAdId())) {
                         cidFeatureMap.put("cid", "");
                         cidFeatureMap.put("cid", "");
@@ -349,18 +358,22 @@ public class RankStrategyBy688 extends RankStrategyBasic {
                 isGuaranteeType = true;
                 isGuaranteeType = true;
             }
             }
 
 
+            // h5 降权
+            double h5Weight = this.getH5SuppressWeight(item);
+
             String layerAndCreativeWeightMapKey = getLayerAndCreativeWeightMapKey(peopleLayer, String.valueOf(item.getAdId()));
             String layerAndCreativeWeightMapKey = getLayerAndCreativeWeightMapKey(peopleLayer, String.valueOf(item.getAdId()));
             // 人群分层&创意的权重
             // 人群分层&创意的权重
             double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
             double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
             double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
             double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
-            double score = item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
+            double score = h5Weight * item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpm", item.getCpm());
             item.getScoreMap().put("cpm", item.getCpm());
             item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
+            item.getScoreMap().put("h5", h5Weight);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
 
 
@@ -895,6 +908,13 @@ public class RankStrategyBy688 extends RankStrategyBasic {
             add("user_conver_ad_class");
             add("user_conver_ad_class");
             add("category_name");
             add("category_name");
             add("material_md5");
             add("material_md5");
+            add("ad_profession_id");
+            add("ad_profession_name");
+            add("ad_category_id");
+            add("ad_category_name");
+            add("ad_sku_id");
+            add("ad_sku_code");
+            add("ad_sku_name");
         }};
         }};
     }
     }
 
 

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

@@ -182,6 +182,7 @@ public class RankStrategyBy833 extends RankStrategyBasic {
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setCustomerId(dto.getCustomerId());
                     adRankItem.setCustomerId(dto.getCustomerId());
                     adRankItem.setProfession(dto.getProfession());
                     adRankItem.setProfession(dto.getProfession());
+                    adRankItem.setLandingPageType(dto.getLandingPageType());
                     adRankItem.setRandom(random.nextInt(1000));
                     adRankItem.setRandom(random.nextInt(1000));
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                         adRankItem.getExt().put("isApi", "0");
                         adRankItem.getExt().put("isApi", "0");
@@ -189,6 +190,7 @@ public class RankStrategyBy833 extends RankStrategyBasic {
                         adRankItem.getExt().put("isApi", "1");
                         adRankItem.getExt().put("isApi", "1");
                     }
                     }
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
@@ -214,6 +216,13 @@ public class RankStrategyBy833 extends RankStrategyBasic {
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
+                    cidFeatureMap.put("ad_profession_id", dto.getAdProfessionId() != null ? String.valueOf(dto.getAdProfessionId()) : "");
+                    cidFeatureMap.put("ad_profession_name", dto.getAdProfessionName() != null ? dto.getAdProfessionName() : "");
+                    cidFeatureMap.put("ad_category_name", dto.getAdCategoryName() != null ? dto.getAdCategoryName() : "");
+                    cidFeatureMap.put("ad_category_id", dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : "");
+                    cidFeatureMap.put("ad_sku_id", dto.getAdSkuId() != null ? String.valueOf(dto.getAdSkuId()) : "");
+                    cidFeatureMap.put("ad_sku_code", dto.getAdSkuCode() != null ? dto.getAdSkuCode() : "");
+                    cidFeatureMap.put("ad_sku_name", dto.getAdSkuName() != null ? dto.getAdSkuName() : "");
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
                         cidFeatureMap.put("cid", "");
                         cidFeatureMap.put("cid", "");
@@ -365,6 +374,9 @@ public class RankStrategyBy833 extends RankStrategyBasic {
                 isGuaranteeType = true;
                 isGuaranteeType = true;
             }
             }
 
 
+            // h5 降权
+            double h5Weight = this.getH5SuppressWeight(item);
+
             // 控制曝光权重
             // 控制曝光权重
             Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
             Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
             Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
             Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
@@ -378,13 +390,14 @@ public class RankStrategyBy833 extends RankStrategyBasic {
             double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
             double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
             double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
             double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
-            double score = expWeight * item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
+            double score = h5Weight * expWeight * item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpm", item.getCpm());
             item.getScoreMap().put("cpm", item.getCpm());
             item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
+            item.getScoreMap().put("h5", h5Weight);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
 
 
@@ -923,6 +936,13 @@ public class RankStrategyBy833 extends RankStrategyBasic {
             add("user_conver_ad_class");
             add("user_conver_ad_class");
             add("category_name");
             add("category_name");
             add("material_md5");
             add("material_md5");
+            add("ad_profession_id");
+            add("ad_profession_name");
+            add("ad_category_id");
+            add("ad_category_name");
+            add("ad_sku_id");
+            add("ad_sku_code");
+            add("ad_sku_name");
         }};
         }};
     }
     }
 
 

+ 15 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy834.java

@@ -186,6 +186,7 @@ public class RankStrategyBy834 extends RankStrategyBasic {
                         adRankItem.getExt().put("isApi", "1");
                         adRankItem.getExt().put("isApi", "1");
                     }
                     }
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
@@ -211,6 +212,13 @@ public class RankStrategyBy834 extends RankStrategyBasic {
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
+                    cidFeatureMap.put("ad_profession_id", dto.getAdProfessionId() != null ? String.valueOf(dto.getAdProfessionId()) : "");
+                    cidFeatureMap.put("ad_profession_name", dto.getAdProfessionName() != null ? dto.getAdProfessionName() : "");
+                    cidFeatureMap.put("ad_category_name", dto.getAdCategoryName() != null ? dto.getAdCategoryName() : "");
+                    cidFeatureMap.put("ad_category_id", dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : "");
+                    cidFeatureMap.put("ad_sku_id", dto.getAdSkuId() != null ? String.valueOf(dto.getAdSkuId()) : "");
+                    cidFeatureMap.put("ad_sku_code", dto.getAdSkuCode() != null ? dto.getAdSkuCode() : "");
+                    cidFeatureMap.put("ad_sku_name", dto.getAdSkuName() != null ? dto.getAdSkuName() : "");
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSet()) && !DnnCidDataHelper.getCidSet().contains(adRankItem.getAdId())) {
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSet()) && !DnnCidDataHelper.getCidSet().contains(adRankItem.getAdId())) {
                         cidFeatureMap.put("cid", "");
                         cidFeatureMap.put("cid", "");
@@ -912,6 +920,13 @@ public class RankStrategyBy834 extends RankStrategyBasic {
             add("user_conver_ad_class");
             add("user_conver_ad_class");
             add("category_name");
             add("category_name");
             add("material_md5");
             add("material_md5");
+            add("ad_profession_id");
+            add("ad_profession_name");
+            add("ad_category_id");
+            add("ad_category_name");
+            add("ad_sku_id");
+            add("ad_sku_code");
+            add("ad_sku_name");
         }};
         }};
     }
     }
 
 

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

@@ -182,6 +182,7 @@ public class RankStrategyBy840 extends RankStrategyBasic {
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setSkuId(dto.getSkuId());
                     adRankItem.setCustomerId(dto.getCustomerId());
                     adRankItem.setCustomerId(dto.getCustomerId());
                     adRankItem.setProfession(dto.getProfession());
                     adRankItem.setProfession(dto.getProfession());
+                    adRankItem.setLandingPageType(dto.getLandingPageType());
                     adRankItem.setRandom(random.nextInt(1000));
                     adRankItem.setRandom(random.nextInt(1000));
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                     if (noApiAdVerIds.contains(dto.getAdVerId())) {
                         adRankItem.getExt().put("isApi", "0");
                         adRankItem.getExt().put("isApi", "0");
@@ -189,6 +190,7 @@ public class RankStrategyBy840 extends RankStrategyBasic {
                         adRankItem.getExt().put("isApi", "1");
                         adRankItem.getExt().put("isApi", "1");
                     }
                     }
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
                     adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctCpaMap", JSONObject.toJSONString(correctCpaMap.get(dto.getAdId())));
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     adRankItem.getExt().put("correctionFactor", correctCpaMap.get(dto.getAdId()).getCorrectionFactor());
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
                     setGuaranteeWeight(map, dto.getAdVerId(), adRankItem.getExt(), isGuaranteedFlow, reqFeature);
@@ -214,6 +216,13 @@ public class RankStrategyBy840 extends RankStrategyBasic {
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
                     cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
+                    cidFeatureMap.put("ad_profession_id", dto.getAdProfessionId() != null ? String.valueOf(dto.getAdProfessionId()) : "");
+                    cidFeatureMap.put("ad_profession_name", dto.getAdProfessionName() != null ? dto.getAdProfessionName() : "");
+                    cidFeatureMap.put("ad_category_name", dto.getAdCategoryName() != null ? dto.getAdCategoryName() : "");
+                    cidFeatureMap.put("ad_category_id", dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : "");
+                    cidFeatureMap.put("ad_sku_id", dto.getAdSkuId() != null ? String.valueOf(dto.getAdSkuId()) : "");
+                    cidFeatureMap.put("ad_sku_code", dto.getAdSkuCode() != null ? dto.getAdSkuCode() : "");
+                    cidFeatureMap.put("ad_sku_name", dto.getAdSkuName() != null ? dto.getAdSkuName() : "");
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     //DNN模型没训练过的cid才不传入广告相关的稀疏特征
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
                     if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
                         cidFeatureMap.put("cid", "");
                         cidFeatureMap.put("cid", "");
@@ -365,6 +374,9 @@ public class RankStrategyBy840 extends RankStrategyBasic {
                 isGuaranteeType = true;
                 isGuaranteeType = true;
             }
             }
 
 
+            // h5 降权
+            double h5Weight = this.getH5SuppressWeight(item);
+
             // 控制曝光权重
             // 控制曝光权重
             Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
             Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
             Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
             Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
@@ -378,13 +390,14 @@ public class RankStrategyBy840 extends RankStrategyBasic {
             double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
             double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
             double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
             double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
             double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
-            double score = expWeight * item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
+            double score = h5Weight * expWeight * item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("guaranteeScoreCoefficient", guaranteeScoreCoefficient);
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpa", item.getCpa());
             item.getScoreMap().put("cpm", item.getCpm());
             item.getScoreMap().put("cpm", item.getCpm());
             item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("bid", bid);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("cpmCoefficient", cpmCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
             item.getScoreMap().put("scoreCoefficient", scoreCoefficient);
+            item.getScoreMap().put("h5", h5Weight);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(userFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
             item.getFeatureMap().putAll(sceneFeatureMap);
 
 
@@ -923,6 +936,13 @@ public class RankStrategyBy840 extends RankStrategyBasic {
             add("user_conver_ad_class");
             add("user_conver_ad_class");
             add("category_name");
             add("category_name");
             add("material_md5");
             add("material_md5");
+            add("ad_profession_id");
+            add("ad_profession_name");
+            add("ad_category_id");
+            add("ad_category_name");
+            add("ad_sku_id");
+            add("ad_sku_code");
+            add("ad_sku_name");
         }};
         }};
     }
     }
 
 

+ 1038 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy843.java

@@ -0,0 +1,1038 @@
+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.helper.AnomalousCidDataHelper;
+import com.tzld.piaoquan.ad.engine.commons.helper.DnnCidDataHelper;
+import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
+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.lang.math.NumberUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.xm.Similarity;
+
+import javax.annotation.PostConstruct;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static com.tzld.piaoquan.ad.engine.commons.math.Const.*;
+
+@Slf4j
+@Component
+public class RankStrategyBy843 extends RankStrategyBasic {
+
+    /**
+     * 空 Map 常量,避免频繁创建空 HashMap
+     */
+    private static final Map<String, String> EMPTY_STRING_MAP = Collections.emptyMap();
+    private static final Map<String, Map<String, String>> EMPTY_NESTED_MAP = Collections.emptyMap();
+
+    private Map<String, double[]> bucketsMap = new HashMap<>();
+
+    private Map<String, Double> bucketsLen = new HashMap<>();
+
+    @Value("${word2vec.exp:694}")
+    private String word2vecExp;
+
+    @ApolloJsonValue("${rank.score.params.843:{}}")
+    private Map<String, String> paramsMap;
+
+    // FIXME(zhoutian): 可能需要独立配置
+    @ApolloJsonValue("${rank.score.weight.680:{}}")
+    private Map<String, Double> weightMap;
+
+    /**
+     * 人群分层&创意的权重
+     * 格式:{layer_creativeId: weight}
+     */
+    @ApolloJsonValue("${rank.score.weight.layer.and.creative:{}}")
+    private Map<String, Double> layerAndCreativeWeightMap;
+
+    @ApolloJsonValue("${rank.score.neg_sample_rate:0.01}")
+    Double negSampleRate;
+
+    Set<String> sparseFeatureSet;
+
+
+    @PostConstruct
+    public void afterInit() {
+        this.readBucketFile();
+        this.initSparseFeatureNames();
+    }
+
+
+    @Override
+    public List<AdRankItem> adItemRank(RankRecommendRequestParam request, ScoreParam scoreParam) {
+        Map<String, Double> weightParam = ObjUtil.nullOrDefault(weightMap, new HashMap<>());
+
+
+        Map<Long, Double> creativeScoreCoefficient = getCreativeScoreCoefficient();
+        Set<String> noApiAdVerIds = getNoApiAdVerIds();
+
+        long ts = System.currentTimeMillis() / 1000;
+
+        String brand = scoreParam.getRequestContext().getMachineinfoBrand();
+        if (StringUtils.isNotEmpty(brand)) {
+            scoreParam.getRequestContext().setMachineinfoBrand(brand + "-n");
+        }
+
+        long start = System.currentTimeMillis();
+        //过滤创意
+        filterRequestAdList(request, scoreParam);
+        // 特征处理
+        // feature1
+        Feature feature = this.getFeature(scoreParam, request);
+        if (feature == null) {
+            log.warn("adItemRank: feature is null, skip processing. request={}", request);
+            return new ArrayList<>();
+        }
+
+        Map<String, Map<String, String>> userFeature = feature.getUserFeature();
+        Map<String, Map<String, String>> videoFeature = feature.getVideoFeature();
+        Map<String, Map<String, Map<String, String>>> allAdVerFeature = feature.getAdVerFeature();
+        Map<String, Map<String, Map<String, String>>> allCidFeature = feature.getCidFeature();
+        Map<String, Map<String, Map<String, String>>> allSkuFeature = feature.getSkuFeature();
+        Map<String, String> reqFeature = this.getReqFeature(scoreParam, request);
+
+        Map<String, String> userFeatureMap = new HashMap<>();
+        Map<String, String> c1Feature = userFeature.getOrDefault("alg_mid_feature_ad_action", EMPTY_STRING_MAP);
+        List<TupleMapEntry<Tuple5>> midActionList = this.handleC1Feature(c1Feature, userFeatureMap);
+
+        Map<String, Double> midTimeDiffMap = this.parseC1FeatureListToTimeDiffMap(midActionList, ts);
+        Map<String, Double> actionStaticMap = this.parseC1FeatureListToActionStaticMap(midActionList);
+
+        Map<String, String> d2Feature = videoFeature.getOrDefault("alg_cid_feature_vid_cf_rank", EMPTY_STRING_MAP);
+        Map<String, String> d3Feature = videoFeature.getOrDefault("alg_vid_feature_basic_info", EMPTY_STRING_MAP);
+
+        Map<String, Map<String, Double>> vidRankMaps = this.parseD2FeatureMap(d2Feature);
+
+        Map<String, String> e1Feature = userFeature.getOrDefault("alg_mid_feature_return_tags", EMPTY_STRING_MAP);
+        Map<String, String> e2Feature = userFeature.getOrDefault("alg_mid_feature_share_tags", EMPTY_STRING_MAP);
+
+        Map<String, String> g1Feature = userFeature.getOrDefault("mid_return_video_cate", EMPTY_STRING_MAP);
+        Map<String, String> g2Feature = userFeature.getOrDefault("mid_share_video_cate", EMPTY_STRING_MAP);
+
+
+        userFeatureMap.put("brand", reqFeature.getOrDefault("brand", ""));
+        userFeatureMap.put("region", reqFeature.getOrDefault("region", ""));
+        userFeatureMap.put("city", reqFeature.getOrDefault("city", ""));
+        userFeatureMap.put("vid", reqFeature.getOrDefault("vid", ""));
+        userFeatureMap.put("apptype", reqFeature.getOrDefault("apptype", ""));
+        userFeatureMap.put("is_first_layer", reqFeature.getOrDefault("is_first_layer", ""));
+        userFeatureMap.put("root_source_scene", reqFeature.getOrDefault("root_source_scene", ""));
+        userFeatureMap.put("root_source_channel", reqFeature.getOrDefault("root_source_channel", ""));
+
+
+        userFeatureMap.put("cate1", d3Feature.get("merge_first_level_cate"));
+        userFeatureMap.put("cate2", d3Feature.get("merge_second_level_cate"));
+        userFeatureMap.put("user_vid_return_tags_2h", e1Feature.getOrDefault("tags_2h", null));
+        userFeatureMap.put("user_vid_return_tags_1d", e1Feature.getOrDefault("tags_1d", null));
+        userFeatureMap.put("user_vid_return_tags_3d", e1Feature.getOrDefault("tags_3d", null));
+        userFeatureMap.put("user_vid_return_tags_7d", e1Feature.getOrDefault("tags_7d", null));
+        userFeatureMap.put("user_vid_return_tags_14d", e1Feature.getOrDefault("tags_14d", null));
+        userFeatureMap.put("title_split", d3Feature.getOrDefault("title_split", null));
+        userFeatureMap.put("user_vid_share_tags_1d", e2Feature.getOrDefault("tags_1d", null));
+        userFeatureMap.put("user_vid_share_tags_14d", e2Feature.getOrDefault("tags_14d", null));
+        userFeatureMap.put("user_vid_return_cate1_14d", g1Feature.getOrDefault("cate1_14d", null));
+        userFeatureMap.put("user_vid_return_cate2_14d", g1Feature.getOrDefault("cate2_14d", null));
+        userFeatureMap.put("user_vid_share_cate1_14d", g2Feature.getOrDefault("cate1_14d", null));
+        userFeatureMap.put("user_vid_share_cate2_14d", g2Feature.getOrDefault("cate2_14d", null));
+
+        Map<String, String> sceneFeatureMap = this.handleSceneFeature(ts);
+        long time1 = System.currentTimeMillis();
+
+        boolean isGuaranteedFlow = getIsGuaranteedFlow(scoreParam);
+        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, isGuaranteedFlow);
+        Map<Long, CorrectCpaParam> correctCpaMap = getCorrectCpaParamMap(request, scoreParam, reqFeature);
+        List<AdRankItem> adRankItems = new ArrayList<>();
+        Random random = new Random();
+        List<Future<AdRankItem>> futures = new ArrayList<>();
+        CountDownLatch cdl1 = new CountDownLatch(request.getAdIdList().size());
+        for (AdPlatformCreativeDTO dto : request.getAdIdList()) {
+            Future<AdRankItem> future = ThreadPoolFactory.feature().submit(() -> {
+                AdRankItem adRankItem = new AdRankItem();
+                try {
+                    adRankItem.setAdId(dto.getCreativeId());
+                    adRankItem.setCreativeCode(dto.getCreativeCode());
+                    adRankItem.setAdVerId(dto.getAdVerId());
+                    adRankItem.setVideoId(request.getVideoId());
+                    adRankItem.setCpa(dto.getCpa());
+                    adRankItem.setId(dto.getAdId());
+                    adRankItem.setCampaignId(dto.getCampaignId());
+                    adRankItem.setCpm(ObjUtil.nullOrDefault(dto.getCpm(), 90).doubleValue());
+                    adRankItem.setSkuId(dto.getSkuId());
+                    adRankItem.setCustomerId(dto.getCustomerId());
+                    adRankItem.setProfession(dto.getProfession());
+                    adRankItem.setLandingPageType(dto.getLandingPageType());
+                    adRankItem.setRandom(random.nextInt(1000));
+                    if (noApiAdVerIds.contains(dto.getAdVerId())) {
+                        adRankItem.getExt().put("isApi", "0");
+                    } else {
+                        adRankItem.getExt().put("isApi", "1");
+                    }
+                    adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
+                    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, reqFeature);
+                    String cidStr = dto.getCreativeId().toString();
+                    Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
+                    Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, EMPTY_NESTED_MAP);
+                    Map<String, String> b1Feature = cidFeature.getOrDefault("alg_cid_feature_basic_info", EMPTY_STRING_MAP);
+
+                    Map<String, Map<String, String>> adVerFeature = allAdVerFeature.getOrDefault(dto.getAdVerId(), EMPTY_NESTED_MAP);
+                    Map<String, Map<String, String>> skuFeature = allSkuFeature.getOrDefault(String.valueOf(dto.getSkuId()), EMPTY_NESTED_MAP);
+                    Map<String, String> d1Feature = cidFeature.getOrDefault("alg_cid_feature_vid_cf", EMPTY_STRING_MAP);
+
+                    this.handleB1Feature(b1Feature, cidFeatureMap, cidStr);
+                    this.handleB2ToB5AndB8ToB9Feature(cidFeature, adVerFeature, cidFeatureMap);
+                    this.handleB6ToB7Feature(cidFeature, cidFeatureMap);
+                    this.handleC1UIFeature(midTimeDiffMap, actionStaticMap, cidFeatureMap, cidStr);
+                    this.handleD1Feature(d1Feature, cidFeatureMap);
+                    this.handleD2Feature(vidRankMaps, cidFeatureMap, cidStr);
+                    this.handleH1AndH2Feature(skuFeature, adVerFeature, cidFeatureMap);
+                    cidFeatureMap.put("cid", dto.getCreativeId() != null ? String.valueOf(dto.getCreativeId()) : "");
+                    cidFeatureMap.put("adid", dto.getAdId() != null ? String.valueOf(dto.getAdId()) : "");
+                    cidFeatureMap.put("adverid", dto.getAdVerId() != null ? dto.getAdVerId() : "");
+                    cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
+                    cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
+                    cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
+                    cidFeatureMap.put("ad_profession_id", dto.getAdProfessionId() != null ? String.valueOf(dto.getAdProfessionId()) : "");
+                    cidFeatureMap.put("ad_profession_name", dto.getAdProfessionName() != null ? dto.getAdProfessionName() : "");
+                    cidFeatureMap.put("ad_category_name", dto.getAdCategoryName() != null ? dto.getAdCategoryName() : "");
+                    cidFeatureMap.put("ad_category_id", dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : "");
+                    cidFeatureMap.put("ad_sku_id", dto.getAdSkuId() != null ? String.valueOf(dto.getAdSkuId()) : "");
+                    cidFeatureMap.put("ad_sku_code", dto.getAdSkuCode() != null ? dto.getAdSkuCode() : "");
+                    cidFeatureMap.put("ad_sku_name", dto.getAdSkuName() != null ? dto.getAdSkuName() : "");
+                    //DNN模型没训练过的cid才不传入广告相关的稀疏特征
+                    if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
+                        cidFeatureMap.put("cid", "");
+                        cidFeatureMap.put("adid", "");
+                        cidFeatureMap.put("adverid", "");
+                    }
+                    return adRankItem;
+                } finally {
+                    cdl1.countDown();
+                }
+            });
+            futures.add(future);
+        }
+        try {
+            cdl1.await(300, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
+        }
+        for (Future<AdRankItem> future : futures) {
+            try {
+                if (future.isDone()) {
+                    adRankItems.add(future.get());
+                }
+            } catch (Exception e) {
+                log.error("Feature handle error", e);
+            }
+        }
+
+        long time2 = System.currentTimeMillis();
+        // feature3
+        // 请求级别的 tag 分词缓存,所有广告共享(同一用户的 tags 相同)
+        Map<String, List<String>> tagWordsCache = new ConcurrentHashMap<>();
+        CountDownLatch cdl2 = new CountDownLatch(adRankItems.size() * 2);
+        for (AdRankItem item : adRankItems) {
+            String cidStr = String.valueOf(item.getAdId());
+            Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, EMPTY_NESTED_MAP);
+            Map<String, String> b1Feature = cidFeature.getOrDefault("alg_cid_feature_basic_info", EMPTY_STRING_MAP);
+            String title = b1Feature.getOrDefault("cidtitle", "");
+            ThreadPoolFactory.defaultPool().submit(() -> {
+                try {
+                    this.handleE1AndE2Feature(e1Feature, e2Feature, title, item.getFeatureMap(), scoreParam, tagWordsCache);
+                } finally {
+                    cdl2.countDown();
+                }
+            });
+            ThreadPoolFactory.defaultPool().submit(() -> {
+                try {
+                    this.handleD3AndB1Feature(d3Feature, title, item.getFeatureMap(), scoreParam);
+                } finally {
+                    cdl2.countDown();
+                }
+            });
+        }
+        try {
+            cdl2.await(150, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
+        }
+
+        long time3 = System.currentTimeMillis();
+        // 分桶
+        userFeatureMap = this.featureBucket(userFeatureMap);
+        CountDownLatch cdl4 = new CountDownLatch(adRankItems.size());
+        for (AdRankItem adRankItem : adRankItems) {
+            ThreadPoolFactory.feature().submit(() -> {
+                try {
+                    Map<String, String> featureMap = adRankItem.getFeatureMap();
+                    adRankItem.setFeatureMap(this.featureBucket(featureMap));
+                } finally {
+                    cdl4.countDown();
+                }
+            });
+        }
+        try {
+            cdl4.await(100, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
+        }
+        long time4 = System.currentTimeMillis();
+        // 打分排序
+        // getScorerPipeline
+
+        if (CollectionUtils.isEmpty(adRankItems)) {
+            log.error("adRankItems is empty");
+        }
+        List<AdRankItem> result = ScorerUtils.getScorerPipeline(ScorerUtils.PAI_SCORE_CONF_20250804).scoring(sceneFeatureMap, userFeatureMap, adRankItems);
+        if (CollectionUtils.isEmpty(result)) {
+            log.error("scoring result is empty");
+        }
+        long time5 = System.currentTimeMillis();
+        int viewLimit = NumberUtils.toInt(paramsMap.getOrDefault("viewLimit", "3000"));
+        double h5PageFactor = NumberUtils.toDouble(paramsMap.getOrDefault("h5PageFactor", "2"));
+        double h5PagePlus = NumberUtils.toDouble(paramsMap.getOrDefault("h5PagePlus", "300"));
+        double selfPageFactor = NumberUtils.toDouble(paramsMap.getOrDefault("selfPageFactor", "1"));
+        double selfPagePlus = NumberUtils.toDouble(paramsMap.getOrDefault("selfPagePlus", "0"));
+        String h5ViewKey = paramsMap.getOrDefault("h5ViewKey", "ad_view_1d");
+        String h5ConverKey = paramsMap.getOrDefault("h5ConverKey", "ad_conversion_1d");
+        // calibrate score for negative sampling or cold start
+        for (AdRankItem item : result) {
+            double originalScore = item.getLrScore();
+            double calibratedScore = originalScore / (originalScore + (1 - originalScore) / negSampleRate);
+            // 该创意尚未在模型中训练,打分不可靠
+            Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
+            Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
+            double view3Day = Double.parseDouble(b3Feature.getOrDefault("ad_view_3d", "0"));
+            boolean h5Page = this.landingH5Page(item);
+            if ((CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(item.getAdId()))
+                    || view3Day <= viewLimit || h5Page) {
+                if (h5Page) {
+                    double view14D = Double.parseDouble(b3Feature.getOrDefault("ad_view_14d", "0"));
+                    double conver14D = Double.parseDouble(b3Feature.getOrDefault("ad_conversion_14d", "0"));
+                    double smoothCxr14D = NumUtil.divSmoothV1(conver14D, view14D * h5PageFactor + h5PagePlus, 1.64);
+                    double view1D = Double.parseDouble(b3Feature.getOrDefault(h5ViewKey, "0"));
+                    double conver1D = Double.parseDouble(b3Feature.getOrDefault(h5ConverKey, "0"));
+                    double smoothCxr1D = NumUtil.divSmoothV1(conver1D, view1D * h5PageFactor + h5PagePlus, 1.64);
+                    // 模型打分和统计计算取打分更低的
+                    item.getScoreMap().put("cvcvrItemValue", 1.0);
+                    if (smoothCxr14D < calibratedScore) {
+                        calibratedScore = smoothCxr14D;
+                        item.getScoreMap().put("cvcvrItemValue", 2.0);
+                    }
+                    if (smoothCxr1D < calibratedScore) {
+                        calibratedScore = smoothCxr1D;
+                        item.getScoreMap().put("cvcvrItemValue", 3.0);
+                    }
+                } else {
+                    double view14D = Double.parseDouble(b3Feature.getOrDefault("ad_view_14d", "0"));
+                    double conver14D = Double.parseDouble(b3Feature.getOrDefault("ad_conversion_14d", "0"));
+                    double smoothCxr14D = NumUtil.divSmoothV1(conver14D, view14D * selfPageFactor + selfPagePlus, 1.64);
+                    //模型打分和统计计算取打分更低的
+                    item.getScoreMap().put("cvcvrItemValue", 1.0);
+                    if (smoothCxr14D <= calibratedScore) {
+                        calibratedScore = smoothCxr14D;
+                        item.getScoreMap().put("cvcvrItemValue", 2.0);
+                    }
+                }
+            }
+            item.setLrScore(calibratedScore);
+            item.getScoreMap().put("originCtcvrScore", originalScore);
+            item.getScoreMap().put("modelCtcvrScore", calibratedScore);
+            item.getScoreMap().put("ctcvrScore", calibratedScore);
+        }
+
+        String calibModelName = paramsMap.getOrDefault("calibModelName", "dnnV3");
+        calculateCtcvrScore(result, request, scoreParam, calibModelName, reqFeature);
+        if (CollectionUtils.isEmpty(result)) {
+            log.error("calculateCtcvrScore result is empty");
+        }
+        // loop
+        double cpmCoefficient = weightParam.getOrDefault("cpmCoefficient", 0.9);
+        boolean isGuaranteeType = false;
+        // 查询人群分层信息
+        String peopleLayer = Optional.of(reqFeature)
+                .map(f -> f.get("layer"))
+                .map(s -> s.replace("-炸", ""))
+                .orElse(null);
+
+        // 控制曝光参数
+        String expOldKey = paramsMap.getOrDefault("expOldKey", "ad_view_yesterday");
+        double expOldThreshold = NumberUtils.toDouble(paramsMap.getOrDefault("expOldThreshold", "1000"));
+        String expNewKey = paramsMap.getOrDefault("expNewKey", "ad_view_today");
+        double expNewThreshold = NumberUtils.toDouble(paramsMap.getOrDefault("expNewThreshold", "3000"));
+        double expLowerWeight = NumberUtils.toDouble(paramsMap.getOrDefault("expLowerWeight", "0.2"));
+        double expUpperWeight = NumberUtils.toDouble(paramsMap.getOrDefault("expUpperWeight", "1.0"));
+        double expScale = NumberUtils.toDouble(paramsMap.getOrDefault("expScale", "10.0"));
+        double anomWeight = NumberUtils.toDouble(paramsMap.getOrDefault("anomWeight", "1.0"));
+        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 anomW = 1.0;
+            boolean h5Page = this.landingH5Page(item);
+            if (h5Page) {
+                if (AnomalousCidDataHelper.contains(item.getAdId())) {
+                    anomW = anomWeight;
+                }
+            }
+
+            // 控制曝光权重
+            Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
+            Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
+            double expWeight = getExpWeight(b3Feature,
+                    expOldKey, expOldThreshold,
+                    expNewKey, expNewThreshold,
+                    expLowerWeight, expUpperWeight, expScale);
+
+            String layerAndCreativeWeightMapKey = getLayerAndCreativeWeightMapKey(peopleLayer, String.valueOf(item.getAdId()));
+            // 人群分层&创意的权重
+            double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
+            double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
+            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
+            double score = anomW * expWeight * item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
+            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.getScoreMap().put("anom", anomW);
+            item.getFeatureMap().putAll(userFeatureMap);
+            item.getFeatureMap().putAll(sceneFeatureMap);
+
+            // 没有转化回传的广告主,使用后台配置的CPM
+            if (noApiAdVerIds.contains(item.getAdVerId())) {
+                score = item.getCpm() * cpmCoefficient / 1000;
+            }
+            item.setScore(score);
+        }
+
+
+        result.sort(ComparatorUtil.equalsRandomComparator());
+
+        String logModelName = paramsMap.getOrDefault("logModelName", "dnnV3");
+        if (CollectionUtils.isNotEmpty(result)) {
+            AdRankItem top1Item = result.get(0);
+            List<String> participateCompetitionType = new ArrayList<>();
+            participateCompetitionType.add("engine");
+            top1Item.getExt().put("isGuaranteeType", isGuaranteeType);
+            if (isGuaranteeType) {
+                participateCompetitionType.add("guarantee");
+            }
+            top1Item.getExt().put("participateCompetitionType", StringUtils.join(participateCompetitionType, ","));
+            Double modelCtcvrScore = top1Item.getScoreMap().get("modelCtcvrScore");
+            Double ctcvrScore = top1Item.getScoreMap().get("ctcvrScore");
+            if (scoreParam.getExpCodeSet().contains(checkoutEcpmExp)) {
+                top1Item.getExt().put("ecpm", ctcvrScore * top1Item.getCpa() * 1000);
+                String filterEcpmValue = paramsMap.getOrDefault("filterEcpm", filterEcpm);
+                top1Item.getExt().put("filterEcpm", filterEcpmValue);
+            } else {
+                top1Item.getExt().put("ecpm", modelCtcvrScore * top1Item.getCpa() * 1000);
+            }
+            putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
+            top1Item.getExt().put("model", logModelName);
+            String coefficientRate = paramsMap.getOrDefault("coefficientRate", "1");
+            top1Item.getExt().put("coefficientRate", coefficientRate);
+        }
+        long time6 = System.currentTimeMillis();
+        log.info("cost={}, getFeature={}, handleFeature={},  similar={}, bucketFeature={}, getScorerPipeline={}, " +
+                        "other={}, adIdSize={}, adRankItemsSize={}",
+                time6 - start, time1 - start, time2 - time1, time3 - time2, time4 - time3,
+                time5 - time4, time6 - time5, request.getAdIdList().size(), adRankItems.size());
+
+        return result;
+    }
+
+    /**
+     * 获取人群分层和创意的权重
+     *
+     * @param key
+     * @return
+     */
+    private Double getLayerAndCreativeWeight(String key) {
+        if (StringUtils.isBlank(key)) {
+            return 1d;
+        }
+        return layerAndCreativeWeightMap.getOrDefault(key, 1d);
+    }
+
+    /**
+     * 获取人群分层和创意的权重key
+     *
+     * @param layer
+     * @param creativeId
+     * @return
+     */
+    private String getLayerAndCreativeWeightMapKey(String layer, String creativeId) {
+        if (StringUtils.isBlank(layer) || StringUtils.isBlank(creativeId)) {
+            return null;
+        }
+        return layer + "_" + creativeId;
+    }
+
+
+    private void handleB1Feature(Map<String, String> b1Feature, Map<String, String> cidFeatureMap, String cid) {
+        cidFeatureMap.put("cid_" + cid, "0.1");
+        // if (StringUtils.isNotBlank(b1Feature.get("adid"))) {
+        //     String adId = b1Feature.get("adid");
+        //     cidFeatureMap.put("adid_" + adId, idDefaultValue);
+        // }
+        if (StringUtils.isNotBlank(b1Feature.get("adverid"))) {
+            String adVerId = b1Feature.get("adverid");
+            cidFeatureMap.put("adverid_" + adVerId, "0.1");
+        }
+        // if (StringUtils.isNotBlank(b1Feature.get("targeting_conversion"))) {
+        //     String targetingConversion = b1Feature.get("targeting_conversion");
+        //     cidFeatureMap.put("targeting_conversion_" + targetingConversion, idDefaultValue);
+        // }
+        if (StringUtils.isNotBlank(b1Feature.get("cpa"))) {
+            String cpa = b1Feature.get("cpa");
+            cidFeatureMap.put("cpa", cpa);
+        }
+    }
+
+    private void handleB2ToB5AndB8ToB9Feature(Map<String, Map<String, String>> c1Feature, Map<String, Map<String, String>> adVerFeature, Map<String, String> cidFeatureMap) {
+        Map<String, String> b2Feature = adVerFeature.getOrDefault("alg_cid_feature_adver_action", EMPTY_STRING_MAP);
+        Map<String, String> b3Feature = c1Feature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
+        Map<String, String> b4Feature = c1Feature.getOrDefault("alg_cid_feature_region_action", EMPTY_STRING_MAP);
+        Map<String, String> b5Feature = c1Feature.getOrDefault("alg_cid_feature_app_action", EMPTY_STRING_MAP);
+        Map<String, String> b8Feature = c1Feature.getOrDefault("alg_cid_feature_brand_action", EMPTY_STRING_MAP);
+        Map<String, String> b9Feature = c1Feature.getOrDefault("alg_cid_feature_weChatVersion_action", EMPTY_STRING_MAP);
+
+        List<String> timeList = Arrays.asList("1h", "2h", "3h", "6h", "12h", "1d", "3d", "7d", "yesterday", "today");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(b2Feature, "b2"),
+                new Tuple2<>(b3Feature, "b3"),
+                new Tuple2<>(b4Feature, "b4"),
+                new Tuple2<>(b5Feature, "b5"),
+                new Tuple2<>(b8Feature, "b8"),
+                new Tuple2<>(b9Feature, "b9")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                double view = Double.parseDouble(feature.getOrDefault("ad_view_" + time, "0"));
+                double click = Double.parseDouble(feature.getOrDefault("ad_click_" + time, "0"));
+                double conver = Double.parseDouble(feature.getOrDefault("ad_conversion_" + time, "0"));
+                double income = Double.parseDouble(feature.getOrDefault("ad_income_" + time, "0"));
+                double cpc = NumUtil.div(income, click);
+                double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
+                double ctcvr = NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR);
+                double ecpm = ctr * cpc * 1000;
+                cidFeatureMap.put(prefix + "_" + time + "_ctr", String.valueOf(ctr));
+                cidFeatureMap.put(prefix + "_" + time + "_ctcvr", String.valueOf(ctcvr));
+                cidFeatureMap.put(prefix + "_" + time + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver", String.valueOf(conver));
+                cidFeatureMap.put(prefix + "_" + time + "_ecpm", String.valueOf(ecpm));
+
+                cidFeatureMap.put(prefix + "_" + time + "_click", String.valueOf(click));
+                cidFeatureMap.put(prefix + "_" + time + "_conver*log(view)", String.valueOf(conver * NumUtil.log(view)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver*ctcvr", String.valueOf(conver * ctcvr));
+            }
+        }
+
+    }
+
+    private void handleB6ToB7Feature(Map<String, Map<String, String>> c1Feature, Map<String, String> cidFeatureMap) {
+        Map<String, String> b6Feature = c1Feature.getOrDefault("alg_cid_feature_week_action", EMPTY_STRING_MAP);
+        Map<String, String> b7Feature = c1Feature.getOrDefault("alg_cid_feature_hour_action", EMPTY_STRING_MAP);
+
+        List<String> timeList = Arrays.asList("7d", "14d");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(b6Feature, "b6"),
+                new Tuple2<>(b7Feature, "b7")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                double view = Double.parseDouble(feature.getOrDefault("ad_view_" + time, "0"));
+                double click = Double.parseDouble(feature.getOrDefault("ad_click_" + time, "0"));
+                double conver = Double.parseDouble(feature.getOrDefault("ad_conversion_" + time, "0"));
+                double income = Double.parseDouble(feature.getOrDefault("ad_income_" + time, "0"));
+                double cpc = NumUtil.div(income, click);
+                double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
+                double ctcvr = NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR);
+                double ecpm = ctr * cpc * 1000;
+                cidFeatureMap.put(prefix + "_" + time + "_ctr", String.valueOf(ctr));
+                cidFeatureMap.put(prefix + "_" + time + "_ctcvr", String.valueOf(ctcvr));
+                cidFeatureMap.put(prefix + "_" + time + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver", String.valueOf(conver));
+                cidFeatureMap.put(prefix + "_" + time + "_ecpm", String.valueOf(ecpm));
+
+                cidFeatureMap.put(prefix + "_" + time + "_click", String.valueOf(click));
+                cidFeatureMap.put(prefix + "_" + time + "_conver*log(view)", String.valueOf(conver * NumUtil.log(view)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver*ctcvr", String.valueOf(conver * ctcvr));
+            }
+        }
+
+    }
+
+    private List<TupleMapEntry<Tuple5>> handleC1Feature(Map<String, String> c1Feature, Map<String, String> featureMap) {
+
+        //用户近1年内是否有转化
+        if (c1Feature.containsKey("user_has_conver_1y") && c1Feature.get("user_has_conver_1y") != null) {
+            featureMap.put("user_has_conver_1y", c1Feature.get("user_has_conver_1y"));
+        }
+        //用户历史转化过品类
+        if (c1Feature.containsKey("user_conver_ad_class") && c1Feature.get("user_conver_ad_class") != null) {
+            featureMap.put("user_conver_ad_class", c1Feature.get("user_conver_ad_class"));
+        }
+
+        // 用户特征
+        List<TupleMapEntry<Tuple5>> midActionList = new ArrayList<>();
+        if (c1Feature.containsKey("action")) {
+            String action = c1Feature.get("action");
+            midActionList = Arrays.stream(action.split(","))
+                    .map(r -> {
+                        String[] rList = r.split(":");
+                        Tuple5 tuple5 = new Tuple5(rList[1], rList[2], rList[3], rList[4], rList[5]);
+                        return new TupleMapEntry<>(rList[0], tuple5);
+                    })
+                    // TODO 倒排
+                    .sorted((a, b) -> Integer.compare(Integer.parseInt(b.value.f1), Integer.parseInt(a.value.f1)))
+                    .collect(Collectors.toList());
+        }
+
+        double viewAll = midActionList.size();
+        double clickAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f2)).sum();
+        double converAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f3)).sum();
+        double incomeAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f4)).sum();
+        featureMap.put("viewAll", String.valueOf(viewAll));
+        featureMap.put("clickAll", String.valueOf(clickAll));
+        featureMap.put("converAll", String.valueOf(converAll));
+        featureMap.put("incomeAll", String.valueOf(incomeAll));
+        featureMap.put("ctr_all", String.valueOf(NumUtil.div(clickAll, viewAll)));
+        featureMap.put("ctcvr_all", String.valueOf(NumUtil.div(converAll, viewAll)));
+        featureMap.put("cvr_all", String.valueOf(NumUtil.div(clickAll, converAll)));
+        featureMap.put("ecpm_all", String.valueOf(NumUtil.div(incomeAll * 1000, viewAll)));
+        if (CollectionUtils.isNotEmpty(midActionList)) {
+            List<String> cidClickList = new ArrayList<>();
+            List<String> cidConverList = new ArrayList<>();
+            for (TupleMapEntry<Tuple5> tupleMapEntry : midActionList) {
+                String cid = tupleMapEntry.key;
+                String click = tupleMapEntry.value.f2;
+                String conver = tupleMapEntry.value.f3;
+                if (Objects.equals(click, "1")) {
+                    cidClickList.add(cid);
+                }
+                if (Objects.equals(conver, "1")) {
+                    cidConverList.add(cid);
+                }
+            }
+            featureMap.put("user_cid_click_list", String.join(",", cidClickList));
+            featureMap.put("user_cid_conver_list", String.join(",", cidConverList));
+        }
+        return midActionList;
+    }
+
+    private void handleC1UIFeature(Map<String, Double> midTimeDiffMap, Map<String, Double> midActionStatic, Map<String, String> featureMap, String cid) {
+        if (midTimeDiffMap.containsKey("timediff_view_" + cid)) {
+            featureMap.put("timediff_view", String.valueOf(midTimeDiffMap.getOrDefault("timediff_view_" + cid, 0.0)));
+        }
+        if (midTimeDiffMap.containsKey("timediff_click_" + cid)) {
+            featureMap.put("timediff_click", String.valueOf(midTimeDiffMap.getOrDefault("timediff_click_" + cid, 0.0)));
+        }
+        if (midTimeDiffMap.containsKey("timediff_conver_" + cid)) {
+            featureMap.put("timediff_conver", String.valueOf(midTimeDiffMap.getOrDefault("timediff_conver_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid)) {
+            featureMap.put("actionstatic_view", String.valueOf(midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            featureMap.put("actionstatic_click", String.valueOf(midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_conver_" + cid)) {
+            featureMap.put("actionstatic_conver", String.valueOf(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_income_" + cid)) {
+            featureMap.put("actionstatic_income", String.valueOf(midActionStatic.getOrDefault("actionstatic_income_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid) && midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            double ctr = NumUtil.div(
+                    midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0),
+                    midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)
+            );
+            featureMap.put("actionstatic_ctr", String.valueOf(ctr));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid) && midActionStatic.containsKey("actionstatic_conver_" + cid)) {
+            double ctcvr = NumUtil.div(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0), midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0));
+            featureMap.put("actionstatic_ctcvr", String.valueOf(ctcvr));
+        }
+        if (midActionStatic.containsKey("actionstatic_conver_" + cid) && midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            double cvr = NumUtil.div(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0), midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0));
+            featureMap.put("actionstatic_cvr", String.valueOf(cvr));
+        }
+    }
+
+    private void handleD1Feature(Map<String, String> d1Feature, Map<String, String> featureMap) {
+        for (String prefix : Arrays.asList("3h", "6h", "12h", "1d", "3d", "7d")) {
+            double view = Double.parseDouble(d1Feature.getOrDefault("ad_view_" + prefix, "0"));
+            double click = Double.parseDouble(d1Feature.getOrDefault("ad_click_" + prefix, "0"));
+            double conver = Double.parseDouble(d1Feature.getOrDefault("ad_conversion_" + prefix, "0"));
+            double income = Double.parseDouble(d1Feature.getOrDefault("ad_income_" + prefix, "0"));
+            double cpc = NumUtil.div(income, click);
+            double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
+            featureMap.put("d1_feature_" + prefix + "_ctr", String.valueOf(ctr));
+            featureMap.put("d1_feature_" + prefix + "_ctcvr", String.valueOf(NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR)));
+            featureMap.put("d1_feature_" + prefix + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
+            featureMap.put("d1_feature_" + prefix + "_conver", String.valueOf(conver));
+            featureMap.put("d1_feature_" + prefix + "_ecpm", String.valueOf(ctr * cpc * 1000));
+        }
+    }
+
+    private void handleD2Feature(Map<String, Map<String, Double>> vidRankMaps, Map<String, String> featureMap, String cid) {
+        if (MapUtils.isEmpty(vidRankMaps)) {
+            return;
+        }
+
+        List<String> prefixes1 = Arrays.asList("ctr", "ctcvr", "ecpm");
+        // List<String> prefixes1 = Arrays.asList("ctr", "ctcvr");
+        List<String> prefixes2 = Arrays.asList("1d", "3d", "7d", "14d");
+
+        for (String prefix1 : prefixes1) {
+            for (String prefix2 : prefixes2) {
+                String combinedKey = prefix1 + "_" + prefix2;
+                if (vidRankMaps.containsKey(combinedKey)) {
+                    Double rank = vidRankMaps.get(combinedKey).getOrDefault(cid, 0.0);
+                    if (rank >= 1.0) {
+                        featureMap.put("vid_rank_" + combinedKey, String.valueOf(NumUtil.div(1, rank)));
+                    }
+                }
+            }
+        }
+    }
+
+    private void handleH1AndH2Feature(Map<String, Map<String, String>> skuFeature,
+                                      Map<String, Map<String, String>> adVerFeature,
+                                      Map<String, String> cidFeatureMap) {
+        Map<String, String> h1Feature = adVerFeature.getOrDefault("alg_mid_feature_adver_action", EMPTY_STRING_MAP);
+        Map<String, String> h2Feature = skuFeature.getOrDefault("alg_mid_feature_sku_action", EMPTY_STRING_MAP);
+        List<String> timeList = Arrays.asList("3d", "7d", "30d");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(h1Feature, "adverid"),
+                new Tuple2<>(h2Feature, "skuid")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                String timeValue = feature.getOrDefault(time, "");
+                if (StringUtils.isNotEmpty(timeValue)) {
+                    String[] split = timeValue.split(",");
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "view" + "_" + time, split[0]);
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "click" + "_" + time, split[1]);
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "conver" + "_" + time, split[2]);
+                }
+            }
+        }
+
+
+    }
+
+    private void handleD3AndB1Feature(Map<String, String> d3Feature, String cTitle, Map<String, String> featureMap,
+                                      ScoreParam scoreParam) {
+        if (MapUtils.isEmpty(d3Feature) || !d3Feature.containsKey("title") || StringUtils.isEmpty(cTitle)) {
+            return;
+        }
+        String vTitle = d3Feature.get("title");
+        double score;
+        if (scoreParam.getExpCodeSet().contains(word2vecExp)) {
+            score = SimilarityUtils.word2VecSimilarity(cTitle, vTitle);
+        } else {
+            score = Similarity.conceptSimilarity(cTitle, vTitle);
+        }
+        featureMap.put("ctitle_vtitle_similarity", String.valueOf(score));
+    }
+
+    private void handleE1AndE2Feature(Map<String, String> e1Feature, Map<String, String> e2Feature, String title,
+                                      Map<String, String> featureMap, ScoreParam scoreParam,
+                                      Map<String, List<String>> tagWordsCache) {
+        if (StringUtils.isEmpty(title)) {
+            return;
+        }
+
+        // 预先分词 title,在整个方法中复用,避免重复分词
+        List<String> titleWords = null;
+        if (scoreParam.getExpCodeSet().contains(word2vecExp)) {
+            titleWords = SimilarityUtils.segment(title);
+        }
+
+        List<Tuple2<Map<String, String>, String>> tuple2List = Arrays.asList(new Tuple2<>(e1Feature, "e1"), new Tuple2<>(e2Feature, "e2"));
+
+        List<String> tagsFieldList = Arrays.asList("tags_3d", "tags_7d", "tags_14d");
+        for (Tuple2<Map<String, String>, String> tuple2 : tuple2List) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            if (MapUtils.isEmpty(feature)) {
+                continue;
+            }
+
+            for (String tagsField : tagsFieldList) {
+                if (StringUtils.isNotEmpty(feature.get(tagsField))) {
+                    String tags = feature.get(tagsField);
+                    Double[] doubles;
+                    if (scoreParam.getExpCodeSet().contains(word2vecExp)) {
+                        // 使用缓存的 title 分词结果和请求级别的 tag 分词缓存
+                        doubles = ExtractorUtils.funcC34567ForTagsNewWithCache(tags, title, titleWords, tagWordsCache);
+                    } else {
+                        doubles = ExtractorUtils.funcC34567ForTags(tags, title);
+                    }
+                    featureMap.put(prefix + "_" + tagsField + "_matchnum", String.valueOf(doubles[0]));
+                    featureMap.put(prefix + "_" + tagsField + "_maxscore", String.valueOf(doubles[1]));
+                    featureMap.put(prefix + "_" + tagsField + "_avgscore", String.valueOf(doubles[2]));
+                }
+            }
+        }
+    }
+
+    private Map<String, Double> parseC1FeatureListToTimeDiffMap(List<TupleMapEntry<Tuple5>> midActionList, long ts) {
+        Map<String, Double> midTimeDiffMap = new HashMap<>();
+        for (TupleMapEntry<Tuple5> entry : midActionList) {
+            String cid = entry.key;
+            double tsHistory = Double.parseDouble(entry.value.f1);
+            double click = Double.parseDouble(entry.value.f2);
+            double conver = Double.parseDouble(entry.value.f3);
+            double d = (ts - tsHistory) / 3600 / 24;
+            if (!midTimeDiffMap.containsKey("timediff_view_" + cid)) {
+                midTimeDiffMap.put("timediff_view_" + cid, NumUtil.div(1, d));
+            }
+            if (!midTimeDiffMap.containsKey("timediff_click_" + cid) && click > 0) {
+                midTimeDiffMap.put("timediff_click_" + cid, NumUtil.div(1, d));
+            }
+            if (!midTimeDiffMap.containsKey("timediff_conver_" + cid) && conver > 0) {
+                midTimeDiffMap.put("timediff_conver_" + cid, NumUtil.div(1, d));
+            }
+        }
+        return midTimeDiffMap;
+    }
+
+    private Map<String, Double> parseC1FeatureListToActionStaticMap(List<TupleMapEntry<Tuple5>> midActionList) {
+        Map<String, Double> midActionStaticsMap = new HashMap<>();
+        for (TupleMapEntry<Tuple5> entry : midActionList) {
+            String cid = entry.key;
+            double click = Double.parseDouble(entry.value.f2);
+            double conver = Double.parseDouble(entry.value.f3);
+            double income = Double.parseDouble(entry.value.f4);
+
+            Double viewSum = midActionStaticsMap.getOrDefault("actionstatic_view_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_view_" + cid, 1 + viewSum);
+
+            Double clickSum = midActionStaticsMap.getOrDefault("actionstatic_click_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_click_" + cid, clickSum + click);
+
+            Double converSum = midActionStaticsMap.getOrDefault("actionstatic_conver_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_conver_" + cid, converSum + conver);
+
+            Double incomSum = midActionStaticsMap.getOrDefault("actionstatic_income_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_income_" + cid, incomSum + income);
+        }
+
+        return midActionStaticsMap;
+    }
+
+    private Map<String, Map<String, Double>> parseD2FeatureMap(Map<String, String> d2Feature) {
+        Map<String, Map<String, Double>> vidRankMaps = new HashMap<>();
+        for (Map.Entry<String, String> entry : d2Feature.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            Map<String, Double> valueMap = Arrays.stream(value.split(",")).map(r -> r.split(":")).collect(Collectors.toMap(rList -> rList[0], rList -> Double.parseDouble(rList[2])));
+            vidRankMaps.put(key, valueMap);
+        }
+        return vidRankMaps;
+    }
+
+    private void readBucketFile() {
+        if (MapUtils.isNotEmpty(bucketsMap)) {
+            return;
+        }
+        synchronized (this) {
+            String bucketFile = "20250217_ad_bucket_688.txt";
+            InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream(bucketFile);
+            if (resourceStream != null) {
+                try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
+                    Map<String, double[]> bucketsMap = new HashMap<>();
+                    Map<String, Double> bucketsLen = new HashMap<>();
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        // 替换空格和换行符,过滤空行
+                        line = line.replace(" ", "").replaceAll("\n", "");
+                        if (!line.isEmpty()) {
+                            String[] rList = line.split("\t");
+                            if (rList.length == 3) {
+                                String key = rList[0];
+                                double value1 = Double.parseDouble(rList[1]);
+                                bucketsLen.put(key, value1);
+                                double[] value2 = Arrays.stream(rList[2].split(",")).mapToDouble(Double::valueOf).toArray();
+                                bucketsMap.put(key, value2);
+                            }
+                        }
+                    }
+                    this.bucketsMap = bucketsMap;
+                    this.bucketsLen = bucketsLen;
+                } catch (IOException e) {
+                    log.error("something is wrong in parse bucket file: ", e);
+                }
+                log.info("load bucket file success: {}", bucketFile);
+            } else {
+                log.error("no bucket file");
+            }
+        }
+    }
+
+    private void initSparseFeatureNames() {
+        this.sparseFeatureSet = new HashSet<String>() {{
+            add("brand");
+            add("region");
+            add("city");
+            add("vid");
+            add("cate1");
+            add("cate2");
+            add("cid");
+            add("adid");
+            add("adverid");
+            add("user_cid_click_list");
+            add("user_cid_conver_list");
+            add("user_vid_return_tags_2h");
+            add("user_vid_return_tags_1d");
+            add("user_vid_return_tags_3d");
+            add("user_vid_return_tags_7d");
+            add("user_vid_return_tags_14d");
+            add("apptype");
+            add("hour");
+            add("hour_quarter");
+            add("root_source_scene");
+            add("root_source_channel");
+            add("is_first_layer");
+            add("title_split");
+            add("profession");
+            add("user_vid_share_tags_1d");
+            add("user_vid_share_tags_14d");
+            add("user_vid_return_cate1_14d");
+            add("user_vid_return_cate2_14d");
+            add("user_vid_share_cate1_14d");
+            add("user_vid_share_cate2_14d");
+            add("user_has_conver_1y");
+            add("user_adverid_view_3d");
+            add("user_adverid_click_3d");
+            add("user_adverid_conver_3d");
+            add("user_adverid_view_7d");
+            add("user_adverid_click_7d");
+            add("user_adverid_conver_7d");
+            add("user_adverid_view_30d");
+            add("user_adverid_click_30d");
+            add("user_adverid_conver_30d");
+            add("user_skuid_view_3d");
+            add("user_skuid_click_3d");
+            add("user_skuid_conver_3d");
+            add("user_skuid_view_7d");
+            add("user_skuid_click_7d");
+            add("user_skuid_conver_7d");
+            add("user_skuid_view_30d");
+            add("user_skuid_click_30d");
+            add("user_skuid_conver_30d");
+            add("user_conver_ad_class");
+            add("category_name");
+            add("material_md5");
+            add("ad_profession_id");
+            add("ad_profession_name");
+            add("ad_category_id");
+            add("ad_category_name");
+            add("ad_sku_id");
+            add("ad_sku_code");
+            add("ad_sku_name");
+        }};
+    }
+
+    private Map<String, String> featureBucket(Map<String, String> featureMap) {
+        // 使用 HashMap 替代 ConcurrentHashMap,分桶操作是单线程的
+        Map<String, String> newFeatureMap = new HashMap<>(featureMap.size());
+        for (Map.Entry<String, String> entry : featureMap.entrySet()) {
+            try {
+                String name = entry.getKey();
+                if (this.sparseFeatureSet.contains(name)) {
+                    if (entry.getValue() != null) {
+                        newFeatureMap.put(name, entry.getValue());
+                    }
+                    continue;
+                }
+                double score = Double.parseDouble(entry.getValue());
+                // 注意:0值、不在分桶文件中的特征,会被过滤掉。
+                if (score > 1E-8) {
+                    if (this.bucketsMap.containsKey(name) && this.bucketsLen.containsKey(name)) {
+                        double[] buckets = this.bucketsMap.get(name);
+                        double bucketNum = this.bucketsLen.get(name);
+                        Double scoreNew = 1.0 / bucketNum * (ExtractorUtils.findInsertPosition(buckets, score) + 1.0);
+                        newFeatureMap.put(name, String.valueOf(scoreNew));
+                    } else {
+                        newFeatureMap.put(name, String.valueOf(score));
+                    }
+                }
+            } catch (Exception e) {
+                log.error("featureBucket error: ", e);
+            }
+        }
+        return newFeatureMap;
+    }
+
+    private double getExpWeight(Map<String, String> featureMap,
+                                String expOldKey, double expOldThreshold,
+                                String expNewKey, double expNewThreshold,
+                                double expLowerWeight, double expUpperWeight, double expScale) {
+        try {
+            if (null != featureMap) {
+                double oldView = Double.parseDouble(featureMap.getOrDefault(expOldKey, "0"));
+                if (oldView < expOldThreshold) {
+                    double newView = Double.parseDouble(featureMap.getOrDefault(expNewKey, "0"));
+                    return getExpWeight(expLowerWeight, expUpperWeight, expScale, expNewThreshold, newView);
+                }
+            }
+        } catch (Exception e) {
+            log.error("getExpWeight error: ", e);
+        }
+        return 1.0;
+    }
+
+    private double getExpWeight(double lowerWeight, double upperWeight, double scale, double upperExp, double exp) {
+        if (exp >= upperExp) {
+            return 1.0;
+        }
+        double weight = Math.log(exp + 1) / scale;
+        return Math.min(Math.max(lowerWeight, weight), upperWeight);
+    }
+}

+ 1012 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy847.java

@@ -0,0 +1,1012 @@
+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.helper.AnomalousCidDataHelper;
+import com.tzld.piaoquan.ad.engine.commons.helper.DnnCidDataHelper;
+import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
+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.lang.math.NumberUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.xm.Similarity;
+
+import javax.annotation.PostConstruct;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static com.tzld.piaoquan.ad.engine.commons.math.Const.*;
+
+@Slf4j
+@Component
+public class RankStrategyBy847 extends RankStrategyBasic {
+
+    /**
+     * 空 Map 常量,避免频繁创建空 HashMap
+     */
+    private static final Map<String, String> EMPTY_STRING_MAP = Collections.emptyMap();
+    private static final Map<String, Map<String, String>> EMPTY_NESTED_MAP = Collections.emptyMap();
+
+    private Map<String, double[]> bucketsMap = new HashMap<>();
+
+    private Map<String, Double> bucketsLen = new HashMap<>();
+
+    @Value("${word2vec.exp:694}")
+    private String word2vecExp;
+
+    @ApolloJsonValue("${rank.score.params.847:{}}")
+    private Map<String, String> paramsMap;
+
+    // FIXME(zhoutian): 可能需要独立配置
+    @ApolloJsonValue("${rank.score.weight.680:{}}")
+    private Map<String, Double> weightMap;
+
+    /**
+     * 人群分层&创意的权重
+     * 格式:{layer_creativeId: weight}
+     */
+    @ApolloJsonValue("${rank.score.weight.layer.and.creative:{}}")
+    private Map<String, Double> layerAndCreativeWeightMap;
+
+    @ApolloJsonValue("${rank.score.neg_sample_rate:0.01}")
+    Double negSampleRate;
+
+    Set<String> sparseFeatureSet;
+
+
+    @PostConstruct
+    public void afterInit() {
+        this.readBucketFile();
+        this.initSparseFeatureNames();
+    }
+
+
+    @Override
+    public List<AdRankItem> adItemRank(RankRecommendRequestParam request, ScoreParam scoreParam) {
+        Map<String, Double> weightParam = ObjUtil.nullOrDefault(weightMap, new HashMap<>());
+
+
+        Map<Long, Double> creativeScoreCoefficient = getCreativeScoreCoefficient();
+        Set<String> noApiAdVerIds = getNoApiAdVerIds();
+
+        long ts = System.currentTimeMillis() / 1000;
+
+        String brand = scoreParam.getRequestContext().getMachineinfoBrand();
+        if (StringUtils.isNotEmpty(brand)) {
+            scoreParam.getRequestContext().setMachineinfoBrand(brand + "-n");
+        }
+
+        long start = System.currentTimeMillis();
+        //过滤创意
+        filterRequestAdList(request, scoreParam);
+        // 特征处理
+        // feature1
+        Feature feature = this.getFeature(scoreParam, request);
+        if (feature == null) {
+            log.warn("adItemRank: feature is null, skip processing. request={}", request);
+            return new ArrayList<>();
+        }
+
+        Map<String, Map<String, String>> userFeature = feature.getUserFeature();
+        Map<String, Map<String, String>> videoFeature = feature.getVideoFeature();
+        Map<String, Map<String, Map<String, String>>> allAdVerFeature = feature.getAdVerFeature();
+        Map<String, Map<String, Map<String, String>>> allCidFeature = feature.getCidFeature();
+        Map<String, Map<String, Map<String, String>>> allSkuFeature = feature.getSkuFeature();
+        Map<String, String> reqFeature = this.getReqFeature(scoreParam, request);
+
+        Map<String, String> userFeatureMap = new HashMap<>();
+        Map<String, String> c1Feature = userFeature.getOrDefault("alg_mid_feature_ad_action", EMPTY_STRING_MAP);
+        List<TupleMapEntry<Tuple5>> midActionList = this.handleC1Feature(c1Feature, userFeatureMap);
+
+        Map<String, Double> midTimeDiffMap = this.parseC1FeatureListToTimeDiffMap(midActionList, ts);
+        Map<String, Double> actionStaticMap = this.parseC1FeatureListToActionStaticMap(midActionList);
+
+        Map<String, String> d2Feature = videoFeature.getOrDefault("alg_cid_feature_vid_cf_rank", EMPTY_STRING_MAP);
+        Map<String, String> d3Feature = videoFeature.getOrDefault("alg_vid_feature_basic_info", EMPTY_STRING_MAP);
+
+        Map<String, Map<String, Double>> vidRankMaps = this.parseD2FeatureMap(d2Feature);
+
+        Map<String, String> e1Feature = userFeature.getOrDefault("alg_mid_feature_return_tags", EMPTY_STRING_MAP);
+        Map<String, String> e2Feature = userFeature.getOrDefault("alg_mid_feature_share_tags", EMPTY_STRING_MAP);
+
+        Map<String, String> g1Feature = userFeature.getOrDefault("mid_return_video_cate", EMPTY_STRING_MAP);
+        Map<String, String> g2Feature = userFeature.getOrDefault("mid_share_video_cate", EMPTY_STRING_MAP);
+
+
+        userFeatureMap.put("brand", reqFeature.getOrDefault("brand", ""));
+        userFeatureMap.put("region", reqFeature.getOrDefault("region", ""));
+        userFeatureMap.put("city", reqFeature.getOrDefault("city", ""));
+        userFeatureMap.put("vid", reqFeature.getOrDefault("vid", ""));
+        userFeatureMap.put("apptype", reqFeature.getOrDefault("apptype", ""));
+        userFeatureMap.put("is_first_layer", reqFeature.getOrDefault("is_first_layer", ""));
+        userFeatureMap.put("root_source_scene", reqFeature.getOrDefault("root_source_scene", ""));
+        userFeatureMap.put("root_source_channel", reqFeature.getOrDefault("root_source_channel", ""));
+
+
+        userFeatureMap.put("cate1", d3Feature.get("merge_first_level_cate"));
+        userFeatureMap.put("cate2", d3Feature.get("merge_second_level_cate"));
+        userFeatureMap.put("user_vid_return_tags_2h", e1Feature.getOrDefault("tags_2h", null));
+        userFeatureMap.put("user_vid_return_tags_1d", e1Feature.getOrDefault("tags_1d", null));
+        userFeatureMap.put("user_vid_return_tags_3d", e1Feature.getOrDefault("tags_3d", null));
+        userFeatureMap.put("user_vid_return_tags_7d", e1Feature.getOrDefault("tags_7d", null));
+        userFeatureMap.put("user_vid_return_tags_14d", e1Feature.getOrDefault("tags_14d", null));
+        userFeatureMap.put("title_split", d3Feature.getOrDefault("title_split", null));
+        userFeatureMap.put("user_vid_share_tags_1d", e2Feature.getOrDefault("tags_1d", null));
+        userFeatureMap.put("user_vid_share_tags_14d", e2Feature.getOrDefault("tags_14d", null));
+        userFeatureMap.put("user_vid_return_cate1_14d", g1Feature.getOrDefault("cate1_14d", null));
+        userFeatureMap.put("user_vid_return_cate2_14d", g1Feature.getOrDefault("cate2_14d", null));
+        userFeatureMap.put("user_vid_share_cate1_14d", g2Feature.getOrDefault("cate1_14d", null));
+        userFeatureMap.put("user_vid_share_cate2_14d", g2Feature.getOrDefault("cate2_14d", null));
+
+        Map<String, String> sceneFeatureMap = this.handleSceneFeature(ts);
+        long time1 = System.currentTimeMillis();
+
+        boolean isGuaranteedFlow = getIsGuaranteedFlow(scoreParam);
+        Map<String, GuaranteeView> map = getGuaranteeViewMap(request, isGuaranteedFlow);
+        Map<Long, CorrectCpaParam> correctCpaMap = getCorrectCpaParamMap(request, scoreParam, reqFeature);
+        List<AdRankItem> adRankItems = new ArrayList<>();
+        Random random = new Random();
+        List<Future<AdRankItem>> futures = new ArrayList<>();
+        CountDownLatch cdl1 = new CountDownLatch(request.getAdIdList().size());
+        for (AdPlatformCreativeDTO dto : request.getAdIdList()) {
+            Future<AdRankItem> future = ThreadPoolFactory.feature().submit(() -> {
+                AdRankItem adRankItem = new AdRankItem();
+                try {
+                    adRankItem.setAdId(dto.getCreativeId());
+                    adRankItem.setCreativeCode(dto.getCreativeCode());
+                    adRankItem.setAdVerId(dto.getAdVerId());
+                    adRankItem.setVideoId(request.getVideoId());
+                    adRankItem.setCpa(dto.getCpa());
+                    adRankItem.setId(dto.getAdId());
+                    adRankItem.setCampaignId(dto.getCampaignId());
+                    adRankItem.setCpm(ObjUtil.nullOrDefault(dto.getCpm(), 90).doubleValue());
+                    adRankItem.setSkuId(dto.getSkuId());
+                    adRankItem.setCustomerId(dto.getCustomerId());
+                    adRankItem.setProfession(dto.getProfession());
+                    adRankItem.setLandingPageType(dto.getLandingPageType());
+                    adRankItem.setRandom(random.nextInt(1000));
+                    if (noApiAdVerIds.contains(dto.getAdVerId())) {
+                        adRankItem.getExt().put("isApi", "0");
+                    } else {
+                        adRankItem.getExt().put("isApi", "1");
+                    }
+                    adRankItem.getExt().put("recallsources", dto.getRecallSources());
+                    fillAdRankItemExt(adRankItem, dto);
+                    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, reqFeature);
+                    String cidStr = dto.getCreativeId().toString();
+                    Map<String, String> cidFeatureMap = adRankItem.getFeatureMap();
+                    Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, EMPTY_NESTED_MAP);
+                    Map<String, String> b1Feature = cidFeature.getOrDefault("alg_cid_feature_basic_info", EMPTY_STRING_MAP);
+
+                    Map<String, Map<String, String>> adVerFeature = allAdVerFeature.getOrDefault(dto.getAdVerId(), EMPTY_NESTED_MAP);
+                    Map<String, Map<String, String>> skuFeature = allSkuFeature.getOrDefault(String.valueOf(dto.getSkuId()), EMPTY_NESTED_MAP);
+                    Map<String, String> d1Feature = cidFeature.getOrDefault("alg_cid_feature_vid_cf", EMPTY_STRING_MAP);
+
+                    this.handleB1Feature(b1Feature, cidFeatureMap, cidStr);
+                    this.handleB2ToB5AndB8ToB9Feature(cidFeature, adVerFeature, cidFeatureMap);
+                    this.handleB6ToB7Feature(cidFeature, cidFeatureMap);
+                    this.handleC1UIFeature(midTimeDiffMap, actionStaticMap, cidFeatureMap, cidStr);
+                    this.handleD1Feature(d1Feature, cidFeatureMap);
+                    this.handleD2Feature(vidRankMaps, cidFeatureMap, cidStr);
+                    this.handleH1AndH2Feature(skuFeature, adVerFeature, cidFeatureMap);
+                    cidFeatureMap.put("cid", dto.getCreativeId() != null ? String.valueOf(dto.getCreativeId()) : "");
+                    cidFeatureMap.put("adid", dto.getAdId() != null ? String.valueOf(dto.getAdId()) : "");
+                    cidFeatureMap.put("adverid", dto.getAdVerId() != null ? dto.getAdVerId() : "");
+                    cidFeatureMap.put("profession", dto.getProfession() != null ? dto.getProfession() : "");
+                    cidFeatureMap.put("category_name", dto.getCategoryName() != null ? dto.getCategoryName() : "");
+                    cidFeatureMap.put("material_md5", dto.getMaterialMd5() != null ? dto.getMaterialMd5() : "");
+                    cidFeatureMap.put("ad_profession_id", dto.getAdProfessionId() != null ? String.valueOf(dto.getAdProfessionId()) : "");
+                    cidFeatureMap.put("ad_profession_name", dto.getAdProfessionName() != null ? dto.getAdProfessionName() : "");
+                    cidFeatureMap.put("ad_category_name", dto.getAdCategoryName() != null ? dto.getAdCategoryName() : "");
+                    cidFeatureMap.put("ad_category_id", dto.getAdCategoryId() != null ? String.valueOf(dto.getAdCategoryId()) : "");
+                    cidFeatureMap.put("ad_sku_id", dto.getAdSkuId() != null ? String.valueOf(dto.getAdSkuId()) : "");
+                    cidFeatureMap.put("ad_sku_code", dto.getAdSkuCode() != null ? dto.getAdSkuCode() : "");
+                    cidFeatureMap.put("ad_sku_name", dto.getAdSkuName() != null ? dto.getAdSkuName() : "");
+                    //DNN模型没训练过的cid才不传入广告相关的稀疏特征
+                    if (CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(adRankItem.getAdId())) {
+                        cidFeatureMap.put("cid", "");
+                        cidFeatureMap.put("adid", "");
+                        cidFeatureMap.put("adverid", "");
+                    }
+                    return adRankItem;
+                } finally {
+                    cdl1.countDown();
+                }
+            });
+            futures.add(future);
+        }
+        try {
+            cdl1.await(300, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
+        }
+        for (Future<AdRankItem> future : futures) {
+            try {
+                if (future.isDone()) {
+                    adRankItems.add(future.get());
+                }
+            } catch (Exception e) {
+                log.error("Feature handle error", e);
+            }
+        }
+
+        long time2 = System.currentTimeMillis();
+        // feature3
+        // 请求级别的 tag 分词缓存,所有广告共享(同一用户的 tags 相同)
+        Map<String, List<String>> tagWordsCache = new ConcurrentHashMap<>();
+        CountDownLatch cdl2 = new CountDownLatch(adRankItems.size() * 2);
+        for (AdRankItem item : adRankItems) {
+            String cidStr = String.valueOf(item.getAdId());
+            Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(cidStr, EMPTY_NESTED_MAP);
+            Map<String, String> b1Feature = cidFeature.getOrDefault("alg_cid_feature_basic_info", EMPTY_STRING_MAP);
+            String title = b1Feature.getOrDefault("cidtitle", "");
+            ThreadPoolFactory.defaultPool().submit(() -> {
+                try {
+                    this.handleE1AndE2Feature(e1Feature, e2Feature, title, item.getFeatureMap(), scoreParam, tagWordsCache);
+                } finally {
+                    cdl2.countDown();
+                }
+            });
+            ThreadPoolFactory.defaultPool().submit(() -> {
+                try {
+                    this.handleD3AndB1Feature(d3Feature, title, item.getFeatureMap(), scoreParam);
+                } finally {
+                    cdl2.countDown();
+                }
+            });
+        }
+        try {
+            cdl2.await(150, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
+        }
+
+        long time3 = System.currentTimeMillis();
+        // 分桶
+        userFeatureMap = this.featureBucket(userFeatureMap);
+        CountDownLatch cdl4 = new CountDownLatch(adRankItems.size());
+        for (AdRankItem adRankItem : adRankItems) {
+            ThreadPoolFactory.feature().submit(() -> {
+                try {
+                    Map<String, String> featureMap = adRankItem.getFeatureMap();
+                    adRankItem.setFeatureMap(this.featureBucket(featureMap));
+                } finally {
+                    cdl4.countDown();
+                }
+            });
+        }
+        try {
+            cdl4.await(100, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            log.error("handleE1AndE2Feature and handleD3AndB1Feature wait timeout", e);
+        }
+        long time4 = System.currentTimeMillis();
+        // 打分排序
+        // getScorerPipeline
+
+        if (CollectionUtils.isEmpty(adRankItems)) {
+            log.error("adRankItems is empty");
+        }
+        List<AdRankItem> result = ScorerUtils.getScorerPipeline(ScorerUtils.PAI_SCORE_CONF_20250804).scoring(sceneFeatureMap, userFeatureMap, adRankItems);
+        if (CollectionUtils.isEmpty(result)) {
+            log.error("scoring result is empty");
+        }
+        long time5 = System.currentTimeMillis();
+        int viewLimit = NumberUtils.toInt(paramsMap.getOrDefault("viewLimit", "3000"));
+        // calibrate score for negative sampling or cold start
+        for (AdRankItem item : result) {
+            double originalScore = item.getLrScore();
+            double calibratedScore = originalScore / (originalScore + (1 - originalScore) / negSampleRate);
+            // 该创意尚未在模型中训练,打分不可靠
+            Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
+            Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
+            double view3Day = Double.parseDouble(b3Feature.getOrDefault("ad_view_3d", "0"));
+            if ((CollectionUtils.isNotEmpty(DnnCidDataHelper.getCidSetV2()) && !DnnCidDataHelper.getCidSetV2().contains(item.getAdId()))
+                    || view3Day <= viewLimit) {
+                double view = Double.parseDouble(b3Feature.getOrDefault("ad_view_14d", "0"));
+                double conver = Double.parseDouble(b3Feature.getOrDefault("ad_conversion_14d", "0"));
+                double smoothCxr = NumUtil.divSmoothV1(conver, view, 1.64);
+                //模型打分和统计计算取打分更低的
+                item.getScoreMap().put("cvcvrItemValue", 1.0);
+                if (smoothCxr <= calibratedScore) {
+                    calibratedScore = smoothCxr;
+                    item.getScoreMap().put("cvcvrItemValue", 2.0);
+                }
+            }
+            item.setLrScore(calibratedScore);
+            item.getScoreMap().put("originCtcvrScore", originalScore);
+            item.getScoreMap().put("modelCtcvrScore", calibratedScore);
+            item.getScoreMap().put("ctcvrScore", calibratedScore);
+        }
+
+        String calibModelName = paramsMap.getOrDefault("calibModelName", "dnnV3");
+        calculateCtcvrScore(result, request, scoreParam, calibModelName, reqFeature);
+        if (CollectionUtils.isEmpty(result)) {
+            log.error("calculateCtcvrScore result is empty");
+        }
+        // loop
+        double cpmCoefficient = weightParam.getOrDefault("cpmCoefficient", 0.9);
+        boolean isGuaranteeType = false;
+        // 查询人群分层信息
+        String peopleLayer = Optional.of(reqFeature)
+                .map(f -> f.get("layer"))
+                .map(s -> s.replace("-炸", ""))
+                .orElse(null);
+
+        // 控制曝光参数
+        String expOldKey = paramsMap.getOrDefault("expOldKey", "ad_view_yesterday");
+        double expOldThreshold = NumberUtils.toDouble(paramsMap.getOrDefault("expOldThreshold", "1000"));
+        String expNewKey = paramsMap.getOrDefault("expNewKey", "ad_view_today");
+        double expNewThreshold = NumberUtils.toDouble(paramsMap.getOrDefault("expNewThreshold", "3000"));
+        double expLowerWeight = NumberUtils.toDouble(paramsMap.getOrDefault("expLowerWeight", "0.2"));
+        double expUpperWeight = NumberUtils.toDouble(paramsMap.getOrDefault("expUpperWeight", "1.0"));
+        double expScale = NumberUtils.toDouble(paramsMap.getOrDefault("expScale", "10.0"));
+        double anomWeight = NumberUtils.toDouble(paramsMap.getOrDefault("anomWeight", "1.0"));
+        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 anomW = 1.0;
+            boolean h5Page = this.landingH5Page(item);
+            if (h5Page) {
+                if (AnomalousCidDataHelper.contains(item.getAdId())) {
+                    anomW = anomWeight;
+                }
+            }
+
+            // 控制曝光权重
+            Map<String, Map<String, String>> cidFeature = allCidFeature.getOrDefault(String.valueOf(item.getAdId()), EMPTY_NESTED_MAP);
+            Map<String, String> b3Feature = cidFeature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
+            double expWeight = getExpWeight(b3Feature,
+                    expOldKey, expOldThreshold,
+                    expNewKey, expNewThreshold,
+                    expLowerWeight, expUpperWeight, expScale);
+
+            String layerAndCreativeWeightMapKey = getLayerAndCreativeWeightMapKey(peopleLayer, String.valueOf(item.getAdId()));
+            // 人群分层&创意的权重
+            double layerAndCreativeWeight = getLayerAndCreativeWeight(layerAndCreativeWeightMapKey);
+            double scoreCoefficient = creativeScoreCoefficient.getOrDefault(item.getAdId(), 1d);
+            double guaranteeScoreCoefficient = getGuaranteeScoreCoefficient(isGuaranteedFlow, item.getExt());
+            double score = anomW * expWeight * item.getLrScore() * bid * scoreCoefficient * guaranteeScoreCoefficient * layerAndCreativeWeight;
+            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.getScoreMap().put("anom", anomW);
+            item.getFeatureMap().putAll(userFeatureMap);
+            item.getFeatureMap().putAll(sceneFeatureMap);
+
+            // 没有转化回传的广告主,使用后台配置的CPM
+            if (noApiAdVerIds.contains(item.getAdVerId())) {
+                score = item.getCpm() * cpmCoefficient / 1000;
+            }
+            item.setScore(score);
+        }
+
+
+        result.sort(ComparatorUtil.equalsRandomComparator());
+
+        String logModelName = paramsMap.getOrDefault("logModelName", "dnnV3");
+        if (CollectionUtils.isNotEmpty(result)) {
+            AdRankItem top1Item = result.get(0);
+            List<String> participateCompetitionType = new ArrayList<>();
+            participateCompetitionType.add("engine");
+            top1Item.getExt().put("isGuaranteeType", isGuaranteeType);
+            if (isGuaranteeType) {
+                participateCompetitionType.add("guarantee");
+            }
+            top1Item.getExt().put("participateCompetitionType", StringUtils.join(participateCompetitionType, ","));
+            Double modelCtcvrScore = top1Item.getScoreMap().get("modelCtcvrScore");
+            Double ctcvrScore = top1Item.getScoreMap().get("ctcvrScore");
+            if (scoreParam.getExpCodeSet().contains(checkoutEcpmExp)) {
+                top1Item.getExt().put("ecpm", ctcvrScore * top1Item.getCpa() * 1000);
+                String filterEcpmValue = paramsMap.getOrDefault("filterEcpm", filterEcpm);
+                top1Item.getExt().put("filterEcpm", filterEcpmValue);
+            } else {
+                top1Item.getExt().put("ecpm", modelCtcvrScore * top1Item.getCpa() * 1000);
+            }
+            putMetaFeature(top1Item, feature, reqFeature, sceneFeatureMap, request);
+            top1Item.getExt().put("model", logModelName);
+            String coefficientRate = paramsMap.getOrDefault("coefficientRate", "1");
+            top1Item.getExt().put("coefficientRate", coefficientRate);
+        }
+        long time6 = System.currentTimeMillis();
+        log.info("cost={}, getFeature={}, handleFeature={},  similar={}, bucketFeature={}, getScorerPipeline={}, " +
+                        "other={}, adIdSize={}, adRankItemsSize={}",
+                time6 - start, time1 - start, time2 - time1, time3 - time2, time4 - time3,
+                time5 - time4, time6 - time5, request.getAdIdList().size(), adRankItems.size());
+
+        return result;
+    }
+
+    /**
+     * 获取人群分层和创意的权重
+     *
+     * @param key
+     * @return
+     */
+    private Double getLayerAndCreativeWeight(String key) {
+        if (StringUtils.isBlank(key)) {
+            return 1d;
+        }
+        return layerAndCreativeWeightMap.getOrDefault(key, 1d);
+    }
+
+    /**
+     * 获取人群分层和创意的权重key
+     *
+     * @param layer
+     * @param creativeId
+     * @return
+     */
+    private String getLayerAndCreativeWeightMapKey(String layer, String creativeId) {
+        if (StringUtils.isBlank(layer) || StringUtils.isBlank(creativeId)) {
+            return null;
+        }
+        return layer + "_" + creativeId;
+    }
+
+
+    private void handleB1Feature(Map<String, String> b1Feature, Map<String, String> cidFeatureMap, String cid) {
+        cidFeatureMap.put("cid_" + cid, "0.1");
+        // if (StringUtils.isNotBlank(b1Feature.get("adid"))) {
+        //     String adId = b1Feature.get("adid");
+        //     cidFeatureMap.put("adid_" + adId, idDefaultValue);
+        // }
+        if (StringUtils.isNotBlank(b1Feature.get("adverid"))) {
+            String adVerId = b1Feature.get("adverid");
+            cidFeatureMap.put("adverid_" + adVerId, "0.1");
+        }
+        // if (StringUtils.isNotBlank(b1Feature.get("targeting_conversion"))) {
+        //     String targetingConversion = b1Feature.get("targeting_conversion");
+        //     cidFeatureMap.put("targeting_conversion_" + targetingConversion, idDefaultValue);
+        // }
+        if (StringUtils.isNotBlank(b1Feature.get("cpa"))) {
+            String cpa = b1Feature.get("cpa");
+            cidFeatureMap.put("cpa", cpa);
+        }
+    }
+
+    private void handleB2ToB5AndB8ToB9Feature(Map<String, Map<String, String>> c1Feature, Map<String, Map<String, String>> adVerFeature, Map<String, String> cidFeatureMap) {
+        Map<String, String> b2Feature = adVerFeature.getOrDefault("alg_cid_feature_adver_action", EMPTY_STRING_MAP);
+        Map<String, String> b3Feature = c1Feature.getOrDefault("alg_cid_feature_cid_action", EMPTY_STRING_MAP);
+        Map<String, String> b4Feature = c1Feature.getOrDefault("alg_cid_feature_region_action", EMPTY_STRING_MAP);
+        Map<String, String> b5Feature = c1Feature.getOrDefault("alg_cid_feature_app_action", EMPTY_STRING_MAP);
+        Map<String, String> b8Feature = c1Feature.getOrDefault("alg_cid_feature_brand_action", EMPTY_STRING_MAP);
+        Map<String, String> b9Feature = c1Feature.getOrDefault("alg_cid_feature_weChatVersion_action", EMPTY_STRING_MAP);
+
+        List<String> timeList = Arrays.asList("1h", "2h", "3h", "6h", "12h", "1d", "3d", "7d", "yesterday", "today");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(b2Feature, "b2"),
+                new Tuple2<>(b3Feature, "b3"),
+                new Tuple2<>(b4Feature, "b4"),
+                new Tuple2<>(b5Feature, "b5"),
+                new Tuple2<>(b8Feature, "b8"),
+                new Tuple2<>(b9Feature, "b9")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                double view = Double.parseDouble(feature.getOrDefault("ad_view_" + time, "0"));
+                double click = Double.parseDouble(feature.getOrDefault("ad_click_" + time, "0"));
+                double conver = Double.parseDouble(feature.getOrDefault("ad_conversion_" + time, "0"));
+                double income = Double.parseDouble(feature.getOrDefault("ad_income_" + time, "0"));
+                double cpc = NumUtil.div(income, click);
+                double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
+                double ctcvr = NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR);
+                double ecpm = ctr * cpc * 1000;
+                cidFeatureMap.put(prefix + "_" + time + "_ctr", String.valueOf(ctr));
+                cidFeatureMap.put(prefix + "_" + time + "_ctcvr", String.valueOf(ctcvr));
+                cidFeatureMap.put(prefix + "_" + time + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver", String.valueOf(conver));
+                cidFeatureMap.put(prefix + "_" + time + "_ecpm", String.valueOf(ecpm));
+
+                cidFeatureMap.put(prefix + "_" + time + "_click", String.valueOf(click));
+                cidFeatureMap.put(prefix + "_" + time + "_conver*log(view)", String.valueOf(conver * NumUtil.log(view)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver*ctcvr", String.valueOf(conver * ctcvr));
+            }
+        }
+
+    }
+
+    private void handleB6ToB7Feature(Map<String, Map<String, String>> c1Feature, Map<String, String> cidFeatureMap) {
+        Map<String, String> b6Feature = c1Feature.getOrDefault("alg_cid_feature_week_action", EMPTY_STRING_MAP);
+        Map<String, String> b7Feature = c1Feature.getOrDefault("alg_cid_feature_hour_action", EMPTY_STRING_MAP);
+
+        List<String> timeList = Arrays.asList("7d", "14d");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(b6Feature, "b6"),
+                new Tuple2<>(b7Feature, "b7")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                double view = Double.parseDouble(feature.getOrDefault("ad_view_" + time, "0"));
+                double click = Double.parseDouble(feature.getOrDefault("ad_click_" + time, "0"));
+                double conver = Double.parseDouble(feature.getOrDefault("ad_conversion_" + time, "0"));
+                double income = Double.parseDouble(feature.getOrDefault("ad_income_" + time, "0"));
+                double cpc = NumUtil.div(income, click);
+                double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
+                double ctcvr = NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR);
+                double ecpm = ctr * cpc * 1000;
+                cidFeatureMap.put(prefix + "_" + time + "_ctr", String.valueOf(ctr));
+                cidFeatureMap.put(prefix + "_" + time + "_ctcvr", String.valueOf(ctcvr));
+                cidFeatureMap.put(prefix + "_" + time + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver", String.valueOf(conver));
+                cidFeatureMap.put(prefix + "_" + time + "_ecpm", String.valueOf(ecpm));
+
+                cidFeatureMap.put(prefix + "_" + time + "_click", String.valueOf(click));
+                cidFeatureMap.put(prefix + "_" + time + "_conver*log(view)", String.valueOf(conver * NumUtil.log(view)));
+                cidFeatureMap.put(prefix + "_" + time + "_conver*ctcvr", String.valueOf(conver * ctcvr));
+            }
+        }
+
+    }
+
+    private List<TupleMapEntry<Tuple5>> handleC1Feature(Map<String, String> c1Feature, Map<String, String> featureMap) {
+
+        //用户近1年内是否有转化
+        if (c1Feature.containsKey("user_has_conver_1y") && c1Feature.get("user_has_conver_1y") != null) {
+            featureMap.put("user_has_conver_1y", c1Feature.get("user_has_conver_1y"));
+        }
+        //用户历史转化过品类
+        if (c1Feature.containsKey("user_conver_ad_class") && c1Feature.get("user_conver_ad_class") != null) {
+            featureMap.put("user_conver_ad_class", c1Feature.get("user_conver_ad_class"));
+        }
+
+        // 用户特征
+        List<TupleMapEntry<Tuple5>> midActionList = new ArrayList<>();
+        if (c1Feature.containsKey("action")) {
+            String action = c1Feature.get("action");
+            midActionList = Arrays.stream(action.split(","))
+                    .map(r -> {
+                        String[] rList = r.split(":");
+                        Tuple5 tuple5 = new Tuple5(rList[1], rList[2], rList[3], rList[4], rList[5]);
+                        return new TupleMapEntry<>(rList[0], tuple5);
+                    })
+                    // TODO 倒排
+                    .sorted((a, b) -> Integer.compare(Integer.parseInt(b.value.f1), Integer.parseInt(a.value.f1)))
+                    .collect(Collectors.toList());
+        }
+
+        double viewAll = midActionList.size();
+        double clickAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f2)).sum();
+        double converAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f3)).sum();
+        double incomeAll = midActionList.stream().mapToInt(e -> Integer.parseInt(e.value.f4)).sum();
+        featureMap.put("viewAll", String.valueOf(viewAll));
+        featureMap.put("clickAll", String.valueOf(clickAll));
+        featureMap.put("converAll", String.valueOf(converAll));
+        featureMap.put("incomeAll", String.valueOf(incomeAll));
+        featureMap.put("ctr_all", String.valueOf(NumUtil.div(clickAll, viewAll)));
+        featureMap.put("ctcvr_all", String.valueOf(NumUtil.div(converAll, viewAll)));
+        featureMap.put("cvr_all", String.valueOf(NumUtil.div(clickAll, converAll)));
+        featureMap.put("ecpm_all", String.valueOf(NumUtil.div(incomeAll * 1000, viewAll)));
+        if (CollectionUtils.isNotEmpty(midActionList)) {
+            List<String> cidClickList = new ArrayList<>();
+            List<String> cidConverList = new ArrayList<>();
+            for (TupleMapEntry<Tuple5> tupleMapEntry : midActionList) {
+                String cid = tupleMapEntry.key;
+                String click = tupleMapEntry.value.f2;
+                String conver = tupleMapEntry.value.f3;
+                if (Objects.equals(click, "1")) {
+                    cidClickList.add(cid);
+                }
+                if (Objects.equals(conver, "1")) {
+                    cidConverList.add(cid);
+                }
+            }
+            featureMap.put("user_cid_click_list", String.join(",", cidClickList));
+            featureMap.put("user_cid_conver_list", String.join(",", cidConverList));
+        }
+        return midActionList;
+    }
+
+    private void handleC1UIFeature(Map<String, Double> midTimeDiffMap, Map<String, Double> midActionStatic, Map<String, String> featureMap, String cid) {
+        if (midTimeDiffMap.containsKey("timediff_view_" + cid)) {
+            featureMap.put("timediff_view", String.valueOf(midTimeDiffMap.getOrDefault("timediff_view_" + cid, 0.0)));
+        }
+        if (midTimeDiffMap.containsKey("timediff_click_" + cid)) {
+            featureMap.put("timediff_click", String.valueOf(midTimeDiffMap.getOrDefault("timediff_click_" + cid, 0.0)));
+        }
+        if (midTimeDiffMap.containsKey("timediff_conver_" + cid)) {
+            featureMap.put("timediff_conver", String.valueOf(midTimeDiffMap.getOrDefault("timediff_conver_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid)) {
+            featureMap.put("actionstatic_view", String.valueOf(midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            featureMap.put("actionstatic_click", String.valueOf(midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_conver_" + cid)) {
+            featureMap.put("actionstatic_conver", String.valueOf(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_income_" + cid)) {
+            featureMap.put("actionstatic_income", String.valueOf(midActionStatic.getOrDefault("actionstatic_income_" + cid, 0.0)));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid) && midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            double ctr = NumUtil.div(
+                    midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0),
+                    midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0)
+            );
+            featureMap.put("actionstatic_ctr", String.valueOf(ctr));
+        }
+        if (midActionStatic.containsKey("actionstatic_view_" + cid) && midActionStatic.containsKey("actionstatic_conver_" + cid)) {
+            double ctcvr = NumUtil.div(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0), midActionStatic.getOrDefault("actionstatic_view_" + cid, 0.0));
+            featureMap.put("actionstatic_ctcvr", String.valueOf(ctcvr));
+        }
+        if (midActionStatic.containsKey("actionstatic_conver_" + cid) && midActionStatic.containsKey("actionstatic_click_" + cid)) {
+            double cvr = NumUtil.div(midActionStatic.getOrDefault("actionstatic_conver_" + cid, 0.0), midActionStatic.getOrDefault("actionstatic_click_" + cid, 0.0));
+            featureMap.put("actionstatic_cvr", String.valueOf(cvr));
+        }
+    }
+
+    private void handleD1Feature(Map<String, String> d1Feature, Map<String, String> featureMap) {
+        for (String prefix : Arrays.asList("3h", "6h", "12h", "1d", "3d", "7d")) {
+            double view = Double.parseDouble(d1Feature.getOrDefault("ad_view_" + prefix, "0"));
+            double click = Double.parseDouble(d1Feature.getOrDefault("ad_click_" + prefix, "0"));
+            double conver = Double.parseDouble(d1Feature.getOrDefault("ad_conversion_" + prefix, "0"));
+            double income = Double.parseDouble(d1Feature.getOrDefault("ad_income_" + prefix, "0"));
+            double cpc = NumUtil.div(income, click);
+            double ctr = NumUtil.divSmoothV2(click, view, CTR_SMOOTH_BETA_FACTOR);
+            featureMap.put("d1_feature_" + prefix + "_ctr", String.valueOf(ctr));
+            featureMap.put("d1_feature_" + prefix + "_ctcvr", String.valueOf(NumUtil.divSmoothV2(conver, view, CTCVR_SMOOTH_BETA_FACTOR)));
+            featureMap.put("d1_feature_" + prefix + "_cvr", String.valueOf(NumUtil.divSmoothV2(conver, click, CVR_SMOOTH_BETA_FACTOR)));
+            featureMap.put("d1_feature_" + prefix + "_conver", String.valueOf(conver));
+            featureMap.put("d1_feature_" + prefix + "_ecpm", String.valueOf(ctr * cpc * 1000));
+        }
+    }
+
+    private void handleD2Feature(Map<String, Map<String, Double>> vidRankMaps, Map<String, String> featureMap, String cid) {
+        if (MapUtils.isEmpty(vidRankMaps)) {
+            return;
+        }
+
+        List<String> prefixes1 = Arrays.asList("ctr", "ctcvr", "ecpm");
+        // List<String> prefixes1 = Arrays.asList("ctr", "ctcvr");
+        List<String> prefixes2 = Arrays.asList("1d", "3d", "7d", "14d");
+
+        for (String prefix1 : prefixes1) {
+            for (String prefix2 : prefixes2) {
+                String combinedKey = prefix1 + "_" + prefix2;
+                if (vidRankMaps.containsKey(combinedKey)) {
+                    Double rank = vidRankMaps.get(combinedKey).getOrDefault(cid, 0.0);
+                    if (rank >= 1.0) {
+                        featureMap.put("vid_rank_" + combinedKey, String.valueOf(NumUtil.div(1, rank)));
+                    }
+                }
+            }
+        }
+    }
+
+    private void handleH1AndH2Feature(Map<String, Map<String, String>> skuFeature,
+                                      Map<String, Map<String, String>> adVerFeature,
+                                      Map<String, String> cidFeatureMap) {
+        Map<String, String> h1Feature = adVerFeature.getOrDefault("alg_mid_feature_adver_action", EMPTY_STRING_MAP);
+        Map<String, String> h2Feature = skuFeature.getOrDefault("alg_mid_feature_sku_action", EMPTY_STRING_MAP);
+        List<String> timeList = Arrays.asList("3d", "7d", "30d");
+        List<Tuple2<Map<String, String>, String>> featureList = Arrays.asList(
+                new Tuple2<>(h1Feature, "adverid"),
+                new Tuple2<>(h2Feature, "skuid")
+        );
+        for (Tuple2<Map<String, String>, String> tuple2 : featureList) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            for (String time : timeList) {
+                String timeValue = feature.getOrDefault(time, "");
+                if (StringUtils.isNotEmpty(timeValue)) {
+                    String[] split = timeValue.split(",");
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "view" + "_" + time, split[0]);
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "click" + "_" + time, split[1]);
+                    cidFeatureMap.put("user" + "_" + prefix + "_" + "conver" + "_" + time, split[2]);
+                }
+            }
+        }
+
+
+    }
+
+    private void handleD3AndB1Feature(Map<String, String> d3Feature, String cTitle, Map<String, String> featureMap,
+                                      ScoreParam scoreParam) {
+        if (MapUtils.isEmpty(d3Feature) || !d3Feature.containsKey("title") || StringUtils.isEmpty(cTitle)) {
+            return;
+        }
+        String vTitle = d3Feature.get("title");
+        double score;
+        if (scoreParam.getExpCodeSet().contains(word2vecExp)) {
+            score = SimilarityUtils.word2VecSimilarity(cTitle, vTitle);
+        } else {
+            score = Similarity.conceptSimilarity(cTitle, vTitle);
+        }
+        featureMap.put("ctitle_vtitle_similarity", String.valueOf(score));
+    }
+
+    private void handleE1AndE2Feature(Map<String, String> e1Feature, Map<String, String> e2Feature, String title,
+                                      Map<String, String> featureMap, ScoreParam scoreParam,
+                                      Map<String, List<String>> tagWordsCache) {
+        if (StringUtils.isEmpty(title)) {
+            return;
+        }
+
+        // 预先分词 title,在整个方法中复用,避免重复分词
+        List<String> titleWords = null;
+        if (scoreParam.getExpCodeSet().contains(word2vecExp)) {
+            titleWords = SimilarityUtils.segment(title);
+        }
+
+        List<Tuple2<Map<String, String>, String>> tuple2List = Arrays.asList(new Tuple2<>(e1Feature, "e1"), new Tuple2<>(e2Feature, "e2"));
+
+        List<String> tagsFieldList = Arrays.asList("tags_3d", "tags_7d", "tags_14d");
+        for (Tuple2<Map<String, String>, String> tuple2 : tuple2List) {
+            Map<String, String> feature = tuple2.f1;
+            String prefix = tuple2.f2;
+            if (MapUtils.isEmpty(feature)) {
+                continue;
+            }
+
+            for (String tagsField : tagsFieldList) {
+                if (StringUtils.isNotEmpty(feature.get(tagsField))) {
+                    String tags = feature.get(tagsField);
+                    Double[] doubles;
+                    if (scoreParam.getExpCodeSet().contains(word2vecExp)) {
+                        // 使用缓存的 title 分词结果和请求级别的 tag 分词缓存
+                        doubles = ExtractorUtils.funcC34567ForTagsNewWithCache(tags, title, titleWords, tagWordsCache);
+                    } else {
+                        doubles = ExtractorUtils.funcC34567ForTags(tags, title);
+                    }
+                    featureMap.put(prefix + "_" + tagsField + "_matchnum", String.valueOf(doubles[0]));
+                    featureMap.put(prefix + "_" + tagsField + "_maxscore", String.valueOf(doubles[1]));
+                    featureMap.put(prefix + "_" + tagsField + "_avgscore", String.valueOf(doubles[2]));
+                }
+            }
+        }
+    }
+
+    private Map<String, Double> parseC1FeatureListToTimeDiffMap(List<TupleMapEntry<Tuple5>> midActionList, long ts) {
+        Map<String, Double> midTimeDiffMap = new HashMap<>();
+        for (TupleMapEntry<Tuple5> entry : midActionList) {
+            String cid = entry.key;
+            double tsHistory = Double.parseDouble(entry.value.f1);
+            double click = Double.parseDouble(entry.value.f2);
+            double conver = Double.parseDouble(entry.value.f3);
+            double d = (ts - tsHistory) / 3600 / 24;
+            if (!midTimeDiffMap.containsKey("timediff_view_" + cid)) {
+                midTimeDiffMap.put("timediff_view_" + cid, NumUtil.div(1, d));
+            }
+            if (!midTimeDiffMap.containsKey("timediff_click_" + cid) && click > 0) {
+                midTimeDiffMap.put("timediff_click_" + cid, NumUtil.div(1, d));
+            }
+            if (!midTimeDiffMap.containsKey("timediff_conver_" + cid) && conver > 0) {
+                midTimeDiffMap.put("timediff_conver_" + cid, NumUtil.div(1, d));
+            }
+        }
+        return midTimeDiffMap;
+    }
+
+    private Map<String, Double> parseC1FeatureListToActionStaticMap(List<TupleMapEntry<Tuple5>> midActionList) {
+        Map<String, Double> midActionStaticsMap = new HashMap<>();
+        for (TupleMapEntry<Tuple5> entry : midActionList) {
+            String cid = entry.key;
+            double click = Double.parseDouble(entry.value.f2);
+            double conver = Double.parseDouble(entry.value.f3);
+            double income = Double.parseDouble(entry.value.f4);
+
+            Double viewSum = midActionStaticsMap.getOrDefault("actionstatic_view_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_view_" + cid, 1 + viewSum);
+
+            Double clickSum = midActionStaticsMap.getOrDefault("actionstatic_click_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_click_" + cid, clickSum + click);
+
+            Double converSum = midActionStaticsMap.getOrDefault("actionstatic_conver_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_conver_" + cid, converSum + conver);
+
+            Double incomSum = midActionStaticsMap.getOrDefault("actionstatic_income_" + cid, 0.0);
+            midActionStaticsMap.put("actionstatic_income_" + cid, incomSum + income);
+        }
+
+        return midActionStaticsMap;
+    }
+
+    private Map<String, Map<String, Double>> parseD2FeatureMap(Map<String, String> d2Feature) {
+        Map<String, Map<String, Double>> vidRankMaps = new HashMap<>();
+        for (Map.Entry<String, String> entry : d2Feature.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            Map<String, Double> valueMap = Arrays.stream(value.split(",")).map(r -> r.split(":")).collect(Collectors.toMap(rList -> rList[0], rList -> Double.parseDouble(rList[2])));
+            vidRankMaps.put(key, valueMap);
+        }
+        return vidRankMaps;
+    }
+
+    private void readBucketFile() {
+        if (MapUtils.isNotEmpty(bucketsMap)) {
+            return;
+        }
+        synchronized (this) {
+            String bucketFile = "20250217_ad_bucket_688.txt";
+            InputStream resourceStream = this.getClass().getClassLoader().getResourceAsStream(bucketFile);
+            if (resourceStream != null) {
+                try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
+                    Map<String, double[]> bucketsMap = new HashMap<>();
+                    Map<String, Double> bucketsLen = new HashMap<>();
+                    String line;
+                    while ((line = reader.readLine()) != null) {
+                        // 替换空格和换行符,过滤空行
+                        line = line.replace(" ", "").replaceAll("\n", "");
+                        if (!line.isEmpty()) {
+                            String[] rList = line.split("\t");
+                            if (rList.length == 3) {
+                                String key = rList[0];
+                                double value1 = Double.parseDouble(rList[1]);
+                                bucketsLen.put(key, value1);
+                                double[] value2 = Arrays.stream(rList[2].split(",")).mapToDouble(Double::valueOf).toArray();
+                                bucketsMap.put(key, value2);
+                            }
+                        }
+                    }
+                    this.bucketsMap = bucketsMap;
+                    this.bucketsLen = bucketsLen;
+                } catch (IOException e) {
+                    log.error("something is wrong in parse bucket file: ", e);
+                }
+                log.info("load bucket file success: {}", bucketFile);
+            } else {
+                log.error("no bucket file");
+            }
+        }
+    }
+
+    private void initSparseFeatureNames() {
+        this.sparseFeatureSet = new HashSet<String>() {{
+            add("brand");
+            add("region");
+            add("city");
+            add("vid");
+            add("cate1");
+            add("cate2");
+            add("cid");
+            add("adid");
+            add("adverid");
+            add("user_cid_click_list");
+            add("user_cid_conver_list");
+            add("user_vid_return_tags_2h");
+            add("user_vid_return_tags_1d");
+            add("user_vid_return_tags_3d");
+            add("user_vid_return_tags_7d");
+            add("user_vid_return_tags_14d");
+            add("apptype");
+            add("hour");
+            add("hour_quarter");
+            add("root_source_scene");
+            add("root_source_channel");
+            add("is_first_layer");
+            add("title_split");
+            add("profession");
+            add("user_vid_share_tags_1d");
+            add("user_vid_share_tags_14d");
+            add("user_vid_return_cate1_14d");
+            add("user_vid_return_cate2_14d");
+            add("user_vid_share_cate1_14d");
+            add("user_vid_share_cate2_14d");
+            add("user_has_conver_1y");
+            add("user_adverid_view_3d");
+            add("user_adverid_click_3d");
+            add("user_adverid_conver_3d");
+            add("user_adverid_view_7d");
+            add("user_adverid_click_7d");
+            add("user_adverid_conver_7d");
+            add("user_adverid_view_30d");
+            add("user_adverid_click_30d");
+            add("user_adverid_conver_30d");
+            add("user_skuid_view_3d");
+            add("user_skuid_click_3d");
+            add("user_skuid_conver_3d");
+            add("user_skuid_view_7d");
+            add("user_skuid_click_7d");
+            add("user_skuid_conver_7d");
+            add("user_skuid_view_30d");
+            add("user_skuid_click_30d");
+            add("user_skuid_conver_30d");
+            add("user_conver_ad_class");
+            add("category_name");
+            add("material_md5");
+            add("ad_profession_id");
+            add("ad_profession_name");
+            add("ad_category_id");
+            add("ad_category_name");
+            add("ad_sku_id");
+            add("ad_sku_code");
+            add("ad_sku_name");
+        }};
+    }
+
+    private Map<String, String> featureBucket(Map<String, String> featureMap) {
+        // 使用 HashMap 替代 ConcurrentHashMap,分桶操作是单线程的
+        Map<String, String> newFeatureMap = new HashMap<>(featureMap.size());
+        for (Map.Entry<String, String> entry : featureMap.entrySet()) {
+            try {
+                String name = entry.getKey();
+                if (this.sparseFeatureSet.contains(name)) {
+                    if (entry.getValue() != null) {
+                        newFeatureMap.put(name, entry.getValue());
+                    }
+                    continue;
+                }
+                double score = Double.parseDouble(entry.getValue());
+                // 注意:0值、不在分桶文件中的特征,会被过滤掉。
+                if (score > 1E-8) {
+                    if (this.bucketsMap.containsKey(name) && this.bucketsLen.containsKey(name)) {
+                        double[] buckets = this.bucketsMap.get(name);
+                        double bucketNum = this.bucketsLen.get(name);
+                        Double scoreNew = 1.0 / bucketNum * (ExtractorUtils.findInsertPosition(buckets, score) + 1.0);
+                        newFeatureMap.put(name, String.valueOf(scoreNew));
+                    } else {
+                        newFeatureMap.put(name, String.valueOf(score));
+                    }
+                }
+            } catch (Exception e) {
+                log.error("featureBucket error: ", e);
+            }
+        }
+        return newFeatureMap;
+    }
+
+    private double getExpWeight(Map<String, String> featureMap,
+                                String expOldKey, double expOldThreshold,
+                                String expNewKey, double expNewThreshold,
+                                double expLowerWeight, double expUpperWeight, double expScale) {
+        try {
+            if (null != featureMap) {
+                double oldView = Double.parseDouble(featureMap.getOrDefault(expOldKey, "0"));
+                if (oldView < expOldThreshold) {
+                    double newView = Double.parseDouble(featureMap.getOrDefault(expNewKey, "0"));
+                    return getExpWeight(expLowerWeight, expUpperWeight, expScale, expNewThreshold, newView);
+                }
+            }
+        } catch (Exception e) {
+            log.error("getExpWeight error: ", e);
+        }
+        return 1.0;
+    }
+
+    private double getExpWeight(double lowerWeight, double upperWeight, double scale, double upperExp, double exp) {
+        if (exp >= upperExp) {
+            return 1.0;
+        }
+        double weight = Math.log(exp + 1) / scale;
+        return Math.min(Math.max(lowerWeight, weight), upperWeight);
+    }
+}

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

@@ -50,6 +50,7 @@ public class RankStrategyByWeight extends RankStrategyBasic {
                 adRankItem.getExt().put("isApi", "1");
                 adRankItem.getExt().put("isApi", "1");
             }
             }
             adRankItem.getExt().put("recallsources", dto.getRecallSources());
             adRankItem.getExt().put("recallsources", dto.getRecallSources());
+            fillAdRankItemExt(adRankItem, dto);
 
 
 
 
             adRankItems.add(adRankItem);
             adRankItems.add(adRankItem);