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.ConvertUtil; import lombok.extern.slf4j.Slf4j; 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; @Autowired private VisitSummaryService visitSummaryService; // 最新 private List<ThemeAnalysDO> recentThemeList = new ArrayList<>(); // 推荐 private Map<String, List<String>> recommondList = new HashMap<>(); // 用户已经看过的 private Map<String, Set<String>> returnedIdsMap = new HashMap<>(); public List<String> getRecommendThemes(String lastId, Integer pageSize, String userId) { //最热话题,剔除当前用户的主题 List<String> hotThemeIds = rankService.getHotestThemes().stream() .filter(o -> !userId.equals(o.getAuthorId())) .map(ThemeAnalysDO::getThemeId) .collect(Collectors.toList()); //最新话题,剔除当前用户的主题 List<String> newThemeIds = this.getRecentThemeList().stream() .filter(o -> !userId.equals(o.getAuthorId())) .map(ThemeAnalysDO::getThemeId) .collect(Collectors.toList()); //推荐话题 List<String> recThemeIds = getPythonRecommendList(userId); // 混合 如果重新搜索,则刷新返回id Set<String> returnedIds = (StringUtils.isEmpty(lastId)) || !returnedIdsMap.containsKey(userId) || returnedIdsMap.get(userId) == null ? new HashSet<>() : returnedIdsMap.get(userId); List<String> result = new ArrayList<>(); getResultList(hotThemeIds, 0, newThemeIds, 0, recThemeIds, 0, returnedIds, result, pageSize, userId); result = result.stream().limit(pageSize).collect(Collectors.toList()); //记录已返回主题id if (StringUtils.isEmpty(lastId)) { returnedIdsMap.put(userId, new HashSet<>(result)); } else { HashSet<String> newSet = new HashSet<>(); newSet.addAll(returnedIdsMap.get(userId)); newSet.addAll(result); returnedIdsMap.put(userId, newSet); } return result; } public List<ThemeAnalysDO> getRecentThemeList() { 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推荐主题 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) { ArrayList<String> result = new ArrayList<>(); 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); result.add(id); hotIdx++; hotTimes--; } while (newTimes > 0 && newThemeIds.size() > newIdx) { id = newThemeIds.get(newIdx); result.add(id); newIdx++; newTimes--; } while (recTimes > 0 && recThemeIds.size() > recIdx) { id = recThemeIds.get(recIdx); result.add(id); recIdx++; recTimes--; } } return result; } private void getResultList(List<String> hotThemeIds, Integer hotTag, List<String> newThemeIds, Integer newTag, List<String> recThemeIds, Integer recTag, Set<String> returnedIds, List<String> result, Integer pageSize, String userId) { if (hotThemeIds.size() <= hotTag && newThemeIds.size() <= newTag && recThemeIds.size() <= recTag) { //所有列表已循环结束,返回 return; } while (result.size() < pageSize * 1.5) { int hotTimes = hotRatio; int newTimes = newRatio; int recTimes = pythonRatio; String id; while (hotTimes > 0 && hotThemeIds.size() > hotTag) { id = hotThemeIds.get(hotTag); if (!returnedIds.contains(id)) { result.add(id); returnedIds.add(id); } hotTag++; hotTimes--; } while (newTimes > 0 && newThemeIds.size() > newTag) { id = newThemeIds.get(newTag); if (!returnedIds.contains(id)) { result.add(id); returnedIds.add(id); } newTag++; newTimes--; } while (recTimes > 0 && recThemeIds.size() > recTag) { id = recThemeIds.get(recTag); if (!returnedIds.contains(id)) { result.add(id); returnedIds.add(id); } recTag++; recTimes--; } } //去重已看过(查看正文) result = visitSummaryService.filterUserNotVisited(userId, result); if (result.size() < pageSize) { getResultList(hotThemeIds, hotTag, newThemeIds, newTag, recThemeIds, recTag, returnedIds, result, pageSize, userId); } } }