基于Java+SpringBoot+Vue前后端分离博客系统设计与实现

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第1张图片

博主介绍:✌全网粉丝3W+,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战✌

博主作品:《微服务实战》专栏是本人的实战经验总结,《Spring家族及微服务系列》专注Spring、SpringMVC、SpringBoot、SpringCloud系列、Nacos等源码解读、热门面试题、架构设计等。除此之外还有不少文章等你来细细品味,更多惊喜等着你哦

开源项目:点击这里克隆或者下载

文末关注公众号获取联系精彩专栏推荐订阅 不然下次找不到哟
 

  ✨【微服务】Nacos为什么丢弃短连接(http)而选择拥抱长连接(gRPC)

目录

一、前言

1.导言

1.1编写目的

1.2项目范围

1.3参考资料

2.项目介绍

2.1项目背景

2.2项目目标

二、系统设计

1、系统运行环境

2、系统架构设计

三、功能需求简介

1、系统角色分析

1.1、游客

1.2、会员

1.3管理员

2、用例分析与设计

2.1、管理员

2.2、博主

2.3、游客​编辑 

四、功能截图

1、管理员

1.1、文章管理

1.2、消息管理

1.3、用户管理

1.4、权限管理

1.5、系统管理

1.6、相册管理

1.7、说说管理

2、博主

2.1、首页

2.2、点击测试文章进来

五、实现代码

1、文章控制器

2、服务接口

3、实现类

六、获取源码


一、前言

1.导言


1.1编写目的


该文档描述了个人博客网站的功能和性能的要求,将作为对该项目在概要设计阶段的设计输入。
本文档的预期读者包括:
1.设计开发人员
2.项日管理人员
3.测试人员
4.用户

1.2项目范围


该文档的月的是解决整个项月系统中“做仁么”的问题。对于开发技术在这里并没有涉及,而+要是通过建立模型的方式来描述用户的需求为,客户、用户,开发方等不同参与方提供一个交流的平台。

1.3参考资料


[1]《软件工程导论第6版》 张海潘等清华大学出版社
[2]《软件项目管理案例教程第3版》韩万江等,机械工业出版社

2.项目介绍


2.1项目背景


博客是使用特定的软件,在网络上出版、发表和张贴个人文章的人,或者是一种通常由个人管理、不定期张贴新的文章的网站。博客上的文章通常以网页形式出现,并根据张贴时间,以倒序排列。博客是继MSN、BBS、ICQ之后出现的第4种网络交流方式,现已受到大家的欢迎,是网络时代的个人“读者文摘”,是以超级链接为入口的网络日记,它代表着新的生活、工作和学习方式。许多博客专注在特定的课题上提供评论或新闻,其他则被作为个人性的日记。一个典型的博客结合了文字、图像、其他博客或网站的链接及其它与主题相关的媒体,能够让读者以互动的方式留下意见,是许多博客的重要要素。大部分的博客内容以文字为主,但仍有一些博客专注在艺术、摄影、视频、音乐、播客等各种主题

2.2项目目标


开发一个具有开放体系结构的、易扩充的、易维护的、具有良好人机交互界面的个人博客网站,为网络用户提供进行在线交流的网络平台。
通过个人博客网站可以结交更多的朋友,表达更多的想法,它随时可以发布文章。
 

二、系统设计

1、系统运行环境


开发系统:Windows10左右

架构模式:MVC/前后端分离

JDK版本:Java JDK1.8

开发工具:IDEA

数据库版本: mysql5.7

数据库可视化工具: SQL yog或Navicat for MySQL

服务器:SpringBoot自带 apache tomcat

主要技术:Java、SpringBoot2、MyBatis-plus、SpringSecurity、MySQL、RabbitMQ、ES、Html、Vue、Elementui等,建造者模式、策略模式等
 

2、系统架构设计
基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第2张图片

三、功能需求简介

1、系统角色分析


角色是指与系统产生交互的外部用户或者外部系统。本系统的使用角色主要有游客、会员、管理员三种。以下是详细介绍

1.1、游客


