『博客开发日记』之评论列表接口的实现

本文最后更新于 2026年1月13日 晚上

评论列表接口的实现


评论列表接口的需求

要有父子评论

文章详情页面要展示这篇文章下的评论列表

articleId:文章id

pageNum: 页码

pageSize: 每页条数

评论列表

生成有关comment的类,并写出有关业务方法

CommentServiceImpl

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@Service("commentService")
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService
{

//查询评论列表(包括文章评论,友链评论和留言板评论)
@Override
public ResponseResult commentList(Integer type, Long articleId, Integer pageNum, Integer pageSize)
{
// 查询根评论
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Comment::getType, String.valueOf(type));
// 文章评论需要关联articleId
if (SystemConstants.COMMENT_TYPE_ARTICLE.equals(String.valueOf(type))) {
queryWrapper.eq(Comment::getArticleId, articleId);
}
queryWrapper.eq(Comment::getRootId, SystemConstants.COMMENT_ROOT_ID);
queryWrapper.eq(Comment::getStatus, SystemConstants.COMMENT_STATUS_NORMAL);
queryWrapper.orderByDesc(Comment::getCreateTime);

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

//转换为VO并设置Gravatar头像
List<CommentVo> commentVoList = toCommentVoList(page.getRecords());

//查询子评论
for (CommentVo commentVo : commentVoList) {
List<CommentVo> children = getChildren(commentVo.getId());
commentVo.setChildren(children);
}

return ResponseResult.okResult(new PageVo(commentVoList, page.getTotal()));
}

//获取子评论列表
private List<CommentVo> getChildren(Long rootId)
{
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Comment::getRootId, rootId);
queryWrapper.eq(Comment::getStatus, SystemConstants.COMMENT_STATUS_NORMAL);
queryWrapper.orderByAsc(Comment::getCreateTime);
List<Comment> comments = list(queryWrapper);
return toCommentVoList(comments);
}

//转换为CommentVo列表,并设置回复目标昵称和用户类型
private List<CommentVo> toCommentVoList(List<Comment> comments)
{
List<CommentVo> voList = BeanCopyUtils.copyBeanList(comments, CommentVo.class);
for (int i = 0; i < voList.size(); i++) {
CommentVo vo = voList.get(i);
Comment original = comments.get(i);
// 直接使用数据库中保存的头像
vo.setAvatar(original.getAvatar());
// 设置用户类型(用于显示博主标识)
// 优先通过邮箱判断是否为博主(支持匿名评论识别博主)
if (StringUtils.hasText(original.getEmail())
&& original.getEmail().equalsIgnoreCase(SystemConstants.BLOGGER_EMAIL)) {
vo.setUserType("1"); //博主标识
} else if (original.getUserId() != null) {
User user = userMapper.selectById(original.getUserId());
if (user != null) {
vo.setUserType(user.getUserType());
}
} else {
vo.setUserType("0"); //普通用户
}
// 设置回复目标昵称和用户类型
if (vo.getReplyToCommentId() != null && vo.getReplyToCommentId() > 0) {
Comment replyTo = getById(vo.getReplyToCommentId());
if (replyTo != null) {
vo.setReplyToCommentNickname(replyTo.getNickname());
// 判断被回复者是否为博主
if (StringUtils.hasText(replyTo.getEmail())
&& replyTo.getEmail().equalsIgnoreCase(SystemConstants.BLOGGER_EMAIL)) {
vo.setReplyToCommentUserType("1"); //博主或者管理员
} else if (replyTo.getUserId() != null){
User replyToUser = userMapper.selectById(replyTo.getUserId());
if (replyToUser != null) {
vo.setReplyToCommentUserType(replyToUser.getUserType());
}
} else {
vo.setReplyToCommentUserType("0"); //普通用户
}
}
}
}
return voList;
}
}

Comment

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
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("comment")
public class Comment extends Model<Comment> {
@TableId
//评论ID
private Long id;
//评论类型(0代表文章评论,1代表友链评论,2代表留言板评论
private String type;
//文章ID
private Long articleId;
//评论用户ID(登录用户)
private Long userId;
//昵称
private String nickname;
//邮箱
private String email;
//头像URL
private String avatar;
//评论内容
private String content;
//父评论ID(回复时使用)
private Long parentId;
//根评论ID
private Long rootId;
//回复目标用户ID
private Long replyToUserId;
//回复目标评论的ID
private Long replyToCommentId;
//点赞数
private Integer likeCount;
//状态(0-正常,1-待审核,2-已删除)
private String status;
//删除标志(0-未删除,1-已删除)
private String delFlag;
//创建时间
private Date createTime;

}

CommentVo

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
@Data
@AllArgsConstructor
@NoArgsConstructor
public class CommentVo {
//评论ID
private Long id;
//评论类型
private String type;
//文章ID
private Long articleId;
//昵称
private String nickname;
//头像URL
private String avatar;
//评论内容
private String content;
//根评论ID
private Long rootId;
//回复目标评论ID
private Long replyToCommentId;
//回复目标评论的用户昵称
private String replyToCommentNickname;
//点赞数
private Integer likeCount;
//创建时间
private Date createTime;
//子评论列表
private List<CommentVo> children;
}

CommentService

1
2
3
4
5
6
7
8
9
10
11
12
public interface CommentService extends IService<Comment> {

/**
* 获取评论列表(统一接口,支持文章评论、友链评论、留言板评论)
* @param type 评论类型(0-文章评论,1-友链评论,2-留言板评论)
* @param articleId 文章ID(type=0时必填)
* @param pageNum 页码
* @param pageSize 每页数量
* @return 评论列表
*/
ResponseResult commentList(Integer type, Long articleId, Integer pageNum, Integer pageSize);
}

CommentController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@RestController
@RequestMapping("/comment")
public class CommentController {

@Autowired
private CommentService commentService;

/**
* 获取评论列表(统一接口,支持文章评论、友链评论、留言板评论)
* @param type 评论类型(0-文章评论,1-友链评论,2-留言板评论)
* @param articleId 文章ID(type=0时必填)
* @param pageNum 页码
* @param pageSize 每页数量
* @return 评论列表
*/
@GetMapping("/commentList")
public ResponseResult commentList(@RequestParam Integer type,
@RequestParam(required = false) Long articleId,
@RequestParam Integer pageNum,
@RequestParam(defaultValue = "20") Integer pageSize) {
return commentService.commentList(type, articleId, pageNum, pageSize);
}

}

CommentMapper

1
2
3
public interface CommentMapper extends BaseMapper<Comment> {

}


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

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


预告

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

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

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


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


『博客开发日记』之评论列表接口的实现
http://example.com/2026/01/12/『博客开发日记』之评论列表接口的实现/
作者
云梦泽
发布于
2026年1月12日
更新于
2026年1月13日
许可协议