Просмотр исходного кода

推荐池 category 过滤按身份分支:内部(2)/代理(3) 放开「早中晚好」,「节日祝福」仍过滤;合作方/其他维持现状(两个都过滤);「公众号合作-Daily-自选」渠道白名单语义保留(所有身份不过滤)。原 XML 硬编码 NOT IN + 渠道例外迁至 Service `resolveExcludeCategories`,XML 参数化 `<foreach>`。搜索路径(selectSearchWhitelist)维持现状不动。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
刘立冬 14 часов назад
Родитель
Сommit
ecd80b3d3a

+ 7 - 1
api-module/src/main/java/com/tzld/piaoquan/api/dao/mapper/contentplatform/ext/ContentPlatformDemandVideoMapperExt.java

@@ -44,6 +44,11 @@ public interface ContentPlatformDemandVideoMapperExt {
      * dimension:等值过滤;dimensionExclude:排除该 dimension(包含 NULL 视为通过);
      * demandFilterSortStrategyLike:对 demand_filter_sort_strategy 做 LIKE 过滤;
      * excludeSelfTitle=true 时过滤掉 video.title == demand_content_title 的行。
+     * excludeCategories:要剔除的 category 列表(SQL `NOT IN`,NULL 视为通过);为空/null 不做品类过滤。
+     *   身份相关规则由 Service 层 `resolveExcludeCategories` 决定:
+     *   - 渠道「公众号合作-Daily-自选」对所有身份返回空(白名单)
+     *   - 内部/代理:["节日祝福"]
+     *   - 合作方/其他:["早中晚好","节日祝福"]
      */
     List<ContentPlatformDemandVideo> selectForRecommend(@Param("dt") String dt,
                                                        @Param("channelName") String channelName,
@@ -58,7 +63,8 @@ public interface ContentPlatformDemandVideoMapperExt {
                                                        @Param("matchMethod") String matchMethod,
                                                        @Param("crowdPackage") String crowdPackage,
                                                        @Param("limit") int limit,
-                                                       @Param("excludeSelfTitle") boolean excludeSelfTitle);
+                                                       @Param("excludeSelfTitle") boolean excludeSelfTitle,
+                                                       @Param("excludeCategories") List<String> excludeCategories);
 
     List<String> selectDistinctCrowdPackages(@Param("dt") String dt, @Param("channelName") String channelName);
 

+ 25 - 6
api-module/src/main/java/com/tzld/piaoquan/api/service/contentplatform/impl/ContentPlatformPlanServiceImpl.java

@@ -709,6 +709,22 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
         return null;
     }
 
+    /**
+     * 推荐池剔除的 category 列表(身份相关)。
+     * - 渠道「公众号合作-Daily-自选」:所有身份返回空(白名单语义保留,等价于不做 NOT IN)
+     * - 其他渠道:内部(2)/代理(3) 仅过滤「节日祝福」;合作方(1)/其他 过滤「早中晚好」+「节日祝福」
+     * 仅作用于推荐池(selectForRecommend);搜索路径维持现状不做 category 过滤。
+     */
+    private List<String> resolveExcludeCategories(String channelName, ContentPlatformAccount user) {
+        if (CHANNEL_NAME_GZH_DAILY.equals(channelName)) return Collections.emptyList();
+        Integer type = user == null ? null : user.getType();
+        boolean privileged = Objects.equals(type, ContentPlatformAccountTypeEnum.INTERNAL.getVal())
+                || Objects.equals(type, ContentPlatformAccountTypeEnum.AGENT.getVal());
+        return privileged
+                ? Collections.singletonList("节日祝福")
+                : Arrays.asList("早中晚好", "节日祝福");
+    }
+
     @Override
     public Page<VideoContentItemVO> getVideoContentList(VideoContentListParam param) {
         ContentPlatformAccount user = LoginUserContext.getUser();
@@ -1016,14 +1032,15 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
 
         String category = StringUtils.hasText(param.getCategory()) ? param.getCategory() : null;
         String crowdPackage = StringUtils.hasText(param.getCrowdPackage()) ? param.getCrowdPackage() : null;
+        List<String> excludeCategories = resolveExcludeCategories(channelName, user);
         // priorScene 池新识别:demand_strategy='人群需求' AND match_method='场景已看视频'(0519+ 起,旧 demand_strategy='人群需求-场景' 已迁走)
         List<ContentPlatformDemandVideo> rows = demandVideoMapperExt.selectForRecommend(
-                dt, channelName, crowdSegment, DEMAND_STRATEGY_PRIOR, null, null, null, ghName, null, category, MATCH_METHOD_PRIOR_SCENE, crowdPackage, limit, false);
+                dt, channelName, crowdSegment, DEMAND_STRATEGY_PRIOR, null, null, null, ghName, null, category, MATCH_METHOD_PRIOR_SCENE, crowdPackage, limit, false, excludeCategories);
         // 跨渠道退化:仅合作类渠道(企微/即转/Daily-自选)主查 crowd_segment 0 行时去掉合作方代码再试一次,
         // 仍保留 ghName,避免公众号入口结果跨账号串号。投流/服务号类渠道 crowdSegment 已是 null,主查与本路径等价,不会进入。
         if (channelName != null && crowdSegment != null && rows.isEmpty()) {
             rows = demandVideoMapperExt.selectForRecommend(
-                    dt, channelName, null, DEMAND_STRATEGY_PRIOR, null, null, null, ghName, null, category, MATCH_METHOD_PRIOR_SCENE, crowdPackage, limit, false);
+                    dt, channelName, null, DEMAND_STRATEGY_PRIOR, null, null, null, ghName, null, category, MATCH_METHOD_PRIOR_SCENE, crowdPackage, limit, false, excludeCategories);
         }
         // 1. 同 video_id 取 total_rov 最大的代表行(SQL 已排序,putIfAbsent 保留首次)
         LinkedHashMap<Long, ContentPlatformDemandVideo> bestPerVideo = new LinkedHashMap<>();
