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; import com.tanpu.community.util.BizUtils; import com.tanpu.community.util.ConvertUtil; import lombok.extern.slf4j.Slf4j; 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; // 最新 private List<ThemeAnalysDO> recentThemeList = new ArrayList<>(); // 推荐 private Map<String, List<String>> recommondList = new HashMap<>(); public List<String> getRecommendThemes(Integer pageStart, Integer pageSize, String userId, List<String> excludeIds) { // 最热话题,筛掉用户发表的 & 最近看过的 & excludeIds List<String> hotThemeIds = rankService.getHotestThemes().stream() .filter(theme -> { return !excludeIds.contains(theme.getThemeId()) && !userId.equals(theme.getAuthorId()); }) .map(ThemeAnalysDO::getThemeId).collect(Collectors.toList()); hotThemeIds = BizUtils.subList(hotThemeIds, pageStart, pageSize); //最新话题,筛掉用户发表的 & 最近看过的 & excludeIds List<String> newThemeIds = getNewestThemes().stream() .filter(theme -> { return !excludeIds.contains(theme.getThemeId()) && !userId.equals(theme.getAuthorId()); }) .map(ThemeAnalysDO::getThemeId).collect(Collectors.toList()); newThemeIds = BizUtils.subList(newThemeIds, pageStart, pageSize); //推荐话题 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); } // 获取最新话题 public List<ThemeAnalysDO> getNewestThemes() { if (recentThemeList.size() == 0) { refreshNewestThemes(); } return recentThemeList; } // 从数据库查询最新主题 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); } } //HTTP查询python推荐主题 python返回最多50个 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(); } } // 合并,去重 private List<String> mergeList(List<String> hotThemeIds, List<String> newThemeIds, List<String> recThemeIds, Set<String> set) { ArrayList<String> result = new ArrayList<>(); // 3个集合的指针 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 (newTimes > 0 && newThemeIds.size() > newIdx) { id = newThemeIds.get(newIdx); if (!set.contains(id)) { result.add(id); set.add(id); } newIdx++; newTimes--; } while (hotTimes > 0 && hotThemeIds.size() > hotIdx) { id = hotThemeIds.get(hotIdx); if (!set.contains(id)) { result.add(id); set.add(id); } hotIdx++; hotTimes--; } while (recTimes > 0 && recThemeIds.size() > recIdx) { id = recThemeIds.get(recIdx); if (!set.contains(id)) { result.add(id); set.add(id); } recIdx++; recTimes--; } } return result; } // 按照 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 newestStart = round * newRatio; int hotStart = round * hotRatio; int recmdStart = round * pythonRatio; if (hotStart >= hotIds.size() && newestStart >= newestIds.size() && recmdStart >= recmdIds.size()) { break; } retList.addAll(BizUtils.subList(newestIds, newestStart, newRatio)); retList.addAll(BizUtils.subList(hotIds, hotStart, hotRatio)); retList.addAll(BizUtils.subList(recmdIds, recmdStart, pythonRatio)); round++; } return retList; } }