手写一个Hexo评论系统(二)

1,269 阅读8分钟

接着上一篇文章《手写一个 Hexo 评论系统(一)》,本篇文章主要是讲述记述评论系统实现的一些具体的设计方案与技术细节,方便以后修改或者重构。

2020-11-27

字数统计: 2.3k 字   |   阅读时长≈ 8 分

接着上一篇文章《手写一个 Hexo 评论系统(一)》,本篇文章主要是讲述记述评论系统实现的一些具体的设计方案与技术细节,方便以后修改或者重构。还有项目的部署问题,包括域名解析,Nginx 配置代理,云服务器选购的一些问题,选购服务器的坑是真的大,所以还是尽量选择大厂,稳定一点,好用一点就不用在乎多几那块钱嘛,而且根据自己的需要买配置还是比较划算的。

上次总共列出了如下的一些需求,根据这些需求来构想一下如何设计:

1、无需登录和验证码,直接评论即可

2、支持回复评论,且可无限递归回复

3、支持评论点赞,按照点赞排序,相同则按照时间排序

3、支持文章评分,显示评分人数和评分的平均分数

4、支持自己的评论被别人回复后可以邮件通知

5、支持评论后台管理系统,可以设置对应属性

6、支持评论导出,且高度 100% 可恢复数据

第一版得 UI 就长这样了,整体看起来风格比较简单,这也是我比较喜欢的风格。在文章加载后评论者会获得一个随机的昵称与头像,点击昵称可以切换昵称和头像,不过都是随机的而已。然后就是三个输入框,邮箱输入框、评论内容、文章评分。所以从整体上分成了两部分,一部分是用户信息和评论框,另一部分是具体评论展示区域,只要思考清楚了那么就可以开始了。

反正是做 PC 的 Web 端 UI 嘛,直接上 ElementUI 了,其实还真的挺适合的,尤其是在展示具体评论的时候按照时间线来展示最合适不过了,而且评分组件也很 OK,挺好看的,以后有时间再拿其他的组件库试试!

首先用户要一打开博客就可以获得随机头像和昵称,并且要保持这个用户的状态,所以整体思路如下:

首先对于一个从未打开过某篇博客的页面的用户来说,在浏览器端存储的 clientID 肯定是没有这个字段的,直接可以判定为新用户,所以去请求后台给一个 ClientID,并且分配随机的昵称和头像,把这个用户信息给落库,即使用户点击了刷新昵称和头像,但是 ClientID 并没有变,用户信息就这样得以保存了下来。如果浏览器已经存在了 ClientID,那么数据库里面也应该有对应的用户信息,所以就直接请求后台拿到用户数据即可。

第二个问题,后端怎么知道哪些评论是哪些博客的呢?其实这里使用了一个比较简略的方式,那就是直接根据 URL 来判断,相同的 URL 肯定是同一篇博客,但是这样做也是有缺陷的,就是在本地调试的时候是 localhost:8080 开头,但是远程是 zouchanglin.cn,这样在本地的测试数据就不会展示在正式的博客评论上。而且 zouchanglin.cn 和 zouchanglin.gitee.io 的评论并不相通,其实这是不合理的,应该把域名和剩余部分 URL 分离出来,这样通用性才会更好,这一点以后有时间了我会进行重构。

第三个问题,用户设置了邮箱那么怎么才能回复的时候相互通知呢?其实对这样的 Client 端设计,只要在用户填写了邮箱后,进行评论或者回复的操作,都可以设置邮箱,因为这样可以从很大程度上减少业务复杂度,只要你的邮箱出现变更,你的下一次评论想到得到通知的时候肯定会填写新的邮箱,这样就可以直接更新整个用户的邮箱,非常方便。

数据库使用 MySQL 8.0。首先是设计客户端信息表,也就是代表了用户信息 (主要包括了客户端 ID、Email、昵称、创建时间、头像、客户端 OS 等信息,其他字段等有需要再添加就 OK):

然后是文章信息表(主要字段是文章 URL、文章评论数),一条数据对应着一条博客,其中还有博客的评论总计数目,其实完全可以拿 URL 做主键,但是为了以后改动方便,目前还是决定自定义生成主键比较好一些。

