此评论样式采用valine的样式,JavaScript由博主个人独立编写

目前不会公布源码(因为有些安全问题与一些小bug),这篇文章主要如果你想体验这个评论可以返回主页看公告栏。

问题

一张表实现评论评论
假设:
A==父评论
B==子评论
C==父评论
D==子评论
问题1:如何判断是子评论还是父评论
问题2:如何判断B评论是A的子评论
问题3:如何判断B评论不是C的子评论(如何区分开来)
问题4:如何判断回复评论时,回复的是子评论还是父评论
(如:如果我回复的是A评论,那么我(我是D)的这条评论就是A的子评论,要是我回复了B,如何将我(我是D)显示在在A评论下)
还有几个小问题就不列举了

正文

Maven依赖

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
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

创建数据表

数据库表结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE `comment` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`artitle` BIGINT(20) NOT NULL DEFAULT '0' COMMENT '关联的blog主键', # 根据需求使用,去过你只是单纯的做个人主页留言的话,每个人评论全部设置为1即可,当然你也可以直接删除
`nick` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '昵称',
`mail` VARCHAR(100) NOT NULL DEFAULT '' COMMENT '邮箱',
`link` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '网址',
`content` VARCHAR(200) NOT NULL DEFAULT '' COMMENT '内容',
`commentatorIP` VARCHAR(20) NOT NULL DEFAULT '' COMMENT 'ip地址',
`createTime` DATETIME DEFAULT NULL COMMENT '评论时间',
`isReply` INT(4) BIGINT '0' COMMENT '是否是回复 0为否 1为是',
`commentStatus` BIGINT(4) NOT NULL DEFAULT '0' COMMENT '是否审核通过 0-未审核 1-审核通过',
`commentUrl` VARCHAR(100) NOT NULL COMMENT '评论对应的页面地址',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=53 DEFAULT CHARSET=utf8;

关键代码

实体类、Service接口、ApiUtil类(调用QQ头像、Gravatar、QQ昵称等api)、MD5Util类(MD5加密)、PatternUtil类(正则表达式类,判断邮箱、网站地址等)js,css等
以上代码将忽略 CommentMapper.xml也将忽略,本文只围绕文章开头的几个问题进行处理,并不展示回复等功能(就insert操作很简单)

project1
project2

