『博客开发日记』之前台和后台一些关于获取文章列表接口对文章所属分类检测的优化

本文最后更新于 2026年4月19日 下午

前台和后台一些关于获取文章列表接口对文章所属分类检测的优化


起因

在测试后台删除和修改(状态修改为禁用)分类时前后台对获取文章列表会出现关于 分类 的空指针异常

经过检测发现是在前后台中的获取文章列表接口在获取列表时没对分类的状态进行检测

应该做的检测是

获取文章列表时要获取状态正常的分类(正常且未删除)下的文章

为了减少对数据库的查询

应该在查文章表时使用子查询


代码实现

使用子查询

1
2
3
//查询分类状态正常的文章(使用子查询)
lambdaQueryWrapper.inSql(Article::getCategoryId,
"SELECT id FROM category WHERE status = '0' AND del_flag = '0'");

这样查出来的sql语句是这样的

1
2
3
4
5
6
7
SELECT * FROM article 
WHERE category_id IN (
SELECT id FROM category WHERE status = '0' AND del_flag = '0'
)
AND status = 0
ORDER BY is_top DESC, create_time DESC


再加到相应的方法中

前台 articleList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//分页查询文章列表
@Override
public ResponseResult articleList(Integer pageNum, Integer pageSize)
{
//查询条件
LambdaQueryWrapper<Article> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//只查询分类状态正常的文章(使用子查询)
lambdaQueryWrapper.inSql(Article::getCategoryId,
"SELECT id FROM category WHERE status = '0' AND del_flag = '0'");
//状态是正式发布的
lambdaQueryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
//置顶的文章要放在前面,isTop进行降序排序
lambdaQueryWrapper.orderByDesc(Article::getIsTop);
//按发布时间降序排序,越新发布的文章越靠前
lambdaQueryWrapper.orderByDesc(Article::getCreateTime);
//分页查询
Page<Article> page = new Page<>(pageNum, pageSize);
page(page, lambdaQueryWrapper);

//查询categoryName
List<Article> articles = page.getRecords();
//stream流方式查询
articles.forEach(article -> {
article.setCategoryName(categoryService.getById(article.getCategoryId()).getName());
//从redis中获取viewCount
Integer viewCount = redisCache.getCacheMapValue(SystemConstants.ARTICLE_VIEW_COUNT, article.getId().toString());
article.setViewCount(viewCount.longValue());
});

//stream流方式查询(方法1)
// articles.stream()
// .map(new Function<Article, Article>() {
// @Override
// public Article apply(Article article) {
// //获取分类id,查询分类信息,获取分类名称
// Category category = categoryService.getById(article.getCategoryId());
// String name = category.getName();
// //把分类名称设置给articles
// article.setCategoryName(name);
// return article;
// }
// });

//for循环方式查询
// //再通过articleId去查询articleName
// for (Article article : articles) {
// Category category = categoryService.getById(article.getCategoryId());
// article.setCategoryName(category.getName());
// }

//封装查询结果为Vo
List<ArticleListVo> articleListVos = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class);

//获取标签
fillArticleListTags(articleListVos);

PageVo pageVo = new PageVo(articleListVos, page.getTotal());
return ResponseResult.okResult(pageVo);
}

前台 articleListByTagId

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//根据标签id查询文章列表
@Override
public ResponseResult articleListByTagId(Long tagId, Integer pageNum, Integer pageSize)
{
//先通过ArticleTag关联表查询该标签下的所有文章ID
LambdaQueryWrapper<ArticleTag> articleTagWrapper = new LambdaQueryWrapper<>();
articleTagWrapper.eq(ArticleTag::getTagId, tagId);
List<ArticleTag> articleTagList = articleTagMapper.selectList(articleTagWrapper);

//获取文章ID列表
List<Long> articleIds = articleTagList.stream()
.map(ArticleTag::getArticleId)
.collect(Collectors.toList());

//查询条件
LambdaQueryWrapper<Article> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//如果有文章ID,则根据文章ID列表查询;否则查询条件会返回空结果
if (!articleIds.isEmpty()) {
lambdaQueryWrapper.in(Article::getId, articleIds);
} else {
//没有文章时返回空结果
lambdaQueryWrapper.eq(Article::getId, -1L);
}
//只查询分类状态正常的文章(使用子查询)
lambdaQueryWrapper.inSql(Article::getCategoryId,
"SELECT id FROM category WHERE status = '0' AND del_flag = '0'");
//状态是正式发布的
lambdaQueryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
//置顶的文章要放在前面
lambdaQueryWrapper.orderByDesc(Article::getIsTop);
//分页查询
Page<Article> page = new Page<>(pageNum, pageSize);
page(page, lambdaQueryWrapper);

//查询categoryName
List<Article> articles = page.getRecords();
articles.forEach(article ->
article.setCategoryName(categoryService.getById(article.getCategoryId()).getName())
);

//封装查询结果为Vo
List<ArticleListVo> articleListVos = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class);