游客是第一次进入网站,但未进行注册,进行部分功能试用,在具体的功能实现上,可以分为以下几个部分:

1.1.1.搜索和浏览他人的博客
游客不须登录系统就可以实现查看日志照片以及博客主的资料信息。

1.1.2.用户注册
游客将个人的信息存储到博客网站的数据库中,以成为本博客的正式用户。

1.2、会员


通过计算机网络将前台与后台的数据库相连,系统用户将从前台得到的信息进行处理,实现文章管理,信息管理,个人相册管理,评论,留言等子系统。

1.2.1.博客管理
注册用户员对网站中自己的文章进行删除,更新等操作。

1.2.2.信息管理
发布,更改个人资料信息。

1.2.3.个人相册管理
对博客相册中的图片进行上传,更新,删除操作。

1.2.4.好友管理
添加或删除好友。

1.2.5.评论
对于他人给自己的不恰当评论予以删除。

1.2.6.留言
对他人给自己的留言进行回复或删除。

1.3管理员


1.3.1.用户管理
对已注册的用户进行管理。

1.3.2.评论、留言管理
对已注册的用户发表的评论和留言进行管理。

1.3.3.相册管理
对已注册用户上传的照片进行审核,严禁上传不和谐的照片。

1.3.4.博客管理
对用户已发表的博文进行管理,规范其内容,屏蔽掉一些不健康或反动的言论。
 

2、用例分析与设计

分析现有情况及问题,将个人博客系统划分为三个功能用例:游客用例、用户用例、管理员用例。
在个人博客系统中,管理员要让每个博客申请个人博客账号,并让博客设置个人密码,账户内存储每个博客的个人信息。有账号的博客用员可以通过管理员浏览好友动态、写博文、分享博文等。每个博客浏览的范围、期限不同,可通过互联网或登录个人博客网站查询个人信息和其他情况。
登录个人博客主页时,先输入博客的账号和密码,系统验证该帐号的有效性,无效则提示其原因,有效则显示博客的主页信息,供管理员人工核对。然后可以进行浏览动态,添加应用等一些功能。
1系统总体用例

2.1、管理员

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第3张图片

2.2、博主

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第4张图片

2.3、游客
基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第5张图片

四、功能截图

1、管理员

登录界面:

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第6张图片

 基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第7张图片

首页:

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第8张图片

1.1、文章管理

1)发布文章

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第9张图片

2)文章列表

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第10张图片

3)分类管理

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第11张图片

4)标签管理

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第12张图片

1.2、消息管理

1)评论管理

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第13张图片

2)留言管理

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第14张图片

1.3、用户管理

1)用户列表

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第15张图片

2)在线用户

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第16张图片

1.4、权限管理

1)角色管理

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第17张图片

2)接口管理

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第18张图片

3)菜单管理

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第19张图片

1.5、系统管理

1)友链管理

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第20张图片

1.6、相册管理

1)相册列表

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第21张图片

1.7、说说管理

1)发布说说

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第22张图片

2)说说列表

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第23张图片

2、博主

博主也有很多功能界面,这里就不一一举出了。

2.1、首页

可以播放音乐,试听结合

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第24张图片

 基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第25张图片

2.2、点击测试文章进来

基于Java+SpringBoot+Vue前后端分离博客系统设计与实现_第26张图片

五、实现代码

1、文章控制器

@Api(tags = "文章模块")
@RestController
public class ArticleController {
    @Autowired
    private ArticleService articleService;
    @Autowired
    private UploadStrategyContext uploadStrategyContext;
    @Autowired
    private ArticleImportStrategyContext articleImportStrategyContext;

    /**
     * 查看文章归档
     *
     * @return {@link Result} 文章归档列表
     */
    @ApiOperation(value = "查看文章归档")
    @GetMapping("/articles/archives")
    public Result> listArchives() {
        return Result.ok(articleService.listArchives());
    }

    /**
     * 查看首页文章
     *
     * @return {@link Result} 首页文章列表
     */
    @ApiOperation(value = "查看首页文章")
    @GetMapping("/articles")
    public Result> listArticles() {
        return Result.ok(articleService.listArticles());
    }

