package com.liuyanzhao.sens.service.impl; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.google.common.base.Strings; import com.liuyanzhao.sens.common.constant.RedisKeyExpire; import com.liuyanzhao.sens.common.constant.RedisKeys; import com.liuyanzhao.sens.entity.*; import com.liuyanzhao.sens.mapper.*; import com.liuyanzhao.sens.model.dto.*; import com.liuyanzhao.sens.model.enums.PostStatusEnum; import com.liuyanzhao.sens.model.enums.PostTypeEnum; import com.liuyanzhao.sens.service.*; import com.liuyanzhao.sens.utils.RedisUtil; import com.liuyanzhao.sens.utils.SensUtils; import cn.hutool.http.HtmlUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.elasticsearch.action.search.SearchRequest; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestHighLevelClient; import org.elasticsearch.common.text.Text; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.index.query.BoolQueryBuilder; import org.elasticsearch.index.query.QueryBuilders; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.ScoreSortBuilder; import org.elasticsearch.search.sort.SortOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * <pre> * 记录业务逻辑实现类 * </pre> * * @author : saysky * @date : 2017/11/14 */ @Service @Slf4j public class PostServiceImpl implements PostService { @Autowired private PostMapper postMapper; @Autowired private PostCategoryRefMapper postCategoryRefMapper; @Autowired private PostTagRefMapper postTagRefMapper; @Autowired private CategoryService categoryService; @Autowired private UserMapper userMapper; @Autowired private LinkMapper linkMapper; @Autowired private TagMapper tagMapper; @Autowired private CommentMapper commentMapper; @Autowired private RestHighLevelClient highLevelClient; @Autowired private RedisUtil redisUtil; @Override public Post updatePostStatus(Long postId, Integer status) { Post post = this.get(postId); post.setPostStatus(status); postMapper.updateById(post); return post; } @Override @Async public void updatePostView(Long postId) { postMapper.incrPostViews(postId); } @Override public Integer countByPostTypeAndStatus(String postType, Integer status) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("post_type", postType); queryWrapper.eq("post_status", status); return postMapper.selectCount(queryWrapper); } @Override public List<Post> findByPostTypeAndStatus(String postType, Integer status) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("post_type", postType); queryWrapper.eq("post_status", status); return postMapper.selectList(queryWrapper); } @Override public Page<Post> pagingByPostTypeAndStatus(String postType, Integer status, Page<Post> page) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("post_type", postType); queryWrapper.eq("post_status", status); return (Page<Post>) postMapper.selectPage(page, queryWrapper); } @Override @Async public void updateAllSummary(Integer postSummary) { Map<String, Object> map = new HashMap<>(); map.put("post_type", PostTypeEnum.POST_TYPE_POST.getValue()); List<Post> posts = postMapper.selectByMap(map); for (Post post : posts) { String text = HtmlUtil.cleanHtmlTag(post.getPostContent()); if (text.length() > postSummary) { postMapper.updatePostSummary(post.getId(), text.substring(0, postSummary)); } else { postMapper.updatePostSummary(post.getId(), text); } } } @Override public List<Post> findAllPosts(String postType) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("post_type", postType); queryWrapper.orderByDesc("create_time"); return postMapper.selectList(queryWrapper); } @Override public List<Post> findPostByStatus(Integer status, String postType) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("post_status", status); queryWrapper.eq("post_type", postType); return postMapper.selectList(queryWrapper); } @Override public Post findByPostId(Long postId, String postType) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("id", postId); queryWrapper.eq("post_type", postType); return postMapper.selectOne(queryWrapper); } @Override public Post findByPostUrl(String postUrl) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("post_url", postUrl); return postMapper.selectOne(queryWrapper); } @Override public Post findByPostUrl(String postUrl, String postType) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("post_url", postUrl); queryWrapper.eq("post_type", postType); return postMapper.selectOne(queryWrapper); } @Override public List<Post> findPostLatest() { return postMapper.findTopFive(); } @Override public Post findNextPost(Long postId, String postType) { return postMapper.findByPostIdAfter(postId, postType); } @Override public Post findPreciousPost(Long postId, String postType) { return postMapper.findByPostIdBefore(postId, postType); } @Override public List<Archive> findPostGroupByYearAndMonth() { List<Archive> archives = postMapper.findPostGroupByYearAndMonth(); for (int i = 0; i < archives.size(); i++) { archives.get(i).setPosts(this.findPostByYearAndMonth(archives.get(i).getYear(), archives.get(i).getMonth())); } return archives; } @Override public List<Archive> findPostGroupByYear() { List<Archive> archives = postMapper.findPostGroupByYear(); for (int i = 0; i < archives.size(); i++) { archives.get(i).setPosts(this.findPostByYear(archives.get(i).getYear())); } return archives; } @Override public List<PostSimpleDto> findPostByYearAndMonth(String year, String month) { return postMapper.findPostByYearAndMonth(year, month); } @Override public List<PostSimpleDto> findPostByYear(String year) { return postMapper.findPostByYear(year); } @Override public Page<Post> findPostByYearAndMonth(String year, String month, Page<Post> page) { return page.setRecords(postMapper.pagingPostByYearAndMonth(year, month, null)); } @Override public Page<Post> findPostByCategory(Category category, Page<Post> page) { //子分类Id列表 List<Long> ids = categoryService.selectChildCateId(category.getId()); ids.add(category.getId()); //查询记录和封装分类 List<Post> postList = postMapper.pagingPostByCategoryIdsAndPostStatus(ids, PostStatusEnum.PUBLISHED.getCode(), page); for (int i = 0; i < postList.size(); i++) { List<Category> categories = categoryService.findByPostId(postList.get(i).getId()); postList.get(i).setCategories(categories); } return page.setRecords(postList); } @Override public Page<Post> findPostsByTags(Tag tag, Page<Post> page) { //查询记录和封装分类 List<Post> postList = postMapper.pagingPostsByTagIdAndPostStatus(tag.getId(), PostStatusEnum.PUBLISHED.getCode(), page); for (int i = 0; i < postList.size(); i++) { List<Category> categories = categoryService.findByPostId(postList.get(i).getId()); postList.get(i).setCategories(categories); } return page.setRecords(postList); } @Override public Page<Post> findPostsByEs(Map<String, Object> criteria, Page<Post> page) { //search request SearchRequest searchRequest = new SearchRequest("blog"); //search builder SearchSourceBuilder sourceBuilder = new SearchSourceBuilder(); BoolQueryBuilder booleanQueryBuilder = QueryBuilders.boolQuery(); for (String key : criteria.keySet()) { booleanQueryBuilder.must(QueryBuilders.matchQuery(key, criteria.get(key))); } sourceBuilder.query(booleanQueryBuilder); sourceBuilder.from(Integer.parseInt(String.valueOf((page.getCurrent() - 1) * page.getSize()))); sourceBuilder.size(Integer.parseInt(String.valueOf(page.getSize()))); sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS)); //sort sourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC)); //highlight HighlightBuilder highlightBuilder = new HighlightBuilder(); HighlightBuilder.Field highlightTitle = new HighlightBuilder.Field("postTitle"); highlightTitle.preTags("<span class=\"highlight\">"); highlightTitle.postTags("</span>"); highlightBuilder.field(highlightTitle); sourceBuilder.highlighter(highlightBuilder); // add builder into request searchRequest.indices("blog"); searchRequest.source(sourceBuilder); //response SearchResponse searchResponse = null; List<Post> postList = new ArrayList<>(); try { searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT); //search hits SearchHits hits = searchResponse.getHits(); long totalHits = hits.getTotalHits(); SearchHit[] searchHits = hits.getHits(); page.setTotal((int) totalHits); for (SearchHit hit : searchHits) { String str = hit.getSourceAsString(); Post esPost = JSONObject.parseObject(str, Post.class); //高亮 Map<String, HighlightField> highlightFields = hit.getHighlightFields(); HighlightField highlight = highlightFields.get("postTitle"); if (highlight != null) { Text[] fragments = highlight.fragments(); String fragmentString = fragments[0].string(); esPost.setPostTitle(fragmentString); } postList.add(esPost); } } catch (Exception e) { e.printStackTrace(); log.error("ES未开启: {}", e); } return page.setRecords(postList); } @Override public List<Post> listSameCategoryPosts(Post post) { //获取当前记录的所有标签 List<Category> categories = post.getCategories(); List<Post> tempPosts = new ArrayList<>(); for (Category category : categories) { tempPosts.addAll(postMapper.findPostsByCategoryId(category.getId())); } //去掉当前的记录 tempPosts.remove(post); //去掉重复的记录 List<Post> allPosts = new ArrayList<>(); for (int i = 0; i < tempPosts.size(); i++) { if (!allPosts.contains(tempPosts.get(i))) { allPosts.add(tempPosts.get(i)); } } //按照访问量排序 allPosts = allPosts.stream().sorted(Comparator.comparing(Post::getPostViews).reversed()).collect(Collectors.toList()); return allPosts; } @Override public Long getTotalPostViews() { return postMapper.getPostViewsSum(); } @Override public Integer countArticleByUserIdAndStatus(Long userId, Integer status) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("user_id", userId); queryWrapper.eq("post_status", status); queryWrapper.eq("post_type", PostTypeEnum.POST_TYPE_POST.getValue()); return postMapper.selectCount(queryWrapper); } @Override public String buildRss(List<Post> posts) { String rss = ""; try { rss = SensUtils.getRss(posts); } catch (Exception e) { e.printStackTrace(); } return rss; } @Override public String buildArticleSiteMap() { Map<String, Object> map = new HashMap<>(2); map.put("post_type", PostTypeEnum.POST_TYPE_POST.getValue()); map.put("post_status", PostStatusEnum.PUBLISHED.getCode()); List<Post> posts = postMapper.selectByMap(map); return SensUtils.getSiteMap(posts); } @Override public void resetCommentSize(Long postId) { postMapper.resetCommentSize(postId); } @Override public void incrPostLikes(Long postId) { postMapper.incrPostLikes(postId); } @Override public void deleteByUserId(Long userId) { postMapper.deleteByUserId(userId); } @Override public Integer countByUserId(Long userId) { QueryWrapper queryWrapper = new QueryWrapper(); queryWrapper.eq("user_id", userId); queryWrapper.eq("post_type", PostTypeEnum.POST_TYPE_POST.getValue()); return postMapper.selectCount(queryWrapper); } @Override public BaseMapper<Post> getRepository() { return postMapper; } @Override public Post insert(Post post) { post.setPostViews(0L); post.setCommentSize(0L); post.setPostLikes(0L); postMapper.insert(post); //添加记录分类关系 if (post.getCategories() != null) { for (int i = 0; i < post.getCategories().size(); i++) { postCategoryRefMapper.insert(new PostCategoryRef(post.getId(), post.getCategories().get(i).getId())); } } //添加记录标签关系 if (post.getTags() != null) { for (int i = 0; i < post.getTags().size(); i++) { postTagRefMapper.insert(new PostTagRef(post.getId(), post.getTags().get(i).getId())); } } //删除缓存 redisUtil.del(RedisKeys.USER_POST_RANKING_BY_VIEWS + post.getUserId()); redisUtil.del(RedisKeys.ALL_POST_RANKING_BY_VIEWS); return post; } @Override public Post update(Post post) { postMapper.updateById(post); if (post.getCategories() != null) { //添加分类和记录关联 postCategoryRefMapper.deleteByPostId(post.getId()); //删除分类和记录关联 for (int i = 0; i < post.getCategories().size(); i++) { PostCategoryRef postCategoryRef = new PostCategoryRef(post.getId(), post.getCategories().get(i).getId()); postCategoryRefMapper.insert(postCategoryRef); // 删除缓存 redisUtil.del(RedisKeys.POST_CATEGORY + post.getId()); } } if (post.getTags() != null) { //删除标签和记录关联 postTagRefMapper.deleteByPostId(post.getId()); //添加标签和记录关联 for (int i = 0; i < post.getTags().size(); i++) { PostTagRef postTagRef = new PostTagRef(post.getId(), post.getTags().get(i).getId()); postTagRefMapper.insert(postTagRef); // 删除缓存 redisUtil.del(RedisKeys.POST_TAG + post.getId()); } } return post; } @Override @Transactional(rollbackFor = Exception.class) public void delete(Long postId) { Post post = this.get(postId); if (post != null) { postTagRefMapper.deleteByPostId(postId); postCategoryRefMapper.deleteByPostId(postId); postMapper.deleteById(post.getId()); } //删除缓存 redisUtil.del(RedisKeys.USER_POST_RANKING_BY_VIEWS + post.getUserId()); redisUtil.del(RedisKeys.ALL_POST_RANKING_BY_VIEWS); } @Override public QueryWrapper<Post> getQueryWrapper(Post post) { //对指定字段查询 QueryWrapper<Post> queryWrapper = new QueryWrapper<>(); if (post != null) { if (StrUtil.isNotBlank(post.getPostTitle())) { queryWrapper.like("post_title", post.getPostTitle()); } if (StrUtil.isNotBlank(post.getPostContent())) { queryWrapper.like("post_content", post.getPostContent()); } if (StrUtil.isNotBlank(post.getPostType())) { queryWrapper.eq("post_type", post.getPostType()); } if (post.getUserId() != null && post.getUserId() != -1) { queryWrapper.eq("user_id", post.getUserId()); } if (post.getPostSource() != null && post.getPostSource() != -1) { queryWrapper.eq("post_source", post.getPostSource()); } if (post.getPostStatus() != null && post.getPostStatus() != -1) { queryWrapper.eq("post_status", post.getPostStatus()); } if (post.getAllowComment() != null && post.getAllowComment() != -1) { queryWrapper.eq("allow_comment", post.getAllowComment()); } } return queryWrapper; } @Override public Post insertOrUpdate(Post post) { if (post.getId() == null) { insert(post); } else { update(post); } return post; } @Override public Integer getTodayCount() { return postMapper.getTodayCount(); } @Override public List<PostSimpleDto> getPostRankingByPostView(Integer limit) { String value = redisUtil.get(RedisKeys.ALL_POST_RANKING_BY_VIEWS); // 先从缓存取,缓存没有从数据库取 if (StringUtils.isNotEmpty(value)) { return JSON.parseArray(value, PostSimpleDto.class); } List<PostSimpleDto> postList = postMapper.getPostRankingByPostView(limit); redisUtil.set(RedisKeys.ALL_POST_RANKING_BY_VIEWS, JSON.toJSONString(postList), RedisKeyExpire.ALL_POST_RANKING_BY_VIEWS); return postList; } @Override public List<PostSimpleDto> getPostRankingByUserIdAndPostView(Long userId, Integer limit) { String value = redisUtil.get(RedisKeys.USER_POST_RANKING_BY_VIEWS + userId); // 先从缓存取,缓存没有从数据库取 if (StringUtils.isNotEmpty(value)) { return JSON.parseArray(value, PostSimpleDto.class); } List<PostSimpleDto> postList = postMapper.getPostRankingByUserIdAndPostView(userId, limit); redisUtil.set(RedisKeys.USER_POST_RANKING_BY_VIEWS + userId, JSON.toJSONString(postList), RedisKeyExpire.USER_POST_RANKING_BY_VIEWS); return postList; } @Override public Page<Post> findPostByCateName(String cateName, Page<Post> page) { return page.setRecords(postMapper.findPostByCateName(cateName, page)); } @Override public CountDTO getAllCount() { String value = redisUtil.get(RedisKeys.ALL_COUNT); // 先从缓存取,缓存没有从数据库取 if (StringUtils.isNotEmpty(value)) { return JSON.parseObject(value, CountDTO.class); } CountDTO countDTO = new CountDTO(); countDTO.setUserCount(userMapper.selectCount(null)); countDTO.setPostCount(postMapper.selectCount(null)); countDTO.setCategoryCount(categoryService.getTotalCount()); countDTO.setTagCount(tagMapper.selectCount(null)); countDTO.setCommentCount(commentMapper.selectCount(null)); countDTO.setLinkCount(linkMapper.selectCount(null)); countDTO.setViewCount(this.getTotalPostViews()); redisUtil.set(RedisKeys.ALL_COUNT, JSON.toJSONString(countDTO), RedisKeyExpire.ALL_COUNT); return countDTO; } }