PageVo pageVo = new PageVo(articleListVos, page.getTotal());
return ResponseResult.okResult(pageVo);
}

前台 getArchiveList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
public ResponseResult getArchiveList(Integer pageNum, Integer pageSize)
{
// 构建查询条件:查询所有已发布的文章,按创建时间倒序,查询分类状态正常的文章(使用子查询)
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL)
.eq(Article::getDelFlag, SystemConstants.NOT_DELETED)
.orderByDesc(Article::getCreateTime)
.inSql(Article::getCategoryId,
"SELECT id FROM category WHERE status = '0' AND del_flag = '0'");

// 分页查询
Page<Article> page = new Page<>(pageNum, pageSize);
articleMapper.selectPage(page, queryWrapper);

// 转换为 VO
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<ArchiveArticleVo> articleVos = page.getRecords().stream()
.map(article -> new ArchiveArticleVo(
article.getId(),
article.getTitle(),
sdf.format(article.getCreateTime())
))
.collect(Collectors.toList());

PageVo pageVo = new PageVo(articleVos, page.getTotal());
return ResponseResult.okResult(pageVo);
}

前台 getTagList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
 //前台获取标签列表
@Override
public ResponseResult getTagList()
{
//先去查询文章表状态为已发布的
LambdaQueryWrapper<Article> articleWrapper = new LambdaQueryWrapper<>();
//查询分类状态正常的文章(使用子查询)
articleWrapper.inSql(Article::getCategoryId,
"SELECT id FROM category WHERE status = '0' AND del_flag = '0'")
.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL);
List<Article> articleList = articleService.list(articleWrapper);

//获取已发布文章的ID集合
Set<Long> articleIds = articleList.stream()
.map(Article::getId)
.collect(Collectors.toSet());

//查询这些文章关联的标签
LambdaQueryWrapper<ArticleTag> articleTagWrapper = new LambdaQueryWrapper<>();
if (!articleIds.isEmpty()) {
articleTagWrapper.in(ArticleTag::getArticleId, articleIds);
} else {
//没有已发布文章时返回空结果
articleTagWrapper.eq(ArticleTag::getArticleId, -1L);
}
List<ArticleTag> articleTagList = articleTagMapper.selectList(articleTagWrapper);

//获取标签ID集合并去重
Set<Long> tagIds = articleTagList.stream()
.map(ArticleTag::getTagId)
.collect(Collectors.toSet());

//统计每个标签关联的文章数量
Map<Long, Long> tagArticleCountMap = articleTagList.stream()
.collect(Collectors.groupingBy(ArticleTag::getTagId, Collectors.counting()));

//查询标签详细信息
LambdaQueryWrapper<Tag> tagWrapper = new LambdaQueryWrapper<>();
if (!tagIds.isEmpty()) {
tagWrapper.in(Tag::getId, tagIds);
} else {
//没有标签时返回空结果
tagWrapper.eq(Tag::getId, -1L);
}
tagWrapper.eq(Tag::getDelFlag, SystemConstants.NOT_DELETED)
.eq(Tag::getStatus, SystemConstants.STATUS_NORMAL);
List<Tag> tagList = list(tagWrapper);

//转换为TagVo并设置文章数量
List<TagVo> tagVoList = tagList.stream()
.map(tag -> {
TagVo tagVo = BeanCopyUtils.copyBean(tag, TagVo.class);
tagVo.setArticleCount(tagArticleCountMap.getOrDefault(tag.getId(), 0L).intValue());
return tagVo;
})
.collect(Collectors.toList());

return ResponseResult.okResult(tagVoList);
}