然后是评论表,在这里我把评论分成了两种类型,一种是文章评论(主要字段是文章 ID、comment_parent 是无效字段、评论客户端 ID、评论内容、点赞数、创建时间、文章评分),也就是直接在文章下面的评论;另一种是子评论(主要字段是所在的文章评论 ID、评论客户端 ID、评论内容、点赞数、回复的客户端 ID、创建时间),这个就是文章评论的回复而已,当然无论是文章评论的子评论还是子评论的回复评论都被当作子评论来存储,所有分了两张表。唯一不同之处在于文章评论会存储自己是那一篇文章下的评论,而子评论会存储自己位于哪一条文章评论底下,而且会存储回复的 Client 是谁,所以还存储了 reply_client 这个字段。

点击此处直到后端代码仓库:gitee.com/zouchanglin…

后端系统还是基于 SpringBoot 搭建的后端服务,其实用的都是比较常见的技术,SpringMVC、SpringDataJPA 等,平时用的比较少的就是邮件发送服务,其实使用 spring-boot-starter-mail 来发送邮件简直不要太容易。需要注意的就是后端的跨域问题,需要配置一个允许跨域的配置类,但是后面部署的时候推荐使用 Nginx 同域部署,跨域问题也就不存在了,这是我目前最喜欢的方案。

1、云服务购买

还是比较推荐腾讯云 88 / 年,百度云太贵、阿里云用过了。腾讯云学生及还是可以买一买的,1 核 2G 的配置,其实完全够用了。刚开始脑子抽搐了买个天翼云,虽然才 77 元 / 年,但是难用的要死,而且最高速度也就 130K/s,这谁受得了这么慢的速度。还是宁愿多花几块钱买个好一点的呀,毕竟大厂靠谱。千万别买天翼云呀,千万别买天翼云呀,千万别买天翼云呀,重要的事情说三遍。

2、解决 HTTPS 问题

整个代码其实都已经写好了,就剩一个部署了,但是由于我的网站全是 HTTPS 的,所以评论系统也必须是 HTTPS 的,否则由于浏览器的安全策略会导致评论模块无法加载。于是乎通过域名解析了一个二级域名 comment.zouchanglin.cn 到服务器上,为这个二级域名申请一个证书,这样就完全 OK 了。

3、Nginx 部署

前端项目打包后直接使用 Nginx 部署一下,顺便把 HTTPS 证书配置一下,这样前端项目就可以 HTTPS 访问了,那么后端呢?不可能有https://comment.zouchanglin.cn:8080这种 URL 存在吧,所以还是通过 Nginx 很轻松的解决了这个问题,只要通过 Nginx 配置一个代理,100% 搞定,而且彻底、一劳永逸地解决了跨域问题,并且共享域名,还利用反向代理隐藏了后端地址,比较方便集中管理。

所以我个人还是非常推荐采用 Nginx 将前后端同域部署的,Nginx 真的帮了大忙!

后端项目 application.yml 配置如下,其实就是访问路径都变成了http://127.0.0.1:8080/api/....

server:
    port: 8080
    servlet:
        context-path: /api

前端项目记得关闭 History 模式,然后请求的基础 URL 就写成了:

axios.defaults.baseURL = 'https://comment.zouchanglin.cn/api/'

Nginx 的配置文件如下:

user  root;
worker_processes  2;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
	
    sendfile        on;
    keepalive_timeout  65;

    gzip  on;
	
    # HTTPS server
    server {
        listen       443 ssl;
        server_name  comment.zpuchanglin.cn;

        # 配置证书
        ssl_certificate      /opt/ssl_file/cn_chain.crt;
        ssl_certificate_key  /opt/ssl_file/cn_key.key;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        location / {           
            root /root/dist;
			try_files $uri $uri/ /index.html;
        }

		location ^~/api/ {
			proxy_pass http://127.0.0.1:8080;
		}
    }
}

集思广益,大家也可以评论留言…

  • 支持后台管理
  • 支持自适应移动端 (宽高度需调整)
  • 支持 Docker 一键部署
  • ……

原文地址:《手写一个 Hexo 评论系统(二)》