Quellcode durchsuchen

feat:添加获取首页tab的接口

zhaohaipeng vor 2 Wochen
Ursprung
Commit
76a01131aa

+ 23 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/common/enums/TabSource.java

@@ -0,0 +1,23 @@
+package com.tzld.piaoquan.recommend.server.common.enums;
+
+import lombok.Getter;
+import lombok.ToString;
+
+@Getter
+@ToString
+public enum TabSource {
+
+    profile("profile", "画像"),
+    share_cate2("share_cate2", "分享二级品类"),
+    return_cate2("return_cate2", "回流二级品类"),
+    default_hot("default_hot", "默认-热度"),
+    ;
+
+    private final String source;
+    private final String desc;
+
+    TabSource(String source, String desc) {
+        this.source = source;
+        this.desc = desc;
+    }
+}

+ 35 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/common/enums/TabType.java

@@ -0,0 +1,35 @@
+package com.tzld.piaoquan.recommend.server.common.enums;
+
+import lombok.Getter;
+import lombok.ToString;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Getter
+@ToString
+public enum TabType {
+
+    territory("territory", "地域"),
+    personalization("personalization", "个性化"),
+
+    other("other", "其他"),
+
+    ;
+    private final String type;
+    private final String desc;
+
+    private static final Map<String, TabType> typeMap = Arrays.stream(TabType.values())
+            .collect(Collectors.toMap(TabType::getType, Function.identity()));
+
+    TabType(String type, String desc) {
+        this.type = type;
+        this.desc = desc;
+    }
+
+    public static TabType getByType(String type) {
+        return typeMap.getOrDefault(type, other);
+    }
+}

+ 2 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/model/RecommendParam.java

@@ -69,5 +69,7 @@ public class RecommendParam {
     private String openGId;
 
     private String unionId;
+
+    private Tab tab;
 }
 

+ 14 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/model/Tab.java

@@ -0,0 +1,14 @@
+package com.tzld.piaoquan.recommend.server.model;
+
+import lombok.Data;
+
+@Data
+public class Tab {
+
+    private String key;
+
+    private String type;
+
+    private String source;
+
+}

+ 14 - 3
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/RecommendService.java

@@ -14,9 +14,11 @@ import com.tzld.piaoquan.recommend.server.feign.GrowthManagerFeign;
 import com.tzld.piaoquan.recommend.server.feign.model.CommonResponse;
 import com.tzld.piaoquan.recommend.server.feign.model.VideoCharacteristicInfo;
 import com.tzld.piaoquan.recommend.server.gen.common.Result;
+import com.tzld.piaoquan.recommend.server.gen.common.TabProto;
 import com.tzld.piaoquan.recommend.server.gen.recommend.*;
 import com.tzld.piaoquan.recommend.server.model.MachineInfo;
 import com.tzld.piaoquan.recommend.server.model.RecommendParam;
+import com.tzld.piaoquan.recommend.server.model.Tab;
 import com.tzld.piaoquan.recommend.server.model.Video;
 import com.tzld.piaoquan.recommend.server.repository.WxUserInfo;
 import com.tzld.piaoquan.recommend.server.repository.WxUserInfoRepository;