    /**
     * 查看后台文章
     *
     * @param conditionVO 条件
     * @return {@link Result} 后台文章列表
     */
    @ApiOperation(value = "查看后台文章")
    @GetMapping("/admin/articles")
    public Result> listArticleBacks(ConditionVO conditionVO) {
        return Result.ok(articleService.listArticleBacks(conditionVO));
    }

    /**
     * 添加或修改文章
     *
     * @param articleVO 文章信息
     * @return {@link Result<>}
     */
    @OptLog(optType = SAVE_OR_UPDATE)
    @ApiOperation(value = "添加或修改文章")
    @PostMapping("/admin/articles")
    public Result saveOrUpdateArticle(@Valid @RequestBody ArticleVO articleVO) {
        articleService.saveOrUpdateArticle(articleVO);
        return Result.ok();
    }

    /**
     * 修改文章置顶状态
     *
     * @param articleTopVO 文章置顶信息
     * @return {@link Result<>}
     */
    @OptLog(optType = UPDATE)
    @ApiOperation(value = "修改文章置顶")
    @PutMapping("/admin/articles/top")
    public Result updateArticleTop(@Valid @RequestBody ArticleTopVO articleTopVO) {
        articleService.updateArticleTop(articleTopVO);
        return Result.ok();
    }

    /**
     * 恢复或删除文章
     *
     * @param deleteVO 逻辑删除信息
     * @return {@link Result<>}
     */
    @OptLog(optType = UPDATE)
    @ApiOperation(value = "恢复或删除文章")
    @PutMapping("/admin/articles")
    public Result updateArticleDelete(@Valid @RequestBody DeleteVO deleteVO) {
        articleService.updateArticleDelete(deleteVO);
        return Result.ok();
    }

    /**
     * 上传文章图片
     *
     * @param file 文件
     * @return {@link Result} 文章图片地址
     */
    @ApiOperation(value = "上传文章图片")
    @ApiImplicitParam(name = "file", value = "文章图片", required = true, dataType = "MultipartFile")
    @PostMapping("/admin/articles/images")
    public Result saveArticleImages(MultipartFile file) {
        return Result.ok(uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.ARTICLE.getPath()));
    }

    /**
     * 删除文章
     *
     * @param articleIdList 文章id列表
     * @return {@link Result<>}
     */
    @OptLog(optType = REMOVE)
    @ApiOperation(value = "物理删除文章")
    @DeleteMapping("/admin/articles")
    public Result deleteArticles(@RequestBody List articleIdList) {
        articleService.deleteArticles(articleIdList);
        return Result.ok();
    }

    /**
     * 根据id查看后台文章
     *
     * @param articleId 文章id
     * @return {@link Result} 后台文章
     */
    @ApiOperation(value = "根据id查看后台文章")
    @ApiImplicitParam(name = "articleId", value = "文章id", required = true, dataType = "Integer")
    @GetMapping("/admin/articles/{articleId}")
    public Result getArticleBackById(@PathVariable("articleId") Integer articleId) {
        return Result.ok(articleService.getArticleBackById(articleId));
    }

    /**
     * 根据id查看文章
     *
     * @param articleId 文章id
     * @return {@link Result} 文章信息
     */
    @ApiOperation(value = "根据id查看文章")
    @ApiImplicitParam(name = "articleId", value = "文章id", required = true, dataType = "Integer")
    @GetMapping("/articles/{articleId}")
    public Result getArticleById(@PathVariable("articleId") Integer articleId) {
        return Result.ok(articleService.getArticleById(articleId));
    }

    /**
     * 根据条件查询文章
     *
     * @param condition 条件
     * @return {@link Result} 文章列表
     */
    @ApiOperation(value = "根据条件查询文章")
    @GetMapping("/articles/condition")
    public Result listArticlesByCondition(ConditionVO condition) {
        return Result.ok(articleService.listArticlesByCondition(condition));
    }

    /**
     * 搜索文章
     *
     * @param condition 条件
     * @return {@link Result} 文章列表
     */
    @ApiOperation(value = "搜索文章")
    @GetMapping("/articles/search")
    public Result> listArticlesBySearch(ConditionVO condition) {
        return Result.ok(articleService.listArticlesBySearch(condition));
    }

    /**
     * 点赞文章
     *
     * @param articleId 文章id
     * @return {@link Result<>}
     */
    @ApiOperation(value = "点赞文章")
    @ApiImplicitParam(name = "articleId", value = "文章id", required = true, dataType = "Integer")
    @PostMapping("/articles/{articleId}/like")
    public Result saveArticleLike(@PathVariable("articleId") Integer articleId) {
        articleService.saveArticleLike(articleId);
        return Result.ok();
    }

    /**
     * 导出文章
     *
     * @param articleIdList 文章id列表
     * @return {@link List} 文件url列表
     */
    @ApiOperation(value = "导出文章")
    @ApiImplicitParam(name = "articleIdList", value = "文章id", required = true, dataType = "List")
    @PostMapping("/admin/articles/export")
    public Result> exportArticles(@RequestBody List articleIdList) {
        return Result.ok(articleService.exportArticles(articleIdList));
    }

    /**
     * 导入文章
     *
     * @param file 文件
     * @param type 文章类型
     * @return {@link Result<>}
     */
    @ApiOperation(value = "导入文章")
    @PostMapping("/admin/articles/import")
    public Result importArticles(MultipartFile file, @RequestParam(required = false) String type) {
        articleImportStrategyContext.importArticles(file, type);
        return Result.ok();
    }
}