@@ -1088,13 +1105,14 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
 
         String category = StringUtils.hasText(param.getCategory()) ? param.getCategory() : null;
         String crowdPackage = StringUtils.hasText(param.getCrowdPackage()) ? param.getCrowdPackage() : null;
+        List<String> excludeCategories = resolveExcludeCategories(channelName, user);
         List<ContentPlatformDemandVideo> rows = demandVideoMapperExt.selectForRecommend(
-                dt, channelName, crowdSegment, DEMAND_STRATEGY_PRIOR, dimension, null, null, ghName, null, category, MATCH_METHOD_PRIOR, crowdPackage, fetchLimit, false);
+                dt, channelName, crowdSegment, DEMAND_STRATEGY_PRIOR, dimension, null, null, ghName, null, category, MATCH_METHOD_PRIOR, crowdPackage, fetchLimit, false, excludeCategories);
 
         // 跨渠道退化:见 fetchPriorSceneCandidates 注释。仅合作类渠道触发,保留 ghName 防串号。
         if (channelName != null && crowdSegment != null && rows.isEmpty()) {
             rows = demandVideoMapperExt.selectForRecommend(
-                    dt, channelName, null, DEMAND_STRATEGY_PRIOR, dimension, null, null, ghName, null, category, MATCH_METHOD_PRIOR, crowdPackage, fetchLimit, false);
+                    dt, channelName, null, DEMAND_STRATEGY_PRIOR, dimension, null, null, ghName, null, category, MATCH_METHOD_PRIOR, crowdPackage, fetchLimit, false, excludeCategories);
         }
 
         rows = rows.stream()
@@ -1164,15 +1182,16 @@ public class ContentPlatformPlanServiceImpl implements ContentPlatformPlanServic
 
         String category = StringUtils.hasText(param.getCategory()) ? param.getCategory() : null;
         String crowdPackage = StringUtils.hasText(param.getCrowdPackage()) ? param.getCrowdPackage() : null;
+        List<String> excludeCategories = resolveExcludeCategories(channelName, user);
         // posterior 池加 match_method='视频库_解构特征_向量相似匹配' 兜底,防止未来上游对优质相似分量出别的 match_method 值后污染本池
         // 优质相似池:drive_dimension_time 一律不限制(含主查与退化路径),避免仅「昨日」窗口召回过少。
         List<ContentPlatformDemandVideo> rows = demandVideoMapperExt.selectForRecommend(
-                dt, channelName, crowdSegment, DEMAND_STRATEGY_POSTERIOR, null, null, null, ghName, null, category, MATCH_METHOD_PRIOR, crowdPackage, fetchLimit, true);
+                dt, channelName, crowdSegment, DEMAND_STRATEGY_POSTERIOR, null, null, null, ghName, null, category, MATCH_METHOD_PRIOR, crowdPackage, fetchLimit, true, excludeCategories);
 
         // 跨渠道退化:见 fetchPriorSceneCandidates 注释。仅合作类渠道触发,保留 ghName 防串号。
         if (channelName != null && crowdSegment != null && rows.isEmpty()) {
             rows = demandVideoMapperExt.selectForRecommend(
-                    dt, channelName, null, DEMAND_STRATEGY_POSTERIOR, null, null, null, ghName, null, category, MATCH_METHOD_PRIOR, crowdPackage, fetchLimit, true);
+                    dt, channelName, null, DEMAND_STRATEGY_POSTERIOR, null, null, null, ghName, null, category, MATCH_METHOD_PRIOR, crowdPackage, fetchLimit, true, excludeCategories);
         }
 
         // 近 7 日 rov 下限,与 prior 池一致(DEMAND_MIN_ROV,统一到 0.03)

+ 6 - 2
api-module/src/main/resources/mapper/contentplatform/ext/ContentPlatformDemandVideoMapperExt.xml

@@ -110,8 +110,12 @@
                match_text, title, cover, video, experiment_id, scene_sum_rov, status, create_timestamp, update_timestamp
         FROM content_platform_demand_video
         WHERE dt = #{dt} AND status = 1
-        <if test="channelName == null || channelName != '公众号合作-Daily-自选'">
-        AND (category IS NULL OR category NOT IN ('早中晚好','节日祝福'))
+        <if test="excludeCategories != null and excludeCategories.size() > 0">
+            AND (category IS NULL OR category NOT IN
+            <foreach collection="excludeCategories" item="c" open="(" close=")" separator=",">
+                #{c}
+            </foreach>
+            )
         </if>
         <if test="channelName != null and channelName != ''">
             AND channel_name = #{channelName}