RecommendService.java 6.68 KB
Newer Older
1 2 3 4 5 6
package com.tanpu.community.service;

import com.tanpu.common.util.JsonUtil;
import com.tanpu.community.api.beans.qo.ThemeAnalysDO;
import com.tanpu.community.api.beans.resp.PythonResponse;
import com.tanpu.community.dao.entity.community.ThemeEntity;
7
import com.tanpu.community.util.BizUtils;
8 9
import com.tanpu.community.util.ConvertUtil;
import lombok.extern.slf4j.Slf4j;
10
import org.apache.commons.collections4.ListUtils;
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RecommendService {

    @Value("${recommend.python.enable}")
    public String enablePython;


    @Value("${recommend.python.url}")
    public String pythonUrl;
    @Value("${recommend.ratio.hot}")
    public Integer hotRatio;
    @Value("${recommend.ratio.new}")
    public Integer newRatio;
    @Value("${recommend.ratio.python}")
    public Integer pythonRatio;


    @Autowired
    private RankService rankService;

    @Autowired
    private ThemeService themeService;

44
    @Autowired
刘基明's avatar
刘基明 committed
45
    private VisitLogService visitLogService;
46

张辰's avatar
张辰 committed
47
    // 最新
48
    private List<ThemeAnalysDO> recentThemeList = new ArrayList<>();
张辰's avatar
张辰 committed
49
    // 推荐
50 51
    private Map<String, List<String>> recommondList = new HashMap<>();

张辰's avatar
张辰 committed
52 53
    public List<String> getRecommendThemes(Integer pageStart, Integer pageSize, String userId, List<String> excludeIds) {
        // 最热话题,筛掉用户发表的 & 最近看过的 & excludeIds
张辰's avatar
张辰 committed
54
        List<String> hotThemeIds = rankService.getHotestThemes().stream()
张辰's avatar
张辰 committed
55 56 57 58 59
                .filter(theme -> {
                    return !excludeIds.contains(theme.getThemeId()) && !userId.equals(theme.getAuthorId());
                })
                .map(ThemeAnalysDO::getThemeId).collect(Collectors.toList());
        hotThemeIds = BizUtils.subList(hotThemeIds, pageStart, pageSize);
60

张辰's avatar
张辰 committed
61
        //最新话题,筛掉用户发表的 & 最近看过的 & excludeIds
62
        List<String> newThemeIds = getNewestThemes().stream()
张辰's avatar
张辰 committed
63 64 65 66 67
                .filter(theme -> {
                    return !excludeIds.contains(theme.getThemeId()) && !userId.equals(theme.getAuthorId());
                })
                .map(ThemeAnalysDO::getThemeId).collect(Collectors.toList());
        newThemeIds = BizUtils.subList(newThemeIds, pageStart, pageSize);
68 69

        //推荐话题
张辰's avatar
张辰 committed
70 71 72 73 74 75 76 77
        List<String> recThemeIds = getPythonRecommendList(userId).stream()
                .filter(id -> {
                    return !excludeIds.contains(id);
                }).collect(Collectors.toList());
        recThemeIds = BizUtils.subList(recThemeIds, pageStart, pageSize);

        // merge
        return mergeRecommend(hotThemeIds, newThemeIds, recThemeIds);
78 79
    }

80 81
    // 获取最新话题
    public List<ThemeAnalysDO> getNewestThemes() {
82 83 84 85 86 87
        if (recentThemeList.size() == 0) {
            refreshNewestThemes();
        }
        return recentThemeList;
    }

88
    // 从数据库查询最新主题
89 90 91 92 93 94 95 96 97 98 99 100 101 102
    public void refreshNewestThemes() {
        List<ThemeEntity> themeEntities = themeService.queryLatestThemes(100);
        this.recentThemeList = ConvertUtil.themeEntityToAnalysDOs(themeEntities);
    }

    //查询python计算推荐列表
    public List<String> getPythonRecommendList(String userId) {
        if (recommondList.containsKey(userId)) {
            return recommondList.get(userId);
        } else {
            return refreshPythonRecommendList(userId);
        }
    }

103
    //HTTP查询python推荐主题 python返回最多50个
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
    public List<String> refreshPythonRecommendList(String userId) {
        if (!"true".equals(enablePython)) {
            return Collections.emptyList();
        }
        RestTemplate restTemplate = new RestTemplate();
        HashMap<String, String> param = new HashMap<>();
        param.put("user_id", userId);
        try {
            String response = restTemplate.getForObject(pythonUrl, String.class, param);
            PythonResponse pythonResponse = JsonUtil.toBean(response, PythonResponse.class);
            recommondList.put(userId, pythonResponse.getAttributes());
            return pythonResponse.getAttributes();
        } catch (Exception e) {
            log.error("调用python失败");
            return Collections.emptyList();
        }


    }

刘基明's avatar
刘基明 committed
124 125
    // 合并,去重
    private List<String> mergeList(List<String> hotThemeIds, List<String> newThemeIds, List<String> recThemeIds, Set<String> set) {
刘基明's avatar
刘基明 committed
126
        ArrayList<String> result = new ArrayList<>();
刘基明's avatar
刘基明 committed
127
        // 3个集合的指针
刘基明's avatar
刘基明 committed
128 129 130 131 132 133 134 135 136 137
        Integer hotIdx = 0;
        Integer newIdx = 0;
        Integer recIdx = 0;
        while (hotThemeIds.size() > hotIdx || newThemeIds.size() > newIdx || recThemeIds.size() > recIdx) {
            int hotTimes = hotRatio;
            int newTimes = newRatio;
            int recTimes = pythonRatio;
            String id;
            while (hotTimes > 0 && hotThemeIds.size() > hotIdx) {
                id = hotThemeIds.get(hotIdx);
刘基明's avatar
刘基明 committed
138 139 140 141 142
                if (!set.contains(id)){
                    result.add(id);
                    set.add(id);
                }

刘基明's avatar
刘基明 committed
143 144 145 146 147
                hotIdx++;
                hotTimes--;
            }
            while (newTimes > 0 && newThemeIds.size() > newIdx) {
                id = newThemeIds.get(newIdx);
刘基明's avatar
刘基明 committed
148 149 150 151 152
                if (!set.contains(id)){
                    result.add(id);
                    set.add(id);
                }

刘基明's avatar
刘基明 committed
153 154 155 156 157
                newIdx++;
                newTimes--;
            }
            while (recTimes > 0 && recThemeIds.size() > recIdx) {
                id = recThemeIds.get(recIdx);
刘基明's avatar
刘基明 committed
158 159 160 161 162
                if (!set.contains(id)){
                    result.add(id);
                    set.add(id);
                }

刘基明's avatar
刘基明 committed
163 164 165 166 167 168 169 170
                recIdx++;
                recTimes--;
            }

        }
        return result;
    }

171 172 173 174 175 176 177 178 179 180 181
    // 按照 6,3,1的比例
    private List<String> mergeRecommend(List<String> hotIds, List<String> newestIds, List<String> recmdIds) {
        List<String> retList = new ArrayList<>();

        int round = 0;
        while (true) {
            int hotStart = round * 6;
            int newestStart = round * 3;
            int recmdStart = round;
            if (hotStart >= hotIds.size() && newestStart >= newestIds.size() && recmdStart >= recmdIds.size()) {
                break;
182
            }
183 184 185
            retList.addAll(BizUtils.subList(hotIds, hotStart, hotStart + 6));
            retList.addAll(BizUtils.subList(newestIds, newestStart, newestStart + 3));
            retList.addAll(BizUtils.subList(recmdIds, recmdStart, recmdStart + 1));
186

187
            round++;
188
        }
189
       return retList;
190 191 192 193
    }


}