2、服务接口

public interface ArticleService extends IService
{ /** * 查询文章归档 * * @return 文章归档 */ PageResult listArchives(); /** * 查询后台文章 * * @param condition 条件 * @return 文章列表 */ PageResult listArticleBacks(ConditionVO condition); /** * 查询首页文章 * * @return 文章列表 */ List listArticles(); /** * 根据条件查询文章列表 * * @param condition 条件 * @return 文章列表 */ ArticlePreviewListDTO listArticlesByCondition(ConditionVO condition); /** * 搜索文章 * * @param condition 条件 * @return 文章列表 */ List listArticlesBySearch(ConditionVO condition); /** * 根据id查看后台文章 * * @param articleId 文章id * @return 文章列表 */ ArticleVO getArticleBackById(Integer articleId); /** * 根据id查看文章 * * @param articleId 文章id * @return {@link ArticleDTO} 文章信息 */ ArticleDTO getArticleById(Integer articleId); /** * 点赞文章 * * @param articleId 文章id */ void saveArticleLike(Integer articleId); /** * 添加或修改文章 * * @param articleVO 文章信息 */ void saveOrUpdateArticle(ArticleVO articleVO); /** * 修改文章置顶 * * @param articleTopVO 文章置顶信息 */ void updateArticleTop(ArticleTopVO articleTopVO); /** * 删除或恢复文章 * * @param deleteVO 逻辑删除对象 */ void updateArticleDelete(DeleteVO deleteVO); /** * 物理删除文章 * * @param articleIdList 文章id集合 */ void deleteArticles(List articleIdList); /** * 导出文章 * * @param articleIdList 文章id列表 * @return {@link List}<{@link String}> 文件地址 */ List exportArticles(List articleIdList); }

3、实现类

@Service
@Slf4j
public class ArticleServiceImpl extends ServiceImpl implements ArticleService {
    @Autowired
    private ArticleDao articleDao;
    @Autowired
    private CategoryDao categoryDao;
    @Autowired
    private TagDao tagDao;
    @Autowired
    private TagService tagService;
    @Autowired
    private ArticleTagDao articleTagDao;
    @Autowired
    private SearchStrategyContext searchStrategyContext;
    @Autowired
    private HttpSession session;
    @Autowired
    private RedisService redisService;
    @Autowired
    private ArticleTagService articleTagService;
    @Autowired
    private BlogInfoService blogInfoService;
    @Autowired
    private UploadStrategyContext uploadStrategyContext;