@@ -198,8 +200,7 @@ public class RecommendService {
             RecommendParam param = genRecommendParam(request, recommendType);
             long genRecommendParamTime = stopwatch.stop().elapsed(TimeUnit.MILLISECONDS);
             timerLogMapTL.get().put("genRecommendParamTime", genRecommendParamTime);
-            // log.info("genRecommendParam={},genRecommendParam cost={}", JSONUtils.toJson(param),
-            //         genRecommendParamTime);
+
             List<Video> videos = videoRecommend(param, request);
             stopwatch.reset().start();
             updateCache(request, param, videos);
@@ -470,6 +471,15 @@ public class RecommendService {
             param.setMachineInfo(machineInfo);
         }
 
+        if (request.hasTab()) {
+            TabProto tabProto = request.getTab();
+            Tab tab = new Tab();
+            tab.setKey(tabProto.getKey());
+            tab.setType(tabProto.getType());
+            tab.setSource(tabProto.getSource());
+            param.setTab(tab);
+        }
+
         if (StringUtils.isNotBlank(request.getCategoryId()) && StringUtils.isNumeric(request.getCategoryId())) {
             param.setCategoryId(Integer.parseInt(request.getCategoryId()));
         }
@@ -732,7 +742,7 @@ public class RecommendService {
         recallParam.setCreativeInfoFeature(param.getCreativeInfoFeature());
         recallParam.setOpenGId(param.getOpenGId());
         recallParam.setUnionId(param.getUnionId());
-
+        recallParam.setTab(param.getTab());
         return recallParam;
     }
 
@@ -767,6 +777,7 @@ public class RecommendService {
         rankParam.setCreativeInfoFeature(param.getCreativeInfoFeature());
         rankParam.setOpenGId(param.getOpenGId());
         rankParam.setUnionId(param.getUnionId());
+        rankParam.setTab(param.getTab());
         return rankParam;
     }
 

+ 3 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/RankParam.java

@@ -1,6 +1,7 @@
 package com.tzld.piaoquan.recommend.server.service.rank;
 
 import com.tzld.piaoquan.recommend.server.model.MachineInfo;
+import com.tzld.piaoquan.recommend.server.model.Tab;
 import com.tzld.piaoquan.recommend.server.service.funnel.FunnelContext;
 import com.tzld.piaoquan.recommend.server.service.rank.bo.UserSRBO;
 import com.tzld.piaoquan.recommend.server.service.rank.bo.UserShareReturnProfile;
@@ -54,4 +55,6 @@ public class RankParam {
     private Map<String, Map<String, String>> behaviorVideos;
     private String openGId;
     private String unionId;
+
+    private Tab tab;
 }

+ 2 - 1
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/rank/RankRouter.java

@@ -59,11 +59,12 @@ public class RankRouter {
             return strategyMap.get(relevantRank).rank(param);
         }
 
+
+
         // 裂变层实验,使用RootSessionId尾号进行实验
         for (Map.Entry<String, RankService> entry : strategyMap.entrySet()) {
             boolean hitExp = experimentService.judgeHitAlgoExp(param.getAppType(), param.getRootSessionId(), null, entry.getKey());
             if (hitExp) {
-                // log.info("apType: {}, rootSessionId: {} 命中实验: {} strategies: {}", param.getAppType(), param.getRootSessionId(), entry.getKey(), entry.getValue().getClass().getSimpleName());
                 return entry.getValue().rank(param);
             }
         }

+ 3 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallParam.java

@@ -1,5 +1,6 @@
 package com.tzld.piaoquan.recommend.server.service.recall;
 
+import com.tzld.piaoquan.recommend.server.model.Tab;
 import com.tzld.piaoquan.recommend.server.service.funnel.FunnelContext;
 import com.tzld.piaoquan.recommend.server.service.rank.bo.UserSRBO;
 import com.tzld.piaoquan.recommend.server.service.rank.bo.UserShareReturnProfile;
@@ -62,4 +63,6 @@ public class RecallParam {
     private Map<String, String> userNetworkSeqFeature;
 
     private Map<Long, Map<String, String>> userNetworkSeqVideoInfoMap;
+
+    private Tab tab;
 }

+ 13 - 0
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/recall/RecallService.java

@@ -3,6 +3,7 @@ package com.tzld.piaoquan.recommend.server.service.recall;
 import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
 import com.tzld.piaoquan.recommend.server.common.ThreadPoolFactory;
 import com.tzld.piaoquan.recommend.server.common.enums.AppTypeEnum;
+import com.tzld.piaoquan.recommend.server.common.enums.TabType;
 import com.tzld.piaoquan.recommend.server.model.Video;
 import com.tzld.piaoquan.recommend.server.service.ExperimentService;
 import com.tzld.piaoquan.recommend.server.service.funnel.FunnelContext;
@@ -124,6 +125,18 @@ public class RecallService implements ApplicationContextAware {
             return strategies;
         }
 
+        // 标签页的召回
+        if (Objects.nonNull(param.getTab())) {
+            TabType tabType = TabType.getByType(param.getTab().getType());
+            if (TabType.territory.equals(tabType)) {
+                strategies.add(strategyMap.get(CityRovnAllRovRecallStrategy.class.getSimpleName()));
+                strategies.add(strategyMap.get(CityRovnRecallStrategy.class.getSimpleName()));
+                strategies.add(strategyMap.get(RegionHRecallStrategy.class.getSimpleName()));
+                strategies.add(strategyMap.get(RegionRealtimeRecallStrategyV1.class.getSimpleName()));
+                return strategies;
+            }
+        }
+
         //1:通过“产品”控制“召回子策略”. 票圈美好祝福与内部tab只走祝福召回。APP只走固定列表。特殊配置的app只有固定召回列表。
         if (this.matchSpecialApp(param.getAppType())) {
             strategies.add(strategyMap.get(RegionRealtimeRecallStrategyV5Hand.class.getSimpleName()));

+ 117 - 7
recommend-server-service/src/main/java/com/tzld/piaoquan/recommend/server/service/tab/TabService.java

@@ -1,42 +1,138 @@
 package com.tzld.piaoquan.recommend.server.service.tab;
 
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
+import com.google.common.collect.Sets;
+import com.tzld.piaoquan.recommend.server.common.enums.TabSource;
+import com.tzld.piaoquan.recommend.server.common.enums.TabType;
 import com.tzld.piaoquan.recommend.server.gen.common.Result;
 import com.tzld.piaoquan.recommend.server.gen.common.TabProto;
 import com.tzld.piaoquan.recommend.server.gen.tab.TabRequest;
 import com.tzld.piaoquan.recommend.server.gen.tab.TabResponse;
 import com.tzld.piaoquan.recommend.server.model.TabParam;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Service
 public class TabService {
 
+
     @Autowired
     @Qualifier("redisTemplate")
     protected RedisTemplate<String, String> redisTemplate;
 
+    @ApolloJsonValue("${homepage.default.tab:[]}")
+    private List<String> homepageDefaultTab;
+
+    @ApolloJsonValue("${personalization.tab.size:2}")
+    private Integer personalizationTabSize;
+
+    /**
+     * 中国行政区划代码(GB/T 2260)省級前缀,前2位。
+     * 11-15 华北, 21-23 东北, 31-37 华东, 41-46 中南, 50-54 西南, 61-65 西北,
+     * 71 台湾, 81-82 香港/澳门
+     */
+    private static final Set<String> CHINA_PROVINCE_CODE_PREFIXES = Sets.newHashSet(
+            "11", "12", "13", "14", "15",
+            "21", "22", "23",
+            "31", "32", "33", "34", "35", "36", "37",
+            "41", "42", "43", "44", "45", "46",
+            "50", "51", "52", "53", "54",
+            "61", "62", "63", "64", "65",
+            "71", "81", "82"
+    );
+
+    private static final String redisKeyPrefox = "personalized_tab_candidate:";
+
+    private static final String PERSONALIZATION_TAB_SEPARATOR = "&";
+
     public TabResponse homepageTab(TabRequest request) {
         TabParam tabParam = this.requestCoverToParam(request);
 
         List<TabProto> tabProtos = new ArrayList<>();
-        tabProtos.add(TabProto.newBuilder().setKey(request.getProvince()).setTitle(request.getProvince()).setSource("").setType("territory").build());
-        tabProtos.add(TabProto.newBuilder().setKey("祝福").setTitle("祝福").setSource("share").setType("personalization").build());
-        tabProtos.add(TabProto.newBuilder().setKey("春节").setTitle("春节").setSource("share").setType("personalization").build());
+        tabProtos.addAll(this.territoryTab(tabParam));
+        tabProtos.addAll(this.personalizationTab(tabParam));
+
         return TabResponse.newBuilder()
                 .setResult(Result.newBuilder().setCode(1).setMessage("success"))
                 .addAllTab(tabProtos)
                 .build();
     }
 
+    private List<TabProto> territoryTab(TabParam tabParam) {
+        List<TabProto> tabProtos = new ArrayList<>();
+        // 非中国地域不返回地域Tab
+        if (isChinaRegion(tabParam.getProvinceCode())) {
+            tabProtos.add(TabProto.newBuilder().setKey(tabParam.getProvince()).setTitle(tabParam.getProvince()).setSource("").setType(TabType.territory.getType()).build());
+        }
+        return tabProtos;
+    }
+
+    private List<TabProto> personalizationTab(TabParam tabParam) {
+        List<TabProto> defaultTab = this.buildTabProto(homepageDefaultTab, TabSource.default_hot, TabType.personalization);
+        List<TabProto> tabProtos = new ArrayList<>(defaultTab);
+
+        if (StringUtils.isBlank(tabParam.getUnionId())) {
+            log.error("unionId is empty");
+            return tabProtos;
+        }
+
+        String key = redisKeyPrefox + tabParam.getUnionId();
+        String value = redisTemplate.opsForValue().get(key);
+        if (StringUtils.isBlank(value)) {
+            log.error("unionId is not exist");
+            return tabProtos;
+        }
+        List<List<String>> tabCollect = Arrays.stream(value.split("\t"))
+                .map(i -> Arrays.asList(i.split(PERSONALIZATION_TAB_SEPARATOR)))
+                .collect(Collectors.toList());
+        if (tabCollect.size() != 4) {
+            log.error("redis value size nq 4, {}", value);
+            return tabProtos;
+        }
+
+        List<TabProto> profileTab = this.buildTabProto(tabCollect.get(0), TabSource.profile, TabType.personalization);
+        List<TabProto> shareCate2Tab = this.buildTabProto(tabCollect.get(0), TabSource.share_cate2, TabType.personalization);
+        List<TabProto> returnCate2Tab = this.buildTabProto(tabCollect.get(0), TabSource.return_cate2, TabType.personalization);
+        List<TabProto> defaultHotTab = this.buildTabProto(tabCollect.get(0), TabSource.default_hot, TabType.personalization);
+
+        // 默认兜底Tab
+        tabProtos.addAll(0, defaultHotTab);
+        tabProtos.addAll(0, returnCate2Tab);
+        tabProtos.addAll(0, shareCate2Tab);
+        tabProtos.addAll(0, profileTab);
+        return tabProtos.subList(0, Math.min(tabProtos.size(), personalizationTabSize));
+    }
+
+    private List<TabProto> buildTabProto(List<String> tabNames, TabSource source, TabType type) {
+        if (CollectionUtils.isEmpty(tabNames)) {
+            return Collections.emptyList();
+        }
+        List<TabProto> tabProtos = new ArrayList<>();
+        for (String tabName : tabNames) {
+            if (StringUtils.isBlank(tabName)) {
+                continue;
+            }
+            tabProtos.add(
+                    TabProto.newBuilder()
+                            .setKey(tabName)
+                            .setTitle(tabName)
+                            .setSource(source.getSource())
+                            .setType(type.getType())
+                            .build()
+            );
+        }
+        return tabProtos;
+    }
+
     private TabParam requestCoverToParam(TabRequest request) {
         TabParam param = new TabParam();
         param.setRequestId(request.getRequestId());
@@ -61,4 +157,18 @@ public class TabService {
         return param;
     }
 
+    /**
+     * 判断行政区划代码是否属于中国。
+     * 通过省份代码前2位匹配 GB/T 2260 中的中国省级前缀来校验。
+     *
+     * @param provinceCode 省份代码(2位或6位均可)
+     * @return true 表示是中国地域
+     */
+    private boolean isChinaRegion(String provinceCode) {
+        if (StringUtils.isBlank(provinceCode) || provinceCode.length() < 2) {
+            return false;
+        }
+        return CHINA_PROVINCE_CODE_PREFIXES.contains(provinceCode.substring(0, 2));
+    }
+
 }