后台 tagList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//获取标签列表
@Override
public ResponseResult tagList(Integer pageNum, Integer pageSize, TagListDto tagListDto)
{
//构建标签查询条件
LambdaQueryWrapper<Tag> tagQueryWrapper = new LambdaQueryWrapper<>();

//根据关键字模糊查询标签名称
if (StringUtils.hasText(tagListDto.getKeywords())) {
tagQueryWrapper.like(Tag::getName, tagListDto.getKeywords());
}

//只查询未删除的标签
tagQueryWrapper.eq(Tag::getDelFlag, SystemConstants.NOT_DELETED);

//按创建时间降序排序
tagQueryWrapper.orderByDesc(Tag::getCreateTime);

//分页查询
Page<Tag> page = new Page<>(pageNum, pageSize);
tagService.page(page, tagQueryWrapper);

//查询所有未删除且已发布和分类状态正常的文章
LambdaQueryWrapper<Article> articleQueryWrapper = new LambdaQueryWrapper<>();
articleQueryWrapper.eq(Article::getDelFlag, SystemConstants.NOT_DELETED)
.eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL)
.select(Article::getId)
.inSql(Article::getCategoryId,
"SELECT id FROM category WHERE status = '0' AND del_flag = '0'");
List<Article> validArticles = articleService.list(articleQueryWrapper);
Set<Long> validArticleIds = validArticles.stream()
.map(Article::getId)
.collect(Collectors.toSet());

//查询所有文章标签关联关系
List<ArticleTag> articleTagList = articleTagMapper.selectList(null);

//统计每个标签关联的未删除且已发布的文章数量
Map<Long, Long> tagArticleCountMap = articleTagList.stream()
.filter(articleTag -> validArticleIds.contains(articleTag.getArticleId()))
.collect(Collectors.groupingBy(ArticleTag::getTagId, Collectors.counting()));

//封装查询结果为Vo并设置文章数量
List<AdminTagListVo> tagListVos = page.getRecords().stream()
.map(tag -> {
AdminTagListVo tagListVo = BeanCopyUtils.copyBean(tag, AdminTagListVo.class);
tagListVo.setArticleCount(tagArticleCountMap.getOrDefault(tag.getId(), 0L).intValue());
return tagListVo;
})
.collect(Collectors.toList());

PageVo pageVo = new PageVo(tagListVos, page.getTotal());
return ResponseResult.okResult(pageVo);
}

后台 postsList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//获取文章列表
@Override
public ResponseResult postsList(Integer pageNum, Integer pageSize, PostsListDto postsListDto)
{
//构建查询条件
LambdaQueryWrapper<Article> queryWrapper = new LambdaQueryWrapper<>();

//根据分类id筛选
if (Objects.nonNull(postsListDto.getCategoryId()) && postsListDto.getCategoryId() > 0) {
queryWrapper.eq(Article::getCategoryId, postsListDto.getCategoryId());
}

//根据状态筛选
if (StringUtils.hasText(postsListDto.getStatus())) {
queryWrapper.eq(Article::getStatus, postsListDto.getStatus());
}

//根据关键词搜索(标题或摘要)
if (StringUtils.hasText(postsListDto.getKeywords())) {
queryWrapper.and(wrapper -> wrapper
.like(Article::getTitle, postsListDto.getKeywords())
.or()
.like(Article::getSummary, postsListDto.getKeywords())
);
}

//置顶的文章要放在前面
queryWrapper.orderByDesc(Article::getIsTop);
//按创建时间降序排序
queryWrapper.orderByDesc(Article::getCreateTime);

//分页查询
Page<Article> page = new Page<>(pageNum, pageSize);
articleService.page(page, queryWrapper);

//填充分类名称和浏览量
List<Article> articles = page.getRecords();
articles.forEach(article -> {
article.setCategoryName(categoryService.getById(article.getCategoryId()).getName());
//从redis中获取viewCount
Integer viewCount = redisCache.getCacheMapValue(SystemConstants.ARTICLE_VIEW_COUNT, article.getId().toString());
article.setViewCount(viewCount.longValue());
});

//封装查询结果为Vo
List<AdminPostsListVo> postsListVos = BeanCopyUtils.copyBeanList(page.getRecords(), AdminPostsListVo.class);

//填充标签信息
postsListVos.forEach(postsListVo -> {
List<TagVo> tags = getTagsByArticleId(postsListVo.getId());
postsListVo.setTags(tags);
});

PageVo pageVo = new PageVo(postsListVos, page.getTotal());
return ResponseResult.okResult(pageVo);
}




PS:该系列只做为作者学习开发项目做的笔记用

不一定符合读者来学习,仅供参考


预告

后续会记录博客的开发过程

每次学习会做一份笔记来进行发表

“一花一世界,一叶一菩提”


版权所有 © 2026 云梦泽
欢迎访问我的个人网站:https://hgt12.github.io/


『博客开发日记』之前台和后台一些关于获取文章列表接口对文章所属分类检测的优化
http://example.com/2026/04/18/『博客开发日记』之前台和后台一些关于获取文章列表接口对文章所属分类检测的优化/
作者
云梦泽
发布于
2026年4月18日
更新于
2026年4月19日
许可协议