package com.yupi.springbootinit.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.yupi.springbootinit.common.ErrorCode; import com.yupi.springbootinit.constant.CommonConstant; import com.yupi.springbootinit.exception.BusinessException; import com.yupi.springbootinit.exception.ThrowUtils; import com.yupi.springbootinit.mapper.PostFavourMapper; import com.yupi.springbootinit.mapper.PostMapper; import com.yupi.springbootinit.mapper.PostThumbMapper; import com.yupi.springbootinit.model.dto.post.PostEsDTO; import com.yupi.springbootinit.model.dto.post.PostQueryRequest; import com.yupi.springbootinit.model.entity.Post; import com.yupi.springbootinit.model.entity.PostFavour; import com.yupi.springbootinit.model.entity.PostThumb; import com.yupi.springbootinit.model.entity.User; import com.yupi.springbootinit.model.vo.PostVO; import com.yupi.springbootinit.model.vo.UserVO; import com.yupi.springbootinit.service.PostService; import com.yupi.springbootinit.service.UserService; import com.yupi.springbootinit.utils.SqlUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import cn.hutool.core.collection.CollUtil; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.sort.SortBuilder; import org.elasticsearch.search.sort.SortBuilders; import org.elasticsearch.search.sort.SortOrder; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.stereotype.Service; /** * 帖子服务实现 * * @author 程序员鱼皮 * @from 编程导航知识星球 */ @Service @Slf4j public class PostServiceImpl extends ServiceImpl implements PostService { @Resource private UserService userService; @Resource private PostThumbMapper postThumbMapper; @Resource private PostFavourMapper postFavourMapper; @Resource private ElasticsearchRestTemplate elasticsearchRestTemplate; /** * 获取帖子封装列表 * * @param postList * @param request * @return */ @Override public List getPostVOList(List postList, HttpServletRequest request) { if (CollUtil.isEmpty(postList)) { return new ArrayList<>(); } // 1. 关联查询用户信息 Set userIdSet = postList.stream().map(Post::getUserId).collect(Collectors.toSet()); Map> userIdUserListMap = userService.listByIds(userIdSet).stream() .collect(Collectors.groupingBy(User::getId)); // 2. 已登录,获取用户点赞、收藏状态 Map postIdHasThumbMap = new HashMap<>(); Map postIdHasFavourMap = new HashMap<>(); User loginUser = userService.getLoginUserPermitNull(request); if (loginUser != null) { Set postIdSet = postList.stream().map(Post::getId).collect(Collectors.toSet()); // 获取点赞 QueryWrapper postThumbQueryWrapper = new QueryWrapper<>(); postThumbQueryWrapper.in("postId", postIdSet); postThumbQueryWrapper.eq("userId", loginUser.getId()); List postPostThumbList = postThumbMapper.selectList(postThumbQueryWrapper); postPostThumbList.forEach(postPostThumb -> postIdHasThumbMap.put(postPostThumb.getPostId(), true)); // 获取收藏 QueryWrapper postFavourQueryWrapper = new QueryWrapper<>(); postFavourQueryWrapper.in("postId", postIdSet); postFavourQueryWrapper.eq("userId", loginUser.getId()); List postFavourList = postFavourMapper.selectList(postFavourQueryWrapper); postFavourList.forEach(postFavour -> postIdHasFavourMap.put(postFavour.getPostId(), true)); } // 3. 填充信息 return postList.stream().map(post -> { PostVO postVO = PostVO.objToVo(post); Long userId = post.getUserId(); User user = null; if (userIdUserListMap.containsKey(userId)) { user = userIdUserListMap.get(userId).get(0); } postVO.setUser(userService.getUserVO(user)); postVO.setHasThumb(postIdHasThumbMap.getOrDefault(post.getId(), false)); postVO.setHasFavour(postIdHasFavourMap.getOrDefault(post.getId(), false)); return postVO; }).collect(Collectors.toList()); } @Override public void validPost(Post post, boolean add) { if (post == null) { throw new BusinessException(ErrorCode.PARAMS_ERROR); } String title = post.getTitle(); String content = post.getContent(); String tags = post.getTags(); // 创建时,参数不能为空 if (add) { ThrowUtils.throwIf(StringUtils.isAnyBlank(title, content, tags), ErrorCode.PARAMS_ERROR); } // 有参数则校验 if (StringUtils.isNotBlank(title) && title.length() > 80) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "标题过长"); } if (StringUtils.isNotBlank(content) && content.length() > 8192) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "内容过长"); } } /** * 获取查询包装类 * * @param postQueryRequest * @return */ @Override public QueryWrapper getQueryWrapper(PostQueryRequest postQueryRequest) { QueryWrapper queryWrapper = new QueryWrapper<>(); if (postQueryRequest == null) { return queryWrapper; } String searchText = postQueryRequest.getSearchText(); String sortField = postQueryRequest.getSortField(); String sortOrder = postQueryRequest.getSortOrder(); Long id = postQueryRequest.getId(); String title = postQueryRequest.getTitle(); String content = postQueryRequest.getContent(); List tagList = postQueryRequest.getTags(); Long userId = postQueryRequest.getUserId(); Long notId = postQueryRequest.getNotId(); // 拼接查询条件 if (StringUtils.isNotBlank(searchText)) { queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText)); } queryWrapper.like(StringUtils.isNotBlank(title), "title", title); queryWrapper.like(StringUtils.isNotBlank(content), "content", content); if (CollUtil.isNotEmpty(tagList)) { for (String tag : tagList) { queryWrapper.like("tags", "\"" + tag + "\""); } } queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId); queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id); queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId); queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField); return queryWrapper; } @Override public Page searchFromEs(PostQueryRequest postQueryRequest) { Long id = postQueryRequest.getId(); Long notId = postQueryRequest.getNotId(); String searchText = postQueryRequest.getSearchText(); String title = postQueryRequest.getTitle(); String content = postQueryRequest.getContent(); List tagList = postQueryRequest.getTags(); List orTagList = postQueryRequest.getOrTags(); Long userId = postQueryRequest.getUserId(); // es 起始页为 0 long current = postQueryRequest.getCurrent() - 1; long pageSize = postQueryRequest.getPageSize(); String sortField = postQueryRequest.getSortField(); String sortOrder = postQueryRequest.getSortOrder(); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); // 过滤 boolQueryBuilder.filter(QueryBuilders.termQuery("isDelete", 0)); if (id != null) { boolQueryBuilder.filter(QueryBuilders.termQuery("id", id)); } if (notId != null) { boolQueryBuilder.mustNot(QueryBuilders.termQuery("id", notId)); } if (userId != null) { boolQueryBuilder.filter(QueryBuilders.termQuery("userId", userId)); } // 必须包含所有标签 if (CollUtil.isNotEmpty(tagList)) { for (String tag : tagList) { boolQueryBuilder.filter(QueryBuilders.termQuery("tags", tag)); } } // 包含任何一个标签即可 if (CollUtil.isNotEmpty(orTagList)) { BoolQueryBuilder orTagBoolQueryBuilder = QueryBuilders.boolQuery(); for (String tag : orTagList) { orTagBoolQueryBuilder.should(QueryBuilders.termQuery("tags", tag)); } orTagBoolQueryBuilder.minimumShouldMatch(1); boolQueryBuilder.filter(orTagBoolQueryBuilder); } // 按关键词检索 if (StringUtils.isNotBlank(searchText)) { boolQueryBuilder.should(QueryBuilders.matchQuery("title", searchText)); boolQueryBuilder.should(QueryBuilders.matchQuery("description", searchText)); boolQueryBuilder.should(QueryBuilders.matchQuery("content", searchText)); boolQueryBuilder.minimumShouldMatch(1); } // 按标题检索 if (StringUtils.isNotBlank(title)) { boolQueryBuilder.should(QueryBuilders.matchQuery("title", title)); boolQueryBuilder.minimumShouldMatch(1); } // 按内容检索 if (StringUtils.isNotBlank(content)) { boolQueryBuilder.should(QueryBuilders.matchQuery("content", content)); boolQueryBuilder.minimumShouldMatch(1); } // 排序 SortBuilder sortBuilder = SortBuilders.scoreSort(); if (StringUtils.isNotBlank(sortField)) { sortBuilder = SortBuilders.fieldSort(sortField); sortBuilder.order(CommonConstant.SORT_ORDER_ASC.equals(sortOrder) ? SortOrder.ASC : SortOrder.DESC); } // 分页 PageRequest pageRequest = PageRequest.of((int) current, (int) pageSize); // 构造查询 NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder) .withPageable(pageRequest).withSorts(sortBuilder).build(); SearchHits searchHits = elasticsearchRestTemplate.search(searchQuery, PostEsDTO.class); Page page = new Page<>(); page.setTotal(searchHits.getTotalHits()); List resourceList = new ArrayList<>(); // 查出结果后,从 db 获取最新动态数据(比如点赞数) if (searchHits.hasSearchHits()) { List> searchHitList = searchHits.getSearchHits(); List postIdList = searchHitList.stream().map(searchHit -> searchHit.getContent().getId()) .collect(Collectors.toList()); List postList = baseMapper.selectBatchIds(postIdList); if (postList != null) { Map> idPostMap = postList.stream().collect(Collectors.groupingBy(Post::getId)); postIdList.forEach(postId -> { if (idPostMap.containsKey(postId)) { resourceList.add(idPostMap.get(postId).get(0)); } else { // 从 es 清空 db 已物理删除的数据 String delete = elasticsearchRestTemplate.delete(String.valueOf(postId), PostEsDTO.class); log.info("delete post {}", delete); } }); } } page.setRecords(resourceList); return page; } @Override public PostVO getPostVO(Post post, HttpServletRequest request) { PostVO postVO = PostVO.objToVo(post); long postId = post.getId(); // 1. 关联查询用户信息 Long userId = post.getUserId(); User user = null; if (userId != null && userId > 0) { user = userService.getById(userId); } UserVO userVO = userService.getUserVO(user); postVO.setUser(userVO); // 2. 已登录,获取用户点赞、收藏状态 User loginUser = userService.getLoginUserPermitNull(request); if (loginUser != null) { // 获取点赞 QueryWrapper postThumbQueryWrapper = new QueryWrapper<>(); postThumbQueryWrapper.in("postId", postId); postThumbQueryWrapper.eq("userId", loginUser.getId()); PostThumb postThumb = postThumbMapper.selectOne(postThumbQueryWrapper); postVO.setHasThumb(postThumb != null); // 获取收藏 QueryWrapper postFavourQueryWrapper = new QueryWrapper<>(); postFavourQueryWrapper.in("postId", postId); postFavourQueryWrapper.eq("userId", loginUser.getId()); PostFavour postFavour = postFavourMapper.selectOne(postFavourQueryWrapper); postVO.setHasFavour(postFavour != null); } return postVO; } @Override public Page getPostVOPage(Page postPage, HttpServletRequest request) { List postList = postPage.getRecords(); Page postVOPage = new Page<>(postPage.getCurrent(), postPage.getSize(), postPage.getTotal()); if (CollUtil.isEmpty(postList)) { return postVOPage; } // 1. 关联查询用户信息 Set userIdSet = postList.stream().map(Post::getUserId).collect(Collectors.toSet()); Map> userIdUserListMap = userService.listByIds(userIdSet).stream() .collect(Collectors.groupingBy(User::getId)); // 2. 已登录,获取用户点赞、收藏状态 Map postIdHasThumbMap = new HashMap<>(); Map postIdHasFavourMap = new HashMap<>(); User loginUser = userService.getLoginUserPermitNull(request); if (loginUser != null) { Set postIdSet = postList.stream().map(Post::getId).collect(Collectors.toSet()); loginUser = userService.getLoginUser(request); // 获取点赞 QueryWrapper postThumbQueryWrapper = new QueryWrapper<>(); postThumbQueryWrapper.in("postId", postIdSet); postThumbQueryWrapper.eq("userId", loginUser.getId()); List postPostThumbList = postThumbMapper.selectList(postThumbQueryWrapper); postPostThumbList.forEach(postPostThumb -> postIdHasThumbMap.put(postPostThumb.getPostId(), true)); // 获取收藏 QueryWrapper postFavourQueryWrapper = new QueryWrapper<>(); postFavourQueryWrapper.in("postId", postIdSet); postFavourQueryWrapper.eq("userId", loginUser.getId()); List postFavourList = postFavourMapper.selectList(postFavourQueryWrapper); postFavourList.forEach(postFavour -> postIdHasFavourMap.put(postFavour.getPostId(), true)); } // 填充信息 List postVOList = postList.stream().map(post -> { PostVO postVO = PostVO.objToVo(post); Long userId = post.getUserId(); User user = null; if (userIdUserListMap.containsKey(userId)) { user = userIdUserListMap.get(userId).get(0); } postVO.setUser(userService.getUserVO(user)); postVO.setHasThumb(postIdHasThumbMap.getOrDefault(post.getId(), false)); postVO.setHasFavour(postIdHasFavourMap.getOrDefault(post.getId(), false)); return postVO; }).collect(Collectors.toList()); postVOPage.setRecords(postVOList); return postVOPage; } }