CommentMapper.java(接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author Lete乐特
* @createDate 2020- 12-25 19:17
*/
@Mapper
public interface CommentMapper {

// 查询总数
int count(); // 上面说了,只展示评论

// 回复
int reply(Comment comment); // 上面说了,只展示评论,只需下面两个方法即可

// 查询父评论
@Select("select * from comment where isReply=0 AND commentStatus=1 ORDER BY createTime DESC")
List<Comment> CommentListf();
// 查询子评论
@Select("select * from comment where isReply!=0 AND commentStatus=1 ORDER BY createTime ")
List<Comment> CommentListz();
}

CommentServiceImpl.java(实现类)

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
/**
* @author Lete乐特
* @createDate 2020- 12-25 19:22
*/
@Service
public class CommentServiceImpl implements CommentService {

@Autowired(required=false)
CommentMapper mapper;

@Override // 查询总记录数
public int count() {
return mapper.count();
}

@Override // 回复
public int reply(Comment comment) {
// 判断是否是父评论
if(comment.getIsReply()==null)comment.setIsReply(0);
if(comment.getId()!=0)comment.setIsReply(comment.getId());
comment.setCreateTime(new Date());
comment.setCommentStatus(0);
return mapper.reply(comment);
}

//-------------------------上方两个功能请忽略(虽然展示此代码无意义)-----------------------------------

@Override // 查询父评论
public List<Comment> CommentListf() {
return mapper.CommentListf();
}

@Override // 查询子评论
public List<Comment> CommentListz() {
return mapper.CommentListz();
}
}

ControllerComment.java(Controller层)

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
/**
* @author Lete乐特
* @createDate 2020- 12-25 19:36
*/
@Controller
public class ControllerComment {

@Autowired(required = false)
private CommentServiceImpl commentService;

// 获取Mail 根据Mail获取评论头像
private List<Comment> Mail(List<Comment> list) throws IOException, ParseException {
for (Comment comment: list){
// 判断Mail true: QQavatar false: Gravatar
if(PatternUtil.isQQEmail(comment.getMail())){ //PatternUtil之前描述了(正则表达式类,判断是否是qq邮箱)
// 获取@之前的内容(获取QQ号)
Integer qq = Integer.valueOf(comment.getMail().substring(0, comment.getMail().indexOf("@")));
// 得到qq号后将qq放到ApiUtil类里(获取qq头像并重新set)
comment.setMail(ApiUtil.getHeadImage(qq));
}else{
// 不是qq邮箱则获取Gravatar头像
comment.setMail(ApiUtil.getGravatar(MD5Util.MD5Encode(comment.getMail())));
}
}
// 最后返回
return list;
}

@RequestMapping("/")
public String comment(Model mod) throws IOException, ParseException {
// 获取评论总数(只统计父评论)
mod.addAttribute("count",commentService.count());
// 获取父评论
List<Comment> listf = commentService.CommentListf();
mod.addAttribute("commentsf",Mail(listf)); // 查询信息,根据邮箱进行获取头像(传入上方的Mail方法)
// 获取子评论
List<Comment> listz = commentService.CommentListz();
mod.addAttribute("commentsz",Mail(listz));// 查询信息,根据邮箱进行获取头像(传入上方的Mail方法)
return "index";
}
}

最后在template下新建index.html
index.html

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
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script>
<div id="post-comment">
<div class="comment-head">
<div class="comment-headline">
<i class="fas fa-comments fa-fw"></i>
<span> 评论</span>
</div>
</div>
<div class="comment-wrap">
<div>
<div class="vcomment v" id="vcomment">
<div class="vpanel">
</div>
<div class="vcount" style="display: block; color: #e58a8a;">
<span class="vnum" th:text="${count}"></span> 评论</div>
<div class="vcards">
<!-- 循环each父评论 -->
<th:block th:each="listf:${commentsf}">
<div class="vcard">
<!--判断是否是博主邮箱头像地址true==显示博主头像false==使用api转换头像 昵称、子评论、子评论一致-->
<img class="vimg lazyload"
th:src="${listf.getMail()}"
referrerpolicy="no-referrer">
<div class="vh">
<div class="vhead">
<a class="vnick" rel="nofollow" th:href="${listf.getLink()}" target="_blank" th:text="${listf.getNick()}"></a>
</div>
<div class="vmeta">
<span style="color: #828282;" class="vtime" th:text="${#dates.format(listf.getCreateTime(),'yyyy-MM-dd HH:mm')}"></span>
<span class="vat" th:value="${listf.getId()}">回复</span>
</div>
<div class="vcontent" data-expand="查看更多..." th:utext="${listf.getContent()}">
</div>
<div class="vreply-wrapper"></div>
<!--循环each子评论-->
<th:block th:each="listz:${commentsz}">
<!--判断子回复是否与父id一致 在此出区分父评论,在评论-->
<div class="vquote" th:if="${listz.getIsReply()}==${listf.getId()}" >
<div class="vcard">
<img class="vimg lazyload"
th:src="${listz.getMail()}"
referrerpolicy="no-referrer">
<div class="vh">
<div class="vhead">
<a class="vnick" th:href="${listz.getLink()}" th:text="${listz.getNick()}"></a>

</div>
<div class="vmeta">
<span style="color: red;" class="vtime"th:text="${#dates.format(listz.getCreateTime(),'yyyy-MM-dd HH:mm')}"></span>
<span class="vat" th:value="${listf.getId()}">回复</span>
</div>
<div class="vcontent" data-expand="查看更多..." th:utext="${listz.getContent()}"></div>
<div class="vreply-wrapper"></div>
</div>
</div>
</div>
</th:block>
</div>
</div>
</th:block>
</div>
<div class="vload-top text-center" style="display:none;">
<i class="vspinner" style="width:30px;height:30px;"></i>
</div>
<div class="vcards"></div>
<div class="vload-bottom text-center" style="display:none;">
<i class="vspinner" style="width:30px;height:30px;"></i>
</div>
<div class="vempty" style="display: block;">
来发评论吧~
</div>
<div class="vpage txt-center" style="display:none">
<button type="button" class="vmore vbtn">加载更多...</button>
</div>
</div>
</div>
</div>
</div>
<link rel="stylesheet" href="/font/comment.css">
<link rel="stylesheet" href="/css/Comment.css">
<script src="/js/article.js"></script>
</body>
</html>

运行后的效果图
Comment

看不懂?很迷?
非常抱歉,由于目前部分功能为完善(其实我觉得完善了,但总感觉哪里不对劲,手动捂嘴笑),待处理完毕会发布到Github上