    @Override
    public PageResult listArchives() {
        Page
page = new Page<>(PageUtils.getCurrent(), PageUtils.getSize()); // 获取分页数据 Page
articlePage = articleDao.selectPage(page, new LambdaQueryWrapper
() .select(Article::getId, Article::getArticleTitle, Article::getCreateTime).orderByDesc(Article::getCreateTime) .eq(Article::getIsDelete, FALSE) .eq(Article::getStatus, PUBLIC.getStatus())); List archiveDTOList = BeanCopyUtils.copyList(articlePage.getRecords(), ArchiveDTO.class); return new PageResult<>(archiveDTOList, (int) articlePage.getTotal()); } @Override public PageResult listArticleBacks(ConditionVO condition) { // 查询文章总量 Integer count = articleDao.countArticleBacks(condition); if (count == 0) { return new PageResult<>(); } // 查询后台文章 List articleBackDTOList = articleDao.listArticleBacks(PageUtils.getLimitCurrent(), PageUtils.getSize(), condition); // 查询文章点赞量和浏览量 Map viewsCountMap = redisService.zAllScore(ARTICLE_VIEWS_COUNT); Map likeCountMap = redisService.hGetAll(ARTICLE_LIKE_COUNT); // 封装点赞量和浏览量 articleBackDTOList.forEach(item -> { Double viewsCount = viewsCountMap.get(item.getId()); if (Objects.nonNull(viewsCount)) { item.setViewsCount(viewsCount.intValue()); } item.setLikeCount((Integer) likeCountMap.get(item.getId().toString())); }); return new PageResult<>(articleBackDTOList, count); } @Override public List listArticles() { return articleDao.listArticles(PageUtils.getLimitCurrent(), PageUtils.getSize()); } @Override public ArticlePreviewListDTO listArticlesByCondition(ConditionVO condition) { // 查询文章 List articlePreviewDTOList = articleDao.listArticlesByCondition(PageUtils.getLimitCurrent(), PageUtils.getSize(), condition); // 搜索条件对应名(标签或分类名) String name; if (Objects.nonNull(condition.getCategoryId())) { name = categoryDao.selectOne(new LambdaQueryWrapper().select(Category::getCategoryName) .eq(Category::getId, condition.getCategoryId())).getCategoryName(); } else { name = tagService.getOne(new LambdaQueryWrapper() .select(Tag::getTagName).eq(Tag::getId, condition.getTagId())).getTagName(); } return ArticlePreviewListDTO.builder().articlePreviewDTOList(articlePreviewDTOList).name(name).build(); } @Override public ArticleDTO getArticleById(Integer articleId) { // 查询推荐文章 CompletableFuture> recommendArticleList = CompletableFuture.supplyAsync(() -> articleDao.listRecommendArticles(articleId)); // 查询最新文章 CompletableFuture> newestArticleList = CompletableFuture.supplyAsync(() -> { List
articleList = articleDao.selectList(new LambdaQueryWrapper
() .select(Article::getId, Article::getArticleTitle, Article::getArticleCover, Article::getCreateTime).eq(Article::getIsDelete, FALSE) .eq(Article::getStatus, PUBLIC.getStatus()).orderByDesc(Article::getId).last("limit 5")); return BeanCopyUtils.copyList(articleList, ArticleRecommendDTO.class); }); // 查询id对应文章 ArticleDTO article = articleDao.getArticleById(articleId); if (Objects.isNull(article)) { throw new BizException("文章不存在"); } // 更新文章浏览量 updateArticleViewsCount(articleId); // 查询上一篇下一篇文章 Article lastArticle = articleDao.selectOne(new LambdaQueryWrapper
() .select(Article::getId, Article::getArticleTitle, Article::getArticleCover).eq(Article::getIsDelete, FALSE) .eq(Article::getStatus, PUBLIC.getStatus()) .lt(Article::getId, articleId) .orderByDesc(Article::getId).last("limit 1")); Article nextArticle = articleDao.selectOne(new LambdaQueryWrapper
() .select(Article::getId, Article::getArticleTitle, Article::getArticleCover).eq(Article::getIsDelete, FALSE) .eq(Article::getStatus, PUBLIC.getStatus()) .gt(Article::getId, articleId).orderByAsc(Article::getId) .last("limit 1")); article.setLastArticle(BeanCopyUtils.copyObject(lastArticle, ArticlePaginationDTO.class)); article.setNextArticle(BeanCopyUtils.copyObject(nextArticle, ArticlePaginationDTO.class)); // 封装点赞量和浏览量 Double score = redisService.zScore(ARTICLE_VIEWS_COUNT, articleId); if (Objects.nonNull(score)) { article.setViewsCount(score.intValue()); } article.setLikeCount((Integer) redisService.hGet(ARTICLE_LIKE_COUNT, articleId.toString())); // 封装文章信息 try { article.setRecommendArticleList(recommendArticleList.get()); article.setNewestArticleList(newestArticleList.get()); } catch (Exception e) { log.error(StrUtil.format("堆栈信息:{}", ExceptionUtil.stacktraceToString(e))); } return article; } @Override public void saveArticleLike(Integer articleId) { // 判断是否点赞 String articleLikeKey = ARTICLE_USER_LIKE + UserUtils.getLoginUser().getUserInfoId(); if (redisService.sIsMember(articleLikeKey, articleId)) { // 点过赞则删除文章id redisService.sRemove(articleLikeKey, articleId); // 文章点赞量-1 redisService.hDecr(ARTICLE_LIKE_COUNT, articleId.toString(), 1L); } else { // 未点赞则增加文章id redisService.sAdd(articleLikeKey, articleId); // 文章点赞量+1 redisService.hIncr(ARTICLE_LIKE_COUNT, articleId.toString(), 1L); } } @Transactional(rollbackFor = Exception.class) @Override public void saveOrUpdateArticle(ArticleVO articleVO) { // 查询博客配置信息 CompletableFuture webConfig = CompletableFuture.supplyAsync(() -> blogInfoService.getWebsiteConfig()); // 保存文章分类 Category category = saveArticleCategory(articleVO); // 保存或修改文章 Article article = BeanCopyUtils.copyObject(articleVO, Article.class); if (Objects.nonNull(category)) { article.setCategoryId(category.getId()); } // 设定默认文章封面 if (StrUtil.isBlank(article.getArticleCover())){ try { article.setArticleCover(webConfig.get().getArticleCover()); } catch (Exception e) { throw new BizException("设定默认文章封面失败"); } } article.setUserId(UserUtils.getLoginUser().getUserInfoId()); this.saveOrUpdate(article); // 保存文章标签 saveArticleTag(articleVO, article.getId()); } /** * 保存文章分类 * * @param articleVO 文章信息 * @return {@link Category} 文章分类 */ private Category saveArticleCategory(ArticleVO articleVO) { // 判断分类是否存在 Category category = categoryDao.selectOne(new LambdaQueryWrapper().eq(Category::getCategoryName, articleVO.getCategoryName())); if (Objects.isNull(category) && !articleVO.getStatus().equals(DRAFT.getStatus())) { category = Category.builder().categoryName(articleVO.getCategoryName()).build(); categoryDao.insert(category); } return category; } @Override public void updateArticleTop(ArticleTopVO articleTopVO) { // 修改文章置顶状态 Article article = Article.builder().id(articleTopVO.getId()).isTop(articleTopVO.getIsTop()).build(); articleDao.updateById(article); } @Override public void updateArticleDelete(DeleteVO deleteVO) { // 修改文章逻辑删除状态 List
articleList = deleteVO.getIdList().stream().map(id -> Article.builder() .id(id) .isTop(FALSE) .isDelete(deleteVO.getIsDelete()) .build()) .collect(Collectors.toList()); this.updateBatchById(articleList); } @Transactional(rollbackFor = Exception.class) @Override public void deleteArticles(List articleIdList) { // 删除文章标签关联 articleTagDao.delete(new LambdaQueryWrapper().in(ArticleTag::getArticleId, articleIdList)); // 删除文章 articleDao.deleteBatchIds(articleIdList); } @Override public List exportArticles(List articleIdList) { // 查询文章信息 List
articleList = articleDao.selectList(new LambdaQueryWrapper
() .select(Article::getArticleTitle, Article::getArticleContent) .in(Article::getId, articleIdList)); // 写入文件并上传 List urlList = new ArrayList<>(); for (Article article : articleList) { try (ByteArrayInputStream inputStream = new ByteArrayInputStream(article.getArticleContent().getBytes())) { String url = uploadStrategyContext.executeUploadStrategy(article.getArticleTitle() + FileExtEnum.MD.getExtName(), inputStream, FilePathEnum.MD.getPath()); urlList.add(url); } catch (Exception e) { log.error(StrUtil.format("导入文章失败,堆栈:{}", ExceptionUtil.stacktraceToString(e))); throw new BizException("导出文章失败"); } } return urlList; } @Override public List listArticlesBySearch(ConditionVO condition) { return searchStrategyContext.executeSearchStrategy(condition.getKeywords()); } @Override public ArticleVO getArticleBackById(Integer articleId) { // 查询文章信息 Article article = articleDao.selectById(articleId); // 查询文章分类 Category category = categoryDao.selectById(article.getCategoryId()); String categoryName = null; if (Objects.nonNull(category)) { categoryName = category.getCategoryName(); } // 查询文章标签 List tagNameList = tagDao.listTagNameByArticleId(articleId); // 封装数据 ArticleVO articleVO = BeanCopyUtils.copyObject(article, ArticleVO.class); articleVO.setCategoryName(categoryName); articleVO.setTagNameList(tagNameList); return articleVO; } /** * 更新文章浏览量 * * @param articleId 文章id */ public void updateArticleViewsCount(Integer articleId) { // 判断是否第一次访问,增加浏览量 Set articleSet = CommonUtils.castSet(Optional.ofNullable(session.getAttribute(ARTICLE_SET)).orElseGet(HashSet::new), Integer.class); if (!articleSet.contains(articleId)) { articleSet.add(articleId); session.setAttribute(ARTICLE_SET, articleSet); // 浏览量+1 redisService.zIncr(ARTICLE_VIEWS_COUNT, articleId, 1D); } } /** * 保存文章标签 * * @param articleVO 文章信息 */ private void saveArticleTag(ArticleVO articleVO, Integer articleId) { // 编辑文章则删除文章所有标签 if (Objects.nonNull(articleVO.getId())) { articleTagDao.delete(new LambdaQueryWrapper().eq(ArticleTag::getArticleId, articleVO.getId())); } // 添加文章标签 List tagNameList = articleVO.getTagNameList(); if (CollectionUtils.isNotEmpty(tagNameList)) { // 查询已存在的标签 List existTagList = tagService.list(new LambdaQueryWrapper().in(Tag::getTagName, tagNameList)); List existTagNameList = existTagList.stream().map(Tag::getTagName).collect(Collectors.toList()); List existTagIdList = existTagList.stream().map(Tag::getId).collect(Collectors.toList()); // 对比新增不存在的标签 tagNameList.removeAll(existTagNameList); if (CollectionUtils.isNotEmpty(tagNameList)) { List tagList = tagNameList.stream().map(item -> Tag.builder().tagName(item).build()).collect(Collectors.toList()); tagService.saveBatch(tagList); List tagIdList = tagList.stream().map(Tag::getId).collect(Collectors.toList()); existTagIdList.addAll(tagIdList); } // 提取标签id绑定文章 List articleTagList = existTagIdList.stream().map(item -> ArticleTag.builder() .articleId(articleId) .tagId(item) .build()) .collect(Collectors.toList()); articleTagService.saveBatch(articleTagList); } } }

六、获取源码

 大家点赞收藏关注评论啦 、关注下方公众号获取联系方式

你可能感兴趣的:(Java项目案例,spring,boot,vue.js)