RecommendService.java 7.15 KB
Newer Older
1 2
package com.tanpu.community.service;

王亚雷's avatar
王亚雷 committed
3
import com.google.common.collect.Lists;
4 5 6 7
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;
8
import com.tanpu.community.util.BizUtils;
9
import com.tanpu.community.util.ConvertUtil;
刘基明's avatar
刘基明 committed
10
import com.tanpu.community.util.TimeUtils;
11
import lombok.extern.slf4j.Slf4j;
王亚雷's avatar
王亚雷 committed
12
import org.apache.commons.lang3.StringUtils;
13 14 15 16 17
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;

刘基明's avatar
刘基明 committed
18
import javax.annotation.Resource;
刘基明's avatar
刘基明 committed
19
import java.time.LocalDateTime;
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
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;

47

刘基明's avatar
刘基明 committed
48 49 50
    @Resource
    private RestTemplate restTemplate;

张辰's avatar
张辰 committed
51
    // 最新
52
    private List<ThemeAnalysDO> recentThemeList = new ArrayList<>();
张辰's avatar
张辰 committed
53
    // 推荐
54 55
    private Map<String, List<String>> recommondList = new HashMap<>();

刘基明's avatar
刘基明 committed
56
    public List<String> getRecommendThemes(Integer pageStart, Integer pageSize, String userId, List<String> excludeIds, LocalDateTime timeAfter) {
张辰's avatar
张辰 committed
57
        // 最热话题,筛掉用户发表的 & 最近看过的 & excludeIds
张辰's avatar
张辰 committed
58
        List<String> hotThemeIds = rankService.getHotestThemes().stream()
张辰's avatar
张辰 committed
59
                .filter(theme -> {
刘基明's avatar
刘基明 committed
60 61
                    // 暂时不过滤用户自己发的 !userId.equals(theme.getAuthorId());
                    return !excludeIds.contains(theme.getThemeId());
张辰's avatar
张辰 committed
62 63
                })
                .map(ThemeAnalysDO::getThemeId).collect(Collectors.toList());
刘基明's avatar
刘基明 committed
64
        hotThemeIds = BizUtils.subList(hotThemeIds, 0, pageSize);
65

刘基明's avatar
刘基明 committed
66 67
        //最新话题,筛掉用户发表的 & 最近看过的 & excludeIds && 上次查询后发帖的
        long margin = TimeUtils.calMinuteTillNow(timeAfter);
68
        List<String> newThemeIds = getNewestThemes().stream()
张辰's avatar
张辰 committed
69
                .filter(theme -> {
刘基明's avatar
刘基明 committed
70
                    // 暂时不过滤用户自己发的 !userId.equals(theme.getAuthorId());
刘基明's avatar
刘基明 committed
71
                    return !excludeIds.contains(theme.getThemeId()) && theme.getMinutesTillNow() > margin;
张辰's avatar
张辰 committed
72 73
                })
                .map(ThemeAnalysDO::getThemeId).collect(Collectors.toList());
刘基明's avatar
刘基明 committed
74
        newThemeIds = BizUtils.subList(newThemeIds, 0, pageSize);
75 76

        //推荐话题
王亚雷's avatar
王亚雷 committed
77 78 79 80 81 82 83 84 85 86
        List<String> recThemeIds;
        if (StringUtils.isNotEmpty(userId)) {
             recThemeIds = getPythonRecommendList(userId).stream()
                    .filter(id -> {
                        return !excludeIds.contains(id);
                    }).collect(Collectors.toList());
            recThemeIds = BizUtils.subList(recThemeIds, pageStart, pageSize);
        } else {
            recThemeIds = Lists.newArrayListWithCapacity(0);
        }
张辰's avatar
张辰 committed
87 88 89

        // merge
        return mergeRecommend(hotThemeIds, newThemeIds, recThemeIds);
90 91
    }

92 93
    // 获取最新话题
    public List<ThemeAnalysDO> getNewestThemes() {
94 95 96 97 98 99
        if (recentThemeList.size() == 0) {
            refreshNewestThemes();
        }
        return recentThemeList;
    }

100
    // 从数据库查询最新主题
101 102 103 104 105 106 107 108 109 110 111 112 113 114
    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);
        }
    }

115
    //HTTP查询python推荐主题 python返回最多50个
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
    public List<String> refreshPythonRecommendList(String userId) {
        if (!"true".equals(enablePython)) {
            return Collections.emptyList();
        }
        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
135 136
    // 合并,去重
    private List<String> mergeList(List<String> hotThemeIds, List<String> newThemeIds, List<String> recThemeIds, Set<String> set) {
刘基明's avatar
刘基明 committed
137
        ArrayList<String> result = new ArrayList<>();
刘基明's avatar
刘基明 committed
138
        // 3个集合的指针
刘基明's avatar
刘基明 committed
139 140 141 142 143 144 145 146
        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;
刘基明's avatar
刘基明 committed
147 148
            while (newTimes > 0 && newThemeIds.size() > newIdx) {
                id = newThemeIds.get(newIdx);
刘基明's avatar
刘基明 committed
149
                if (!set.contains(id)) {
刘基明's avatar
刘基明 committed
150 151 152 153
                    result.add(id);
                    set.add(id);
                }

刘基明's avatar
刘基明 committed
154 155
                newIdx++;
                newTimes--;
刘基明's avatar
刘基明 committed
156
            }
刘基明's avatar
刘基明 committed
157 158 159

            while (hotTimes > 0 && hotThemeIds.size() > hotIdx) {
                id = hotThemeIds.get(hotIdx);
刘基明's avatar
刘基明 committed
160
                if (!set.contains(id)) {
刘基明's avatar
刘基明 committed
161 162 163 164
                    result.add(id);
                    set.add(id);
                }

刘基明's avatar
刘基明 committed
165 166
                hotIdx++;
                hotTimes--;
刘基明's avatar
刘基明 committed
167
            }
刘基明's avatar
刘基明 committed
168

刘基明's avatar
刘基明 committed
169 170
            while (recTimes > 0 && recThemeIds.size() > recIdx) {
                id = recThemeIds.get(recIdx);
刘基明's avatar
刘基明 committed
171
                if (!set.contains(id)) {
刘基明's avatar
刘基明 committed
172 173 174 175
                    result.add(id);
                    set.add(id);
                }

刘基明's avatar
刘基明 committed
176 177 178 179 180 181 182 183
                recIdx++;
                recTimes--;
            }

        }
        return result;
    }

184 185 186 187 188
    // 按照 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) {
刘基明's avatar
刘基明 committed
189 190 191 192

            int newestStart = round * newRatio;
            int hotStart = round * hotRatio;
            int recmdStart = round * pythonRatio;
193 194
            if (hotStart >= hotIds.size() && newestStart >= newestIds.size() && recmdStart >= recmdIds.size()) {
                break;
195
            }
刘基明's avatar
刘基明 committed
196 197 198
            retList.addAll(BizUtils.subList(newestIds, newestStart, newRatio));
            retList.addAll(BizUtils.subList(hotIds, hotStart, hotRatio));
            retList.addAll(BizUtils.subList(recmdIds, recmdStart, pythonRatio));
199

200
            round++;
201
        }
刘基明's avatar
刘基明 committed
202
        return retList;
203 204 205 206
    }


}