氢论坛:评论系统

前言

终于把 氢论坛 的评论系统完成的七七八八了,卡了我很久很久,一直在想怎样才能简洁, 优美地弄出来一个评论系统.然而写着写着发现不知不觉中反而变得愚钝而麻烦了.前前后后花了将近一周时间(期间在写其他模块,闲暇的时候就会想一想关于评论的实现),最后也没有很满意.

但是不管怎么样,功能终归是实现了.其中还有一部分未完成,比如说@某人,以及评论区的 markdown语法解析 .不过这些都不是什么问题了,所以暂且忽略掉这些细枝末节,就当我完成了叭.

此文用来记录下我的设计思路及过程,毕竟是第一次写.权当借鉴啦~

演示

gif

静态图



后端设计

一开始,我想的是评论共分成四级,每一级多一点缩进,这样显示层次. 不过在看了看 bilibili, 掘金, CSDN 等一些网站后发现基本上都是用的二级.就也改成二级的了.父评论作为对文章的评论,如上图的”我来评论一下“和”在来一下“.子评论是对评论的回复.如上图无名氏bestsort的回复.这里我把父评论和子评论单独拆分成了两个表,其主要结构如下:

1
2
3
4
5
6
7
8
9
10
11
#父评论
create table comment_parent
(
id bigint auto_increment primary key ,
comment_by_id bigint,
question_id bigint,
gmt_modified bigint,
gmt_create bigint,
content varchar(255),
like_count bigint default 0
);
1
2
3
4
5
6
7
8
9
10
#子评论
create table comment_kid
(
id bigint auto_increment primary key ,
comment_by_id bigint,
pid bigint,
gmt_modified bigint,
gmt_create bigint,
content varchar(255)
);

其实起初我是放在一个表里的,但是那样的话把对应文章的评论取出来后还得在后台进行排序,然后在进行归类处理.但是排序本身的时间复杂度就是 $O(nlog(n))$的,而且把评论放在一个表里的话子评论会占据大量无用空间,如果想要加速就得依靠索引.但是添加联合索引其实没有太大必要,而且也没有拆开后分别添加索引来的快.想了想(其实就是踩了坑)最终还是把表拆成了上面这样子,然后 以 comment_parent为驱动表,comment_kid 为被驱动表去查询.因为我用了 mybatis, 所以可以直接将结果映射成 CommentParent实体中嵌套List<CommentKid>的形式. sql查询语句如下(知道最好不用select *,但是我真的懒得写了,目前来说这个不足以影响到性能).

1
2
3
4
5
6
7
8
9
10
11
12
13
select
comment_parent.*,
kid.id kid_id,
kid.pid pid,
kid.comment_by_id kid_comment_by_id,
kid.content kid_content,
kid.gmt_create kid_gmt_create,
kid.gmt_modified kid_gmt_modified
from
comment_parent left join comment_kid kid on kid.pid = comment_parent.id
where
comment_parent.question_id = #{id,jdbcType=BIGINT}
order by gmt_create desc,kid_gmt_create

前端

我觉得前端才是我写这个的最大掣肘…因为我是真的不会阿QAQ.

因为父评论涉及到点赞功能,所以要在 HTML 中嵌入每一个评论在数据库中对应的id, 还得根据选择器确定到底选的是哪一条,还得判断是父评论还是子评论. 偏偏我又不想循环去每个评论上都加一个id然后直接用id选择器去选择对应评论…这就导致了大部分时间我都在想怎么捣鼓这里…最后无奈之下,采用了以下方案:

在一个选项点击后,用jQuery去取出它父级/祖父内存着的value(因为无论是父评论还是子评论结构都是固定的.但是这样有个很不好的点就是修改结构后得连这块一起修改,可以说是增加了耦合度吧),然后通过jQuery去修改弹出框的内容.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
<li style="margin: 0" value="4">
<div class="comment-main-level">
<div class="comment-avatar"><img src="https://avatars1.githubusercontent.com/u/43701948?v=4" alt=""></div>
<div class="comment-box">
<div class="comment-head">
<h6 class="comment-name by-author ">
<a href="https://github.com/bestsort">bestsort</a>
</h6>
<span class="comment-post pull-right" style="top: 0">
<input name="commentId" value="4" hidden="true"> <span class="option hover-point">
<i class="iconfont icon-zan">
点赞(0)
</i>
</span>
<span class="option hover-point" style="margin-right: 25px">
<i class="iconfont icon-fenxiang"></i>
评论
</span>
</span>
</div>
<div class="comment-content">我来评论一下
<span> 2019-10-29 14:57:36</span>
</div>
</div>
</div>
<!-- Respuestas de los comentarios -->
<ul class="comments-list reply-list">
<li>
<!-- Avatar -->
<div class="comment-avatar"><img src="https://alicdn.bestsort.cn/icon/default.jpeg" alt=""></div>
<!-- Contenedor del Comentario -->
<div class="comment-box">
<div class="comment-head">
<h6 class="comment-name "><a href="null">无名氏</a></h6>
<span class="comment-post pull-right">
<input value="1" hidden="true">
<span class="option hover-point" style="margin-right: 25px;top: 0">
<i class="iconfont icon-fenxiang"></i>
评论
</span>
</span>
</div>
<div class="comment-content">默认账户再测试一下
<span>2019-10-29 14:58:17</span>
</div>
</div>
</li>
</ul>
</li>

jQuery 相关部分

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
$(document).on('click',".comment-post>span",function () {
/**
* 子评论
*/
if ($(this).parents("li").length === 2){
let own = $(this).prevAll("input");
let pid = $(this).parents("li")[1];
reply_comment(own,pid);
}
/**
* 父评论
*/
else {
let commentId = $(this).prevAll("input").val();
/**
* 评论
*/
if ($(this).next().length === 0){
reply_comment($(this).prevAll("input"));
}
/**
* 点赞
*/
else{
let count = $(this).children().text().replace(/[^\d]/g, '');
let isActive = $(this).children().hasClass("on");
thumb_up_comment(commentId,isActive,count,$(this));
}
}
});

当然整体肯定不止才这么点,我只是在这里提供一下思路抛砖引玉.想进一步了解的可以去我的仓库查看源码.
其中后端处理部分主要位于CommentService.java

前端处理部分主要位于question-details.js.


氢论坛目前还在搭建中,不介意的话赏个 star 鼓励一下呀~

觉得文章不错的话可以请我喝一杯茶哟~
  • 本文作者: bestsort
  • 本文链接: https://bestsort.cn/2019/10/29/1029/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-SA 许可协议。转载请注明出处!并保留本声明。感谢您的阅读和支持!
-------------本文结束感谢您的阅读-------------