Browse Source

Merge branch 'refs/heads/dev-xym-update-branch' into pre-master

xueyiming 1 week ago
parent
commit
5d005f7243
15 changed files with 622 additions and 50 deletions
  1. 9 3
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/CommonResponse.java
  2. 53 0
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/manager/HolidayCalendarFeign.java
  3. 60 0
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/manager/fallback/HolidayCalendarFeignFallbackFactory.java
  4. 87 0
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/manager/service/HolidayService.java
  5. 72 0
      ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/manager/vo/HolidayCalendarVO.java
  6. 141 0
      ad-engine-server/src/main/java/com/tzld/piaoquan/ad/engine/server/controller/HolidayTestController.java
  7. 4 0
      ad-engine-server/src/main/resources/application-dev.yml
  8. 4 0
      ad-engine-server/src/main/resources/application-pre.yml
  9. 4 0
      ad-engine-server/src/main/resources/application-prod.yml
  10. 4 0
      ad-engine-server/src/main/resources/application-test.yml
  11. 8 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/feature/Feature.java
  12. 47 0
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/predict/impl/PredictModelServiceImpl.java
  13. 86 15
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBasic.java
  14. 22 16
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy683.java
  15. 21 16
      ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/score/strategy/RankStrategyBy688.java

+ 9 - 3
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/CommonResponse.java

@@ -11,7 +11,7 @@ import lombok.Data;
 @Data
 public class CommonResponse<T> {
     /**
-     * 返回状态码,0 表示业务成功
+     * 返回状态码,0 表示业务成功,200 也表示成功
      */
     private int code = 0;
     /**
@@ -27,6 +27,12 @@ public class CommonResponse<T> {
      */
     private String redirect;
 
-
-
+    /**
+     * 判断响应是否成功
+     *
+     * @return true-成功,false-失败
+     */
+    public boolean isSuccess() {
+        return code == 0 || code == 200;
+    }
 }

+ 53 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/manager/HolidayCalendarFeign.java

@@ -0,0 +1,53 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright © 2021 xrv <xrg@live.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.tzld.piaoquan.ad.engine.commons.feign.manager;
+
+import com.tzld.piaoquan.ad.engine.commons.feign.CommonResponse;
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.fallback.HolidayCalendarFeignFallbackFactory;
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.vo.HolidayCalendarVO;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+
+/**
+ * 节日日历服务 Feign 客户端
+ *
+ * @author ad-engine
+ * @since 2025-08-22
+ */
+@FeignClient(
+    value = "manager-new",
+    path = "/manager/platform/holidayCalendar",
+    fallbackFactory = HolidayCalendarFeignFallbackFactory.class
+)
+public interface HolidayCalendarFeign {
+    
+    /**
+     * 判断今日是否是节日
+     *
+     * @return 今日节日信息,如果不是节日则data为null
+     */
+    @GetMapping("/isHoliday")
+    CommonResponse<HolidayCalendarVO> isHoliday();
+}

+ 60 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/manager/fallback/HolidayCalendarFeignFallbackFactory.java

@@ -0,0 +1,60 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright © 2021 xrv <xrg@live.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.tzld.piaoquan.ad.engine.commons.feign.manager.fallback;
+
+import com.tzld.piaoquan.ad.engine.commons.feign.CommonResponse;
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.HolidayCalendarFeign;
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.vo.HolidayCalendarVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * 节日日历服务 Feign 客户端降级工厂
+ *
+ * @author ad-engine
+ * @since 2025-08-22
+ */
+@Slf4j
+@Component
+public class HolidayCalendarFeignFallbackFactory implements FallbackFactory<HolidayCalendarFeign> {
+
+    @Override
+    public HolidayCalendarFeign create(Throwable cause) {
+        return new HolidayCalendarFeign() {
+            @Override
+            public CommonResponse<HolidayCalendarVO> isHoliday() {
+                log.error("调用节日查询接口失败,执行降级逻辑", cause);
+                
+                CommonResponse<HolidayCalendarVO> response = new CommonResponse<>();
+                response.setCode(-1);
+                response.setMsg("节日查询服务不可用");
+                response.setData(null);
+                
+                return response;
+            }
+        };
+    }
+}

