package com.aurora.service.impl; import com.alibaba.fastjson.JSON; import com.aurora.model.dto.*; import com.aurora.entity.Article; import com.aurora.entity.ArticleTag; import com.aurora.entity.Category; import com.aurora.entity.Tag; import com.aurora.enums.FileExtEnum; import com.aurora.enums.FilePathEnum; import com.aurora.exception.BizException; import com.aurora.mapper.ArticleMapper; import com.aurora.mapper.ArticleTagMapper; import com.aurora.mapper.CategoryMapper; import com.aurora.mapper.TagMapper; import com.aurora.service.ArticleService; import com.aurora.service.ArticleTagService; import com.aurora.service.RedisService; import com.aurora.service.TagService; import com.aurora.strategy.context.SearchStrategyContext; import com.aurora.strategy.context.UploadStrategyContext; import com.aurora.util.BeanCopyUtil; import com.aurora.util.PageUtil; import com.aurora.util.UserUtil; import com.aurora.model.vo.*; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import lombok.SneakyThrows; import org.springframework.amqp.core.Message; import org.springframework.amqp.core.MessageProperties; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.ByteArrayInputStream; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import static com.aurora.constant.RabbitMQConstant.SUBSCRIBE_EXCHANGE; import static com.aurora.constant.RedisConstant.*; import static com.aurora.enums.ArticleStatusEnum.*; import static com.aurora.enums.StatusCodeEnum.ARTICLE_ACCESS_FAIL; @Service public class ArticleServiceImpl extends ServiceImpl implements ArticleService { @Autowired private ArticleMapper articleMapper; @Autowired private ArticleTagMapper articleTagMapper; @Autowired private CategoryMapper categoryMapper; @Autowired private TagMapper tagMapper; @Autowired private TagService tagService; @Autowired private ArticleTagService articleTagService; @Autowired private RedisService redisService; @Autowired private RabbitTemplate rabbitTemplate; @Autowired private UploadStrategyContext uploadStrategyContext; @Autowired private SearchStrategyContext searchStrategyContext; @SneakyThrows @Override public TopAndFeaturedArticlesDTO listTopAndFeaturedArticles() { List articleCardDTOs = articleMapper.listTopAndFeaturedArticles(); if (articleCardDTOs.size() == 0) { return new TopAndFeaturedArticlesDTO(); } else if (articleCardDTOs.size() > 3) { articleCardDTOs = articleCardDTOs.subList(0, 3); } TopAndFeaturedArticlesDTO topAndFeaturedArticlesDTO = new TopAndFeaturedArticlesDTO(); topAndFeaturedArticlesDTO.setTopArticle(articleCardDTOs.get(0)); articleCardDTOs.remove(0); topAndFeaturedArticlesDTO.setFeaturedArticles(articleCardDTOs); return topAndFeaturedArticlesDTO; } @SneakyThrows @Override public PageResultDTO listArticles() { LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper
() .eq(Article::getIsDelete, 0) .in(Article::getStatus, 1, 2); CompletableFuture asyncCount = CompletableFuture.supplyAsync(() -> articleMapper.selectCount(queryWrapper)); List articles = articleMapper.listArticles(PageUtil.getLimitCurrent(), PageUtil.getSize()); return new PageResultDTO<>(articles, asyncCount.get()); } @SneakyThrows @Override public PageResultDTO listArticlesByCategoryId(Integer categoryId) { LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper
().eq(Article::getCategoryId, categoryId); CompletableFuture asyncCount = CompletableFuture.supplyAsync(() -> articleMapper.selectCount(queryWrapper)); List articles = articleMapper.getArticlesByCategoryId(PageUtil.getLimitCurrent(), PageUtil.getSize(), categoryId); return new PageResultDTO<>(articles, asyncCount.get()); } @SneakyThrows @Override public ArticleDTO getArticleById(Integer articleId) { Article articleForCheck = articleMapper.selectOne(new LambdaQueryWrapper
().eq(Article::getId, articleId)); if (Objects.isNull(articleForCheck)) { return null; } if (articleForCheck.getStatus().equals(2)) { Boolean isAccess; try { isAccess = redisService.sIsMember(ARTICLE_ACCESS + UserUtil.getUserDetailsDTO().getId(), articleId); } catch (Exception exception) { throw new BizException(ARTICLE_ACCESS_FAIL); } if (isAccess.equals(false)) { throw new BizException(ARTICLE_ACCESS_FAIL); } } updateArticleViewsCount(articleId); CompletableFuture asyncArticle = CompletableFuture.supplyAsync(() -> articleMapper.getArticleById(articleId)); CompletableFuture asyncPreArticle = CompletableFuture.supplyAsync(() -> { ArticleCardDTO preArticle = articleMapper.getPreArticleById(articleId); if (Objects.isNull(preArticle)) { preArticle = articleMapper.getLastArticle(); } return preArticle; }); CompletableFuture asyncNextArticle = CompletableFuture.supplyAsync(() -> { ArticleCardDTO nextArticle = articleMapper.getNextArticleById(articleId); if (Objects.isNull(nextArticle)) { nextArticle = articleMapper.getFirstArticle(); } return nextArticle; }); ArticleDTO article = asyncArticle.get(); if (Objects.isNull(article)) { return null; } Double score = redisService.zScore(ARTICLE_VIEWS_COUNT, articleId); if (Objects.nonNull(score)) { article.setViewCount(score.intValue()); } article.setPreArticleCard(asyncPreArticle.get()); article.setNextArticleCard(asyncNextArticle.get()); return article; } @Override public void accessArticle(ArticlePasswordVO articlePasswordVO) { Article article = articleMapper.selectOne(new LambdaQueryWrapper
().eq(Article::getId, articlePasswordVO.getArticleId())); if (Objects.isNull(article)) { throw new BizException("文章不存在"); } if (article.getPassword().equals(articlePasswordVO.getArticlePassword())) { redisService.sAdd(ARTICLE_ACCESS + UserUtil.getUserDetailsDTO().getId(), articlePasswordVO.getArticleId()); } else { throw new BizException("密码错误"); } } @SneakyThrows @Override public PageResultDTO listArticlesByTagId(Integer tagId) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper().eq(ArticleTag::getTagId, tagId); CompletableFuture asyncCount = CompletableFuture.supplyAsync(() -> articleTagMapper.selectCount(queryWrapper)); List articles = articleMapper.listArticlesByTagId(PageUtil.getLimitCurrent(), PageUtil.getSize(), tagId); return new PageResultDTO<>(articles, asyncCount.get()); } @SneakyThrows @Override public PageResultDTO listArchives() { LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper
().eq(Article::getIsDelete, 0).eq(Article::getStatus, 1); CompletableFuture asyncCount = CompletableFuture.supplyAsync(() -> articleMapper.selectCount(queryWrapper)); List articles = articleMapper.listArchives(PageUtil.getLimitCurrent(), PageUtil.getSize()); HashMap> map = new HashMap<>(); for (ArticleCardDTO article : articles) { LocalDateTime createTime = article.getCreateTime(); int month = createTime.getMonth().getValue(); int year = createTime.getYear(); String key = year + "-" + month; if (Objects.isNull(map.get(key))) { List articleCardDTOS = new ArrayList<>(); articleCardDTOS.add(article); map.put(key, articleCardDTOS); } else { map.get(key).add(article); } } List archiveDTOs = new ArrayList<>(); map.forEach((key, value) -> archiveDTOs.add(ArchiveDTO.builder().Time(key).articles(value).build())); archiveDTOs.sort((o1, o2) -> { String[] o1s = o1.getTime().split("-"); String[] o2s = o2.getTime().split("-"); int o1Year = Integer.parseInt(o1s[0]); int o1Month = Integer.parseInt(o1s[1]); int o2Year = Integer.parseInt(o2s[0]); int o2Month = Integer.parseInt(o2s[1]); if (o1Year > o2Year) { return -1; } else if (o1Year < o2Year) { return 1; } else return Integer.compare(o2Month, o1Month); }); return new PageResultDTO<>(archiveDTOs, asyncCount.get()); } @SneakyThrows @Override public PageResultDTO listArticlesAdmin(ConditionVO conditionVO) { CompletableFuture asyncCount = CompletableFuture.supplyAsync(() -> articleMapper.countArticleAdmins(conditionVO)); List articleAdminDTOs = articleMapper.listArticlesAdmin(PageUtil.getLimitCurrent(), PageUtil.getSize(), conditionVO); Map viewsCountMap = redisService.zAllScore(ARTICLE_VIEWS_COUNT); articleAdminDTOs.forEach(item -> { Double viewsCount = viewsCountMap.get(item.getId()); if (Objects.nonNull(viewsCount)) { item.setViewsCount(viewsCount.intValue()); } }); return new PageResultDTO<>(articleAdminDTOs, asyncCount.get()); } @Override @Transactional(rollbackFor = Exception.class) public void saveOrUpdateArticle(ArticleVO articleVO) { Category category = saveArticleCategory(articleVO); Article article = BeanCopyUtil.copyObject(articleVO, Article.class); if (Objects.nonNull(category)) { article.setCategoryId(category.getId()); } article.setUserId(UserUtil.getUserDetailsDTO().getUserInfoId()); this.saveOrUpdate(article); saveArticleTag(articleVO, article.getId()); if (article.getStatus().equals(1)) { rabbitTemplate.convertAndSend(SUBSCRIBE_EXCHANGE, "*", new Message(JSON.toJSONBytes(article.getId()), new MessageProperties())); } } @Override public void updateArticleTopAndFeatured(ArticleTopFeaturedVO articleTopFeaturedVO) { Article article = Article.builder() .id(articleTopFeaturedVO.getId()) .isTop(articleTopFeaturedVO.getIsTop()) .isFeatured(articleTopFeaturedVO.getIsFeatured()) .build(); articleMapper.updateById(article); } @Override public void updateArticleDelete(DeleteVO deleteVO) { List
articles = deleteVO.getIds().stream() .map(id -> Article.builder() .id(id) .isDelete(deleteVO.getIsDelete()) .build()) .collect(Collectors.toList()); this.updateBatchById(articles); } @Override @Transactional(rollbackFor = Exception.class) public void deleteArticles(List articleIds) { articleTagMapper.delete(new LambdaQueryWrapper() .in(ArticleTag::getArticleId, articleIds)); articleMapper.deleteBatchIds(articleIds); } @Override @Transactional(rollbackFor = Exception.class) public ArticleAdminViewDTO getArticleByIdAdmin(Integer articleId) { Article article = articleMapper.selectById(articleId); Category category = categoryMapper.selectById(article.getCategoryId()); String categoryName = null; if (Objects.nonNull(category)) { categoryName = category.getCategoryName(); } List tagNames = tagMapper.listTagNamesByArticleId(articleId); ArticleAdminViewDTO articleAdminViewDTO = BeanCopyUtil.copyObject(article, ArticleAdminViewDTO.class); articleAdminViewDTO.setCategoryName(categoryName); articleAdminViewDTO.setTagNames(tagNames); return articleAdminViewDTO; } @Override public List exportArticles(List articleIds) { List
articles = articleMapper.selectList(new LambdaQueryWrapper
() .select(Article::getArticleTitle, Article::getArticleContent) .in(Article::getId, articleIds)); List urls = new ArrayList<>(); for (Article article : articles) { try (ByteArrayInputStream inputStream = new ByteArrayInputStream(article.getArticleContent().getBytes())) { String url = uploadStrategyContext.executeUploadStrategy(article.getArticleTitle() + FileExtEnum.MD.getExtName(), inputStream, FilePathEnum.MD.getPath()); urls.add(url); } catch (Exception e) { e.printStackTrace(); throw new BizException("导出文章失败"); } } return urls; } @Override public List listArticlesBySearch(ConditionVO condition) { return searchStrategyContext.executeSearchStrategy(condition.getKeywords()); } public void updateArticleViewsCount(Integer articleId) { redisService.zIncr(ARTICLE_VIEWS_COUNT, articleId, 1D); } private Category saveArticleCategory(ArticleVO articleVO) { Category category = categoryMapper.selectOne(new LambdaQueryWrapper() .eq(Category::getCategoryName, articleVO.getCategoryName())); if (Objects.isNull(category) && !articleVO.getStatus().equals(DRAFT.getStatus())) { category = Category.builder() .categoryName(articleVO.getCategoryName()) .build(); categoryMapper.insert(category); } return category; } @Transactional(rollbackFor = Exception.class) public void saveArticleTag(ArticleVO articleVO, Integer articleId) { if (Objects.nonNull(articleVO.getId())) { articleTagMapper.delete(new LambdaQueryWrapper() .eq(ArticleTag::getArticleId, articleVO.getId())); } List tagNames = articleVO.getTagNames(); if (CollectionUtils.isNotEmpty(tagNames)) { List existTags = tagService.list(new LambdaQueryWrapper() .in(Tag::getTagName, tagNames)); List existTagNames = existTags.stream() .map(Tag::getTagName) .collect(Collectors.toList()); List existTagIds = existTags.stream() .map(Tag::getId) .collect(Collectors.toList()); tagNames.removeAll(existTagNames); if (CollectionUtils.isNotEmpty(tagNames)) { List tags = tagNames.stream().map(item -> Tag.builder() .tagName(item) .build()) .collect(Collectors.toList()); tagService.saveBatch(tags); List tagIds = tags.stream() .map(Tag::getId) .collect(Collectors.toList()); existTagIds.addAll(tagIds); } List articleTags = existTagIds.stream().map(item -> ArticleTag.builder() .articleId(articleId) .tagId(item) .build()) .collect(Collectors.toList()); articleTagService.saveBatch(articleTags); } } }