+ 87 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/manager/service/HolidayService.java

@@ -0,0 +1,87 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright © 2021 xrv <xrg@live.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.tzld.piaoquan.ad.engine.commons.feign.manager.service;
+
+import com.tzld.piaoquan.ad.engine.commons.feign.CommonResponse;
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.HolidayCalendarFeign;
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.vo.HolidayCalendarVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 节日查询服务
+ *
+ * @author ad-engine
+ * @since 2025-08-22
+ */
+@Slf4j
+@Service
+public class HolidayService {
+    
+    @Autowired
+    private HolidayCalendarFeign holidayCalendarFeign;
+    
+    /**
+     * 判断今日是否是节日
+     *
+     * @return 节日信息,如果不是节日返回null
+     */
+    public HolidayCalendarVO getTodayHoliday() {
+        try {
+            log.info("调用节日查询接口");
+            CommonResponse<HolidayCalendarVO> response = holidayCalendarFeign.isHoliday();
+            
+            if (response != null && response.isSuccess()) {
+                HolidayCalendarVO holiday = response.getData();
+                if (holiday != null) {
+                    log.info("今日是节日:{}", holiday.getHolidayName());
+                } else {
+                    log.info("今日不是节日");
+                }
+                return holiday;
+            } else {
+                log.error("节日查询接口调用失败,错误码:{},错误信息:{}", 
+                    response != null ? response.getCode() : "null", 
+                    response != null ? response.getMsg() : "response is null");
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("调用节日查询接口异常", e);
+            return null;
+        }
+    }
+    
+    /**
+     * 判断今日是否是节日(简化版)
+     *
+     * @return true-是节日,false-不是节日
+     */
+    public boolean isTodayHoliday() {
+        return getTodayHoliday() != null;
+    }
+    
+
+}

+ 72 - 0
ad-engine-commons/src/main/java/com/tzld/piaoquan/ad/engine/commons/feign/manager/vo/HolidayCalendarVO.java

@@ -0,0 +1,72 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright © 2021 xrv <xrg@live.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.tzld.piaoquan.ad.engine.commons.feign.manager.vo;
+
+import lombok.Data;
+
+/**
+ * 节日日历VO
+ *
+ * @author ad-engine
+ * @since 2025-08-22
+ */
+@Data
+public class HolidayCalendarVO {
+    
+    /**
+     * 节日ID
+     */
+    private Long id;
+    
+    /**
+     * 节日日期,格式:yyyy-MM-dd
+     */
+    private String solarDate;
+    
+    /**
+     * 节日名称
+     */
+    private String holidayName;
+    
+    /**
+     * 启用状态,1-启用,0-禁用
+     */
+    private Integer enabled;
+    
+    /**
+     * 备注信息
+     */
+    private String remark;
+    
+    /**
+     * 创建时间,格式:yyyy-MM-dd HH:mm:ss
+     */
+    private String createTime;
+    
+    /**
+     * 更新时间,格式:yyyy-MM-dd HH:mm:ss
+     */
+    private String updateTime;
+}

+ 141 - 0
ad-engine-server/src/main/java/com/tzld/piaoquan/ad/engine/server/controller/HolidayTestController.java

@@ -0,0 +1,141 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright © 2021 xrv <xrg@live.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package com.tzld.piaoquan.ad.engine.server.controller;
+
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.service.HolidayService;
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.vo.HolidayCalendarVO;
+import com.tzld.piaoquan.ad.engine.service.predict.impl.PredictModelServiceImpl;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 节日查询测试控制器
+ *
+ * @author ad-engine
+ * @since 2025-08-22
+ */
+@Slf4j
+@RestController
+@RequestMapping("/test/holiday")
+@Api(tags = "节日查询测试接口")
+public class HolidayTestController {
+    
+    @Autowired
+    private HolidayService holidayService;
+
+    @Autowired
+    private PredictModelServiceImpl predictModelService;
+    
+
+    /**
+     * 健康检查接口
+     */
+    @GetMapping("/health")
+    @ApiOperation("节日服务健康检查")
+    public Map<String, Object> healthCheck() {
+        Map<String, Object> result = new HashMap<>();
+
+        try {
+            log.info("开始节日服务健康检查,实际请求路径: /manager/platform/holidayCalendar/isHoliday");
+
+            boolean isHoliday = holidayService.isTodayHoliday();
+            result.put("status", "healthy");
+            result.put("service", "holiday-service");
+            result.put("context_path_info", "manager服务使用context-path=/manager");
+            result.put("feign_path", "/manager/platform/holidayCalendar/isHoliday");
+            result.put("timestamp", System.currentTimeMillis());
+            result.put("test_result", isHoliday ? "有节日数据" : "无节日数据");
+
+        } catch (Exception e) {
+            log.error("节日服务健康检查失败,请检查context-path配置", e);
+            result.put("status", "unhealthy");
+            result.put("service", "holiday-service");
+            result.put("context_path_info", "manager服务使用context-path=/manager");
+            result.put("feign_path", "/manager/platform/holidayCalendar/isHoliday");
+            result.put("timestamp", System.currentTimeMillis());
+            result.put("error", e.getMessage());
+            result.put("troubleshooting", "检查Feign客户端path是否包含/manager前缀");
+        }
+
+        return result;
+    }
+
+    /**
+     * 测试早晨节日时间判断函数
+     */
+    @GetMapping("/earlyMorningHoliday")
+    @ApiOperation("测试6-8点节日时间判断")
+    public Map<String, Object> testEarlyMorningHoliday() {
+        Map<String, Object> result = new HashMap<>();
+
+        try {
+            // 调用新增的函数
+            boolean isEarlyMorningHoliday = predictModelService.isEarlyMorningHoliday();
+
+            // 获取当前时间信息用于调试
+            int currentHour = com.tzld.piaoquan.ad.engine.commons.util.DateUtils.getCurrentHour();
+            boolean isHoliday = holidayService.isTodayHoliday();
+            String holidayName = "不是节日";
+            if (isHoliday) {
+                holidayName = holidayService.getTodayHoliday().getHolidayName();
+            }
+            result.put("success", true);
+            result.put("isEarlyMorningHoliday", isEarlyMorningHoliday);
+            result.put("currentHour", currentHour);
+            result.put("isInTimeRange", currentHour >= 6 && currentHour < 8);
+            result.put("isHoliday", isHoliday);
+            result.put("holidayName", holidayName);
+            result.put("timestamp", System.currentTimeMillis());
+
+            // 详细说明
+            if (isEarlyMorningHoliday) {
+                result.put("message", String.format("当前时间%d点在6-8点范围内且今日是节日(%s),返回true",
+                    currentHour, holidayName != null ? holidayName : "未知节日"));
+            } else if (currentHour >= 6 && currentHour < 8) {
+                result.put("message", String.format("当前时间%d点在6-8点范围内但今日不是节日,返回false", currentHour));
+            } else {
+                result.put("message", String.format("当前时间%d点不在6-8点范围内,返回false", currentHour));
+            }
+
+            log.info("早晨节日时间判断测试结果: {}", isEarlyMorningHoliday);
+
+        } catch (Exception e) {
+            log.error("早晨节日时间判断测试失败", e);
+            result.put("success", false);
+            result.put("error", e.getMessage());
+            result.put("isEarlyMorningHoliday", false);
+        }
+
+        return result;
+    }
+}

+ 4 - 0
ad-engine-server/src/main/resources/application-dev.yml

@@ -118,6 +118,10 @@ longvideo:
   feign:
     url: videotest-internal.yishihui.com
 
+manager:
+  feign:
+    url: https://testadmin.piaoquantv.com
+
 feign:
   client:
     config:

+ 4 - 0
ad-engine-server/src/main/resources/application-pre.yml

@@ -114,6 +114,10 @@ longvideo:
   feign:
     url: videopre-internal.piaoquantv.com
 
+manager:
+  feign:
+    url: https://preadmin.piaoquantv.com
+
 feign:
   client:
     config:

+ 4 - 0
ad-engine-server/src/main/resources/application-prod.yml

@@ -116,6 +116,10 @@ longvideo:
   feign:
     url: longvideoapi-internal.piaoquantv.com
 
+manager:
+  feign:
+    url: https://admin.piaoquantv.com
+
 feign:
   client:
     config:

+ 4 - 0
ad-engine-server/src/main/resources/application-test.yml

@@ -110,6 +110,10 @@ longvideo:
   feign:
     url: videotest-internal.yishihui.com
 
+manager:
+  feign:
+    url: https://testadmin.piaoquantv.com
+
 feign:
   client:
     config:

+ 8 - 0
ad-engine-service/src/main/java/com/tzld/piaoquan/ad/engine/service/feature/Feature.java

@@ -23,4 +23,12 @@ public class Feature {
     // k1:skuid、k2:表、v:特征值
     private Map<String, Map<String, Map<String, String>>> skuFeature = new HashMap<>();
 
+    // 合并方法,使用 putAll() 合并,且如果目标为空则取源
+    public void merge(Feature other) {
+        this.cidFeature.putAll(other.cidFeature);
+        this.videoFeature.putAll(other.videoFeature);
+        this.adVerFeature.putAll(other.adVerFeature);
+        this.userFeature.putAll(other.userFeature);
+        this.skuFeature.putAll(other.skuFeature);
+    }
 }

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

@@ -23,6 +23,7 @@ import com.tzld.piaoquan.ad.engine.service.predict.param.request.RoiPredictModel
 import com.tzld.piaoquan.ad.engine.service.predict.param.request.ThresholdPredictModelRequestParam;
 import com.tzld.piaoquan.ad.engine.service.predict.v2.*;
 import com.tzld.piaoquan.ad.engine.service.user.UserService;
+import com.tzld.piaoquan.ad.engine.commons.feign.manager.service.HolidayService;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.MapUtils;
 import org.apache.commons.lang3.StringUtils;
@@ -106,6 +107,10 @@ public class PredictModelServiceImpl implements PredictModelService {
     private AdRedisHelper adRedisHelper;
 
     // List<Integer> appIdArr = Arrays.asList(new Integer[]{0, 3, 4, 5, 6, 17, 18, 19, 21, 22});
+    @Autowired
+    private HolidayService holidayService;
+
+    List<Integer> appIdArr = Arrays.asList(new Integer[]{0, 3, 4, 5, 6, 17, 18, 19, 21, 22});
 
 
     public Map<String, Object> adPredict(ThresholdPredictModelRequestParam requestParam) {
@@ -447,6 +452,12 @@ public class PredictModelServiceImpl implements PredictModelService {
      * @return true-提前出,false-不提前出
      */
     private boolean isAdvanceShowAd() {
+
+        // 节日期间,6点开始出广告,不是节日,走下面的逻辑
+        if (isEarlyMorningHoliday()) {
+            return true;
+        }
+
         // 提前出广告是否生效,不生效表示不用提前出广告,返回false。默认生效
         if (!advanceShowAdSwitch) {
             return false;
@@ -464,4 +475,40 @@ public class PredictModelServiceImpl implements PredictModelService {
         log.info("提前出广告标识: {} ---> {}", redisKey, flag);
         return StringUtils.equals("1", flag);
     }
+
+    /**
+     * 判断当前时间是否在6点之后且为节日
+     *
+     * @return true-当前时间在6点之后且为节日,false-不满足条件
+     */
+    public boolean isEarlyMorningHoliday() {
+        try {
+            // 获取当前小时
+            int currentHour = DateUtils.getCurrentHour();
+
+            // 判断时间是否在6点之后
+            if (currentHour >= 6) {
+                log.info("当前时间{}点在6点之后,开始检查是否为节日", currentHour);
+
+                // 调用节日服务判断今日是否为节日
+                boolean isHoliday = holidayService.isTodayHoliday();
+
+                if (isHoliday) {
+                    log.info("当前时间{}点6点之后且今日是节日,返回true", currentHour);
+                    return true;
+                } else {
+                    log.info("当前时间{}点在6点之后但今日不是节日,返回false", currentHour);
+                    return false;
+                }
+            } else {
+                log.debug("当前时间{}点不在6点之后,返回false", currentHour);
+                return false;
+            }
+
+        } catch (Exception e) {
+            log.error("判断早晨节日时间异常", e);
+            // 异常情况下返回false,确保系统稳定性
+            return false;
+        }
+    }
 }

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

@@ -13,6 +13,7 @@ import com.tzld.piaoquan.ad.engine.commons.param.RankRecommendRequestParam;
 import com.tzld.piaoquan.ad.engine.commons.redis.AdRedisHelper;
 import com.tzld.piaoquan.ad.engine.commons.redis.AlgorithmRedisHelper;
 import com.tzld.piaoquan.ad.engine.commons.score.ScoreParam;
+import com.tzld.piaoquan.ad.engine.commons.thread.ThreadPoolFactory;
 import com.tzld.piaoquan.ad.engine.commons.util.DateUtils;
 import com.tzld.piaoquan.ad.engine.commons.util.ObjUtil;
 import com.tzld.piaoquan.ad.engine.service.entity.*;
@@ -27,6 +28,10 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 
 import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -113,6 +118,9 @@ public abstract class RankStrategyBasic implements RankStrategy {
     @Value("${filter.config.value:[]}")
     protected String filterConfigValue;
 
+    @Value("${feature.branch.config.value:500}")
+    protected Integer featureBranchConfigValue;
+
     @Autowired
     private FeatureService featureService;
     @Autowired
@@ -153,25 +161,88 @@ public abstract class RankStrategyBasic implements RankStrategy {
     }};
 
 
+//    protected Feature getFeature(ScoreParam param, RankRecommendRequestParam request) {
+//        List<AdPlatformCreativeDTO> adIdList = request.getAdIdList();
+//        List<String> cidList = adIdList.stream()
+//                .map(AdPlatformCreativeDTO::getCreativeId)
+//                .map(Object::toString)
+//                .collect(Collectors.toList());
+//
+//        List<String> adVerIdList = adIdList.stream()
+//                .map(AdPlatformCreativeDTO::getAdVerId)
+//                .filter(StringUtils::isNotBlank)
+//                .distinct()
+//                .collect(Collectors.toList());
+//
+//        List<Long> skuIdList = adIdList.stream()
+//                .map(AdPlatformCreativeDTO::getSkuId)
+//                .filter(Objects::nonNull)
+//                .distinct()
+//                .collect(Collectors.toList());
+//        return featureService.getFeature(cidList, adVerIdList, skuIdList, param);
+//    }
+
     protected Feature getFeature(ScoreParam param, RankRecommendRequestParam request) {
         List<AdPlatformCreativeDTO> adIdList = request.getAdIdList();
-        List<String> cidList = adIdList.stream()
-                .map(AdPlatformCreativeDTO::getCreativeId)
-                .map(Object::toString)
-                .collect(Collectors.toList());
+        Feature finalFeature = null;
 
-        List<String> adVerIdList = adIdList.stream()
-                .map(AdPlatformCreativeDTO::getAdVerId)
-                .filter(StringUtils::isNotBlank)
-                .distinct()
-                .collect(Collectors.toList());
+        // 分批处理 AdPlatformCreativeDTO 列表
+        List<List<AdPlatformCreativeDTO>> adIdBatches = partitionList(adIdList, featureBranchConfigValue);
 
-        List<Long> skuIdList = adIdList.stream()
-                .map(AdPlatformCreativeDTO::getSkuId)
-                .filter(Objects::nonNull)
-                .distinct()
-                .collect(Collectors.toList());
-        return featureService.getFeature(cidList, adVerIdList, skuIdList, param);
+        // 使用 ThreadPoolFactory 获取 DEFAULT 线程池
+        ExecutorService executorService = ThreadPoolFactory.defaultPool();  // 使用 DEFAULT 线程池
+        List<Callable<Feature>> tasks = new ArrayList<>();
+
+        for (List<AdPlatformCreativeDTO> batch : adIdBatches) {
+            // 提取批次中的 cidList、adVerIdList 和 skuIdList
+            List<String> cidList = batch.stream()
+                    .map(AdPlatformCreativeDTO::getCreativeId)
+                    .map(Object::toString)
+                    .collect(Collectors.toList());
+
+            List<String> adVerIdList = batch.stream()
+                    .map(AdPlatformCreativeDTO::getAdVerId)
+                    .filter(StringUtils::isNotBlank)
+                    .distinct()
+                    .collect(Collectors.toList());
+
+            List<Long> skuIdList = batch.stream()
+                    .map(AdPlatformCreativeDTO::getSkuId)
+                    .filter(Objects::nonNull)
+                    .distinct()
+                    .collect(Collectors.toList());
+
+            // 将每个批次的请求任务封装为 Callable
+            tasks.add(() -> featureService.getFeature(cidList, adVerIdList, skuIdList, param));
+        }
+
+        try {
+            // 执行所有的任务并等待所有任务完成
+            List<Future<Feature>> futures = executorService.invokeAll(tasks);
+
+            // 等待所有任务完成并合并结果
+            for (Future<Feature> future : futures) {
+                Feature batchFeature = future.get();  // 获取每个任务的结果
+                if (finalFeature == null) {
+                    finalFeature = batchFeature;
+                } else {
+                    // 合并特征
+                    finalFeature.merge(batchFeature);
+                }
+            }
+        } catch (InterruptedException | ExecutionException e) {
+            log.error("getFeature error", e);
+        }
+        return finalFeature;
+    }
+
+    // 辅助方法:将列表分成指定大小的批次
+    private <T> List<List<T>> partitionList(List<T> originalList, Integer batchSize) {
+        List<List<T>> batches = new ArrayList<>();
+        for (int i = 0; i < originalList.size(); i += batchSize) {
+            batches.add(originalList.subList(i, Math.min(i + batchSize, originalList.size())));
+        }
+        return batches;
     }
 
     protected Set<String> getNoApiAdVerIds() {

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

@@ -863,30 +863,36 @@ public class RankStrategyBy683 extends RankStrategyBasic {
             add("user_skuid_conver_30d");
             add("user_conver_ad_class");
             add("category_name");
+            add("material_md5");
         }};
     }
 
     private Map<String, String> featureBucket(Map<String, String> featureMap) {
         Map<String, String> newFeatureMap = new ConcurrentHashMap<>(featureMap.size());
         for (Map.Entry<String, String> entry : featureMap.entrySet()) {
-            String name = entry.getKey();
-            if (this.sparseFeatureSet.contains(name)) {
-                if (entry.getValue() != null) {
-                    newFeatureMap.put(name, entry.getValue());
+            try {
+                String name = entry.getKey();
+                if (this.sparseFeatureSet.contains(name)) {
+                    if (entry.getValue() != null) {
+                        newFeatureMap.put(name, entry.getValue());
+                    }
+                    continue;
                 }
-                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));
+
+                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;

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

@@ -875,30 +875,35 @@ public class RankStrategyBy688 extends RankStrategyBasic {
             add("user_skuid_conver_30d");
             add("user_conver_ad_class");
             add("category_name");
+            add("material_md5");
         }};
     }
 
     private Map<String, String> featureBucket(Map<String, String> featureMap) {
         Map<String, String> newFeatureMap = new ConcurrentHashMap<>(featureMap.size());
         for (Map.Entry<String, String> entry : featureMap.entrySet()) {
-            String name = entry.getKey();
-            if (this.sparseFeatureSet.contains(name)) {
-                if (entry.getValue() != null) {
-                    newFeatureMap.put(name, entry.getValue());
+            try {
+                String name = entry.getKey();
+                if (this.sparseFeatureSet.contains(name)) {
+                    if (entry.getValue() != null) {
+                        newFeatureMap.put(name, entry.getValue());
+                    }
+                    continue;
                 }
-                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));
+                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;