本文介绍了30.博客系统项目开发之评论模块功能实现求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。
对技术面试,学习经验等有一些体会,在此分享。
Spring Boot 博客系统项目开发之评论模块功能实现
本项目的开源仓库地址为:https://github.com/ZHENFENG13/My-Blog
除 My-Blog 项目外,我也在维护另外一个开源项目,仓库地址为:https://github.com/newbee-ltd/newbee-mall
感兴趣的朋友可以去关注一下。
文章总览
在文章详情页面制作的那节实验课程中,我们提到了文章详情页的页面中还会有文章的评论列表区域和文章评论的输入区域,但是在那个实验中并没有直接讲解和介绍,主要是考虑到篇幅的缘故,评论模块不仅仅是详情页的评论添加和列表,还有后台管理系统中的评论管理功能模块,因此就选择单独用一个章节来讲解博客系统的评论模块,包括详情页的功能开发和后台管理系统中的评论管理页面和相关接口的开发。
知识点
- 评论模块介绍
- 评论模块表结构设计及接口实现
- 评论模块页面设计及编码
- 评论功能测试
环境
- JDK 1.8 或者更高版本
- Spring Boot 2.1.0-RELEASE
- Maven 3+
评论模块简介
相信大家对于评论模块并不陌生,不管是电商网站还是视频及音乐网站,评论模块都是不可或缺的,我们通过评论可以看到大家对于一个当前这个事物的评价和建议,通过这些评论可以大致的了解该事物的一些属性,甚至你会觉得评论数据也和详情模块一样,帮助我们更加清晰的了解到眼前的这个事物,但是二者的区别还是很大的,详情模块是运营人员设置的,而评论数据则是用户留下的数据,二者的来源就有很大的不同。回到我们的博客系统中来,论坛、博客、公众号等也都会有的模块,用户在文章下通过文字或者图片来留下自己的评论信息,通过这些文案来表达自己的问题和建议,甚至对文章中提到的内容进行拓展和完善,因此评论模块的开发也是必须的。
在简单的介绍了评论模块后,我们来看一下本系统中的评论页面是怎样的,评论的相关功能模块主要出现在两个地方:
-
其一是博客详情页中会展示评论信息,主要包括评论的列表展示和评论提交;
评论列表:
评论提交:
-
其二是后台管理系统中会有评论数据的管理页面,主要包括评论列表、评论审核和评论回复的功能。
表结构设计及 Mapper 文件自动生成
在进行接口设计和具体的功能实现前,首先将表结构确定下来,主要通过观察前文中几张评论模块的图片来确认一些字段的设置,首先是详情页中的评论展示功能,可以看出评论信息是在某一篇博客下评论,因此需要关联博客主键 id, 同时该系统并没有设置用户体系,而是谁都可以评论,用户评论时需要填写一些信息和验证,评论提交就是普通字符串信息提交,包括一些基础的字段填写,之后是评论信息管理页面,有审核功能,所以评论数据应该有状态字段,有回复功能但是并没有开发多级回复,所以还有一个回复内容字段。
总结下来,评论表中重要的字段如下:
- 关联的文章主键 blogId
- 评论者名称
- 评论人的邮箱
- 评论内容
- 回复内容
- 审核状态
基于以上分析设计的评论表的 SQL 语句如下,直接执行如下 SQL 语句即可:
USE `my_blog_db`; DROP TABLE IF EXISTS `tb_blog_comment`; CREATE TABLE `tb_blog_comment` ( `comment_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', `blog_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '关联的blog主键', `commentator` varchar(50) NOT NULL DEFAULT '' COMMENT '评论者名称', `email` varchar(100) NOT NULL DEFAULT '' COMMENT '评论人的邮箱', `website_url` varchar(50) NOT NULL DEFAULT '' COMMENT '网址', `comment_body` varchar(200) NOT NULL DEFAULT '' COMMENT '评论内容', `comment_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '评论提交时间', `commentator_ip` varchar(20) NOT NULL DEFAULT '' COMMENT '评论时的ip地址', `reply_body` varchar(200) NOT NULL DEFAULT '' COMMENT '回复内容', `reply_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '回复时间', `comment_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否审核通过 0-未审核 1-审核通过', `is_deleted` tinyint(4) DEFAULT '0' COMMENT '是否删除 0-未删除 1-已删除', PRIMARY KEY (`comment_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
加了一些时间字段和一些固定的字段,把表结构导入到数据库中即可,接下来使用 MyBatis-Generator 插件将该表对应的 Mapper 文件及对应的实体类和 Dao 层接口生成出来并针对 is_deleted 字段对部分 SQL 进行修改即可,可参考前文中的知识点进行修改。
评论管理页面制作
导航栏
首先在左侧导航栏中新增评论管理页的导航按钮,在 sidebar.html 文件中新增如下代码:(注:完整代码位于 resources/templates/admin/sidebar.html)
<li class="nav-item"> <a th:href="@{/admin/comments}" th:class="${path}=='comments'?'nav-link active':'nav-link'" > <i class="fa fa-comments nav-icon" aria-hidden="true"></i> <p> 评论管理 </p> </a> </li>
点击后的跳转路径为 /admin/comments,之后我们新建 Controller 来处理该路径并跳转到对应的页面。
Controller 处理跳转
首先在 controller/admin 包下新建 CommentController.java,之后新增如下代码:
package com.site.blog.my.core.controller.admin; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping("/admin") public class CommentController { @GetMapping("/comments") public String list(HttpServletRequest request) { request.setAttribute("path", "comments"); return "admin/comment"; } }
该方法用于处理 /admin/comments 请求,并设置 path 字段,之后跳转到 admin 目录下的 comment.html 中。
comment.html 页面制作
接下来就是博客编辑页面的模板文件制作了,在 resources/templates/admin 目录下新建 comment.html,并引入对应的 js 文件和 css 样式文件,代码如下:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <header th:replace="admin/header::header-fragment"></header> <body class="hold-transition sidebar-mini"> <div class="wrapper"> <!-- 引入页面头header-fragment --> <div th:replace="admin/header::header-nav"></div> <!-- 引入工具栏sidebar-fragment --> <div th:replace="admin/sidebar::sidebar-fragment(${path})"></div> <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"></div> <!-- /.container-fluid --> </div> <!-- Main content --> <div class="content"> <div class="container-fluid"> <div class="card card-primary card-outline"> <div class="card-header"> <h3 class="card-title">评论管理</h3> </div> <!-- /.card-body --> </div> </div> </div> </div> <div th:replace="admin/footer::footer-fragment"></div> </div> <!-- jQuery --> <script th:src="@{/admin/plugins/jquery/jquery.min.js}"></script> <!-- jQuery UI 1.11.4 --> <script th:src="@{/admin/plugins/jQueryUI/jquery-ui.min.js}"></script> <!-- Bootstrap 4 --> <script th:src="@{/admin/plugins/bootstrap/js/bootstrap.bundle.min.js}" ></script> <!-- AdminLTE App --> <script th:src="@{/admin/dist/js/adminlte.min.js}"></script> </body> </html>
至此,跳转逻辑处理完毕,演示效果如下:
评论模块接口设计及实现
接口介绍
关于接口的设计以及前后端交互数据的设计大家可以参考一下第 12 课中的内容,友链模块在后台管理系统中有 4 个接口,分别是:
- 评论列表分页接口
- 评论审核接口
- 评论回复接口
- 删除评论接口
接下来讲解每个接口具体的实现代码,首先在 controller/admin 包下新建 CommentController.java,并在 service 包下新建业务层代码 CommentService.java 及实现类,之后参照接口分别进行功能实现。
评论列表分页接口
列表接口负责接收前端传来的分页参数,如 page 、limit 等参数,之后将数据总数和对应页面的数据列表查询出来并封装为分页数据返回给前端。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/list,请求方法为 GET,代码如下:
/** * 评论列表 */ @GetMapping("/comments/list") @ResponseBody public Result list(@RequestParam Map<String, Object> params) { if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(commentService.getCommentsPage(pageUtil)); }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public PageResult getCommentsPage(PageQueryUtil pageUtil) { List<BlogComment> comments = blogCommentMapper.findBlogCommentList(pageUtil); int total = blogCommentMapper.getTotalBlogComments(pageUtil); PageResult pageResult = new PageResult(comments, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<select id="findBlogCommentList" parameterType="Map" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from tb_blog_comment where is_deleted=0 order by comment_id desc <if test="start!=null and limit!=null"> limit #{start},#{limit} </if> </select> <select id="getTotalBlogComments" parameterType="Map" resultType="int"> select count(*) from tb_blog_comment where is_deleted=0 </select>
SQL 语句在 BlogCommentMapper.xml 文件中,一般的分页也就是使用 limit 关键字实现,获取响应条数的记录和总数之后再进行数据封装,这个接口就是根据前端传的分页参数进行查询并返回分页数据以供前端页面进行数据渲染,评论管理页面的列表是获取全部数据,并不会根据 blog_id 进行过滤。
评论审核接口
审核接口负责接收前端的审核请求,处理前端传输过来的数据后,将这些评论的状态值改为已审核,我们将接受的参数设置为一个数组,可以同时审核多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/checkDone ,请求方法为 POST,代码如下:
@PostMapping("/comments/checkDone") @ResponseBody public Result checkDone(@RequestBody Integer[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.checkDone(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("审核失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean checkDone(Integer[] ids) { return blogCommentMapper.checkDone(ids) > 0; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<update id="checkDone"> update tb_blog_comment set comment_status=1 where comment_id in <foreach item="id" collection="array" open="(" separator="," close=")"> #{id} </foreach> and comment_status = 0 </update>
接口的请求路径为 /comments/checkDone,并使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 checkDone() 批量审核方法修改这些记录的审核状态字段。
评论回复接口
评论回复接口可以在评论审核通过之后调用,目的就是给需要回复的评论添加回复字段内容,代码如下
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/checkDone ,请求方法为 POST,代码如下:
@PostMapping("/comments/reply") @ResponseBody public Result checkDone(@RequestParam("commentId") Long commentId, @RequestParam("replyBody") String replyBody) { if (commentId == null || commentId < 1 || StringUtils.isEmpty(replyBody)) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.reply(commentId, replyBody)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("回复失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean reply(Long commentId, String replyBody) { BlogComment blogComment = blogCommentMapper.selectByPrimaryKey(commentId); //blogComment不为空且状态为已审核,则继续后续操作 if (blogComment != null && blogComment.getCommentStatus().intValue() == 1) { blogComment.setReplyBody(replyBody); blogComment.setReplyCreateTime(new Date()); return blogCommentMapper.updateByPrimaryKeySelective(blogComment) > 0; } return false; }
接口的请求路径为 /comments/reply,入参设置为选中的评论记录 id 以及需要回复的内容字段,参数验证通过后则调用 reply() 对表中的回复相关字段做修改,这里是做成了单条记录的回复,如果有需要的话可以适当的修改代码进行功能扩展。
删除评论接口
删除接口负责接收前端的评论删除请求,处理前端传输过来的数据后,将这些记录从数据库中删除,这里的“删除”功能并不是真正意义上的删除,而是逻辑删除,我们将接受的参数设置为一个数组,可以同时删除多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/delete,请求方法为 POST,代码如下:
@PostMapping("/comments/delete") @ResponseBody public Result delete(@RequestBody Integer[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.deleteBatch(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("刪除失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean deleteBatch(Integer[] ids) { return blogCommentMapper.deleteBatch(ids) > 0; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<update id="deleteBatch"> update tb_blog_comment set is_deleted=1 where comment_id in <foreach item="id" collection="array" open="(" separator="," close=")"> #{id} </foreach> </update>
使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 deleteBatch() 批量删除方法进行数据库操作,否则将向前端返回错误信息。
前端页面实现
功能按钮
评论管理模块我们也设计了常用的几个功能:评论审核、评论回复、评论删除,因此在页面中添加对应的功能按钮以及触发事件,代码如下:(注:完整代码位于 resources/templates/admin/comment.html)
<div class="grid-btn"> <button class="btn btn-success" onclick="checkDoneComments()"> <i class="fa fa-check"></i> 批量审核 </button> <button class="btn btn-info" onclick="reply()"> <i class="fa fa-reply"></i> 回复 </button> <button class="btn btn-danger" onclick="deleteComments()"> <i class="fa fa-trash-o"></i> 批量删除 </button> </div>
分别是批量审核按钮对应的触发事件是 checkDoneComments() 方法,回复按钮对应的触发事件是 reply() 方法,删除按钮对应的触发事件是 deleteComments() 方法,这些方法我们将在后续的代码实现中给出。
分页信息展示区域
页面中已经引入 JqGrid 的相关静态资源文件,需要在页面中展示分页数据的区域增加如下代码:
<table id="jqGrid" class="table table-bordered"></table> <div id="jqGridPager"></div>
页面效果
那么最终的静态页面效果展示如下图所示:
包括功能按钮、数据列表展示区域,此时只是静态效果展示,并没有与后端进行数据交互,接下来我们将结合 Ajax 和后端接口实现具体的功能。
评论管理模块前端功能实现
完成了页面展示的实现,接着完成相关的功能实现。请大家一定自行对照源码完成代码编写。
分页功能
在 resources/static/admin/dist/js 目录下新增 comment.js 文件,并添加如下代码:
$(function () { $('#jqGrid').jqGrid({ url: '/admin/comments/list', datatype: 'json', colModel: [ { label: 'id', name: 'commentId', index: 'commentId', width: 50, key: true, hidden: true, }, { label: '评论内容', name: 'commentBody', index: 'commentBody', width: 120, }, { label: '评论时间', name: 'commentCreateTime', index: 'commentCreateTime', width: 60, }, { label: '评论人名称', name: 'commentator', index: 'commentator', width: 60, }, { label: '评论人邮箱', name: 'email', index: 'email', width: 90 }, { label: '状态', name: 'commentStatus', index: 'commentStatus', width: 60, formatter: statusFormatter, }, { label: '回复内容', name: 'replyBody', index: 'replyBody', width: 120 }, ], height: 700, rowNum: 10, rowList: [10, 20, 50], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: '#jqGridPager', jsonReader: { root: 'data.list', page: 'data.currPage', total: 'data.totalPage', records: 'data.totalCount', }, prmNames: { page: 'page', rows: 'limit', order: 'order', }, gridComplete: function () { //隐藏grid底部滚动条 $('#jqGrid').closest('.ui-jqgrid-bdiv').css({ 'overflow-x': 'hidden' }); }, }); $(window).resize(function () { $('#jqGrid').setGridWidth($('.card-body').width()); }); function statusFormatter(cellvalue) { if (cellvalue == 0) { return '<button type="button" class="btn btn-block btn-secondary btn-sm" style="width: 80%;">待审核</button>'; } else if (cellvalue == 1) { return '<button type="button" class="btn btn-block btn-success btn-sm" style="width: 80%;">已审核</button>'; } } });
以上代码的主要功能为分页数据展示、字段格式化 jqGrid DOM 宽度的自适应,在页面加载时,调用 JqGrid 的初始化方法,将页面中 id 为 jqGrid 的 DOM 渲染为分页表格,并向后端发送请求,请求路径为 /admin/comments/list,该路径即友链分页列表接口,之后按照后端返回的 json 数据填充表格以及表格下方的分页按钮,可以参考第 15 个实验课程中 jqgrid 分页功能整合进行知识的理解,之后可以重启项目验证一下评论信息分页功能是否正常。
批量审核功能
游客在博客详情页提交的评论初始状态为未审核,因为这些游客输入的内容有些可能是无意义的字符串或者一些不适合显示的内容,这种就不需要显示在博客页面了,因此做了审核这个功能。
批量审核按钮的点击触发事件为 checkDoneComments(),在 comment.js 文件中新增如下代码:
/** * 批量审核 */ function checkDoneComments() { var ids = getSelectedRows(); if (ids == null) { return; } swal({ title: '确认弹框', text: '确认审核通过吗?', icon: 'warning', buttons: true, dangerMode: true, }).then((flag) => { if (flag) { $.ajax({ type: 'POST', url: '/admin/comments/checkDone', contentType: 'application/json', data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { swal('审核成功', { icon: 'success', }); $('#jqGrid').trigger('reloadGrid'); } else { swal(r.message, { icon: 'error', }); } }, }); } }); }
获取用户在 jqgrid 表格中选择的需要审核的所有记录的 id,之后将参数封装并向后端发送 Ajax 请求,请求地址为 comments/checkDone。
回复功能实现
回复按钮绑定了触发事件,我们需要在 comment.js 文件中新增 reply() 方法,方法中的实现为打开信息编辑框,下面我们来实现信息编辑框触发事件,代码如下:
“`html
Spring Boot 博客系统项目开发之评论模块功能实现
本项目的开源仓库地址为:https://github.com/ZHENFENG13/My-Blog
除 My-Blog 项目外,我也在维护另外一个开源项目,仓库地址为:https://github.com/newbee-ltd/newbee-mall
感兴趣的朋友可以去关注一下。
文章总览
在文章详情页面制作的那节实验课程中,我们提到了文章详情页的页面中还会有文章的评论列表区域和文章评论的输入区域,但是在那个实验中并没有直接讲解和介绍,主要是考虑到篇幅的缘故,评论模块不仅仅是详情页的评论添加和列表,还有后台管理系统中的评论管理功能模块,因此就选择单独用一个章节来讲解博客系统的评论模块,包括详情页的功能开发和后台管理系统中的评论管理页面和相关接口的开发。
知识点
- 评论模块介绍
- 评论模块表结构设计及接口实现
- 评论模块页面设计及编码
- 评论功能测试
环境
- JDK 1.8 或者更高版本
- Spring Boot 2.1.0-RELEASE
- Maven 3+
评论模块简介
相信大家对于评论模块并不陌生,不管是电商网站还是视频及音乐网站,评论模块都是不可或缺的,我们通过评论可以看到大家对于一个当前这个事物的评价和建议,通过这些评论可以大致的了解该事物的一些属性,甚至你会觉得评论数据也和详情模块一样,帮助我们更加清晰的了解到眼前的这个事物,但是二者的区别还是很大的,详情模块是运营人员设置的,而评论数据则是用户留下的数据,二者的来源就有很大的不同。回到我们的博客系统中来,论坛、博客、公众号等也都会有的模块,用户在文章下通过文字或者图片来留下自己的评论信息,通过这些文案来表达自己的问题和建议,甚至对文章中提到的内容进行拓展和完善,因此评论模块的开发也是必须的。
在简单的介绍了评论模块后,我们来看一下本系统中的评论页面是怎样的,评论的相关功能模块主要出现在两个地方:
-
其一是博客详情页中会展示评论信息,主要包括评论的列表展示和评论提交;
评论列表:
评论提交:
-
其二是后台管理系统中会有评论数据的管理页面,主要包括评论列表、评论审核和评论回复的功能。
表结构设计及 Mapper 文件自动生成
在进行接口设计和具体的功能实现前,首先将表结构确定下来,主要通过观察前文中几张评论模块的图片来确认一些字段的设置,首先是详情页中的评论展示功能,可以看出评论信息是在某一篇博客下评论,因此需要关联博客主键 id, 同时该系统并没有设置用户体系,而是谁都可以评论,用户评论时需要填写一些信息和验证,评论提交就是普通字符串信息提交,包括一些基础的字段填写,之后是评论信息管理页面,有审核功能,所以评论数据应该有状态字段,有回复功能但是并没有开发多级回复,所以还有一个回复内容字段。
总结下来,评论表中重要的字段如下:
- 关联的文章主键 blogId
- 评论者名称
- 评论人的邮箱
- 评论内容
- 回复内容
- 审核状态
基于以上分析设计的评论表的 SQL 语句如下,直接执行如下 SQL 语句即可:
USE `my_blog_db`; DROP TABLE IF EXISTS `tb_blog_comment`; CREATE TABLE `tb_blog_comment` ( `comment_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', `blog_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '关联的blog主键', `commentator` varchar(50) NOT NULL DEFAULT '' COMMENT '评论者名称', `email` varchar(100) NOT NULL DEFAULT '' COMMENT '评论人的邮箱', `website_url` varchar(50) NOT NULL DEFAULT '' COMMENT '网址', `comment_body` varchar(200) NOT NULL DEFAULT '' COMMENT '评论内容', `comment_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '评论提交时间', `commentator_ip` varchar(20) NOT NULL DEFAULT '' COMMENT '评论时的ip地址', `reply_body` varchar(200) NOT NULL DEFAULT '' COMMENT '回复内容', `reply_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '回复时间', `comment_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否审核通过 0-未审核 1-审核通过', `is_deleted` tinyint(4) DEFAULT '0' COMMENT '是否删除 0-未删除 1-已删除', PRIMARY KEY (`comment_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
加了一些时间字段和一些固定的字段,把表结构导入到数据库中即可,接下来使用 MyBatis-Generator 插件将该表对应的 Mapper 文件及对应的实体类和 Dao 层接口生成出来并针对 is_deleted 字段对部分 SQL 进行修改即可,可参考前文中的知识点进行修改。
评论管理页面制作
导航栏
首先在左侧导航栏中新增评论管理页的导航按钮,在 sidebar.html 文件中新增如下代码:(注:完整代码位于 resources/templates/admin/sidebar.html)
<li class="nav-item"> <a th:href="@{/admin/comments}" th:class="${path}=='comments'?'nav-link active':'nav-link'" > <i class="fa fa-comments nav-icon" aria-hidden="true"></i> <p> 评论管理 </p> </a> </li>
点击后的跳转路径为 /admin/comments,之后我们新建 Controller 来处理该路径并跳转到对应的页面。
Controller 处理跳转
首先在 controller/admin 包下新建 CommentController.java,之后新增如下代码:
package com.site.blog.my.core.controller.admin; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping("/admin") public class CommentController { @GetMapping("/comments") public String list(HttpServletRequest request) { request.setAttribute("path", "comments"); return "admin/comment"; } }
该方法用于处理 /admin/comments 请求,并设置 path 字段,之后跳转到 admin 目录下的 comment.html 中。
comment.html 页面制作
接下来就是博客编辑页面的模板文件制作了,在 resources/templates/admin 目录下新建 comment.html,并引入对应的 js 文件和 css 样式文件,代码如下:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <header th:replace="admin/header::header-fragment"></header> <body class="hold-transition sidebar-mini"> <div class="wrapper"> <!-- 引入页面头header-fragment --> <div th:replace="admin/header::header-nav"></div> <!-- 引入工具栏sidebar-fragment --> <div th:replace="admin/sidebar::sidebar-fragment(${path})"></div> <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"></div> <!-- /.container-fluid --> </div> <!-- Main content --> <div class="content"> <div class="container-fluid"> <div class="card card-primary card-outline"> <div class="card-header"> <h3 class="card-title">评论管理</h3> </div> <!-- /.card-body --> </div> </div> </div> </div> <div th:replace="admin/footer::footer-fragment"></div> </div> <!-- jQuery --> <script th:src="@{/admin/plugins/jquery/jquery.min.js}"></script> <!-- jQuery UI 1.11.4 --> <script th:src="@{/admin/plugins/jQueryUI/jquery-ui.min.js}"></script> <!-- Bootstrap 4 --> <script th:src="@{/admin/plugins/bootstrap/js/bootstrap.bundle.min.js}" ></script> <!-- AdminLTE App --> <script th:src="@{/admin/dist/js/adminlte.min.js}"></script> </body> </html>
至此,跳转逻辑处理完毕,演示效果如下:
评论模块接口设计及实现
接口介绍
关于接口的设计以及前后端交互数据的设计大家可以参考一下第 12 课中的内容,友链模块在后台管理系统中有 4 个接口,分别是:
- 评论列表分页接口
- 评论审核接口
- 评论回复接口
- 删除评论接口
接下来讲解每个接口具体的实现代码,首先在 controller/admin 包下新建 CommentController.java,并在 service 包下新建业务层代码 CommentService.java 及实现类,之后参照接口分别进行功能实现。
评论列表分页接口
列表接口负责接收前端传来的分页参数,如 page 、limit 等参数,之后将数据总数和对应页面的数据列表查询出来并封装为分页数据返回给前端。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/list,请求方法为 GET,代码如下:
/** * 评论列表 */ @GetMapping("/comments/list") @ResponseBody public Result list(@RequestParam Map<String, Object> params) { if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(commentService.getCommentsPage(pageUtil)); }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public PageResult getCommentsPage(PageQueryUtil pageUtil) { List<BlogComment> comments = blogCommentMapper.findBlogCommentList(pageUtil); int total = blogCommentMapper.getTotalBlogComments(pageUtil); PageResult pageResult = new PageResult(comments, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<select id="findBlogCommentList" parameterType="Map" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from tb_blog_comment where is_deleted=0 order by comment_id desc <if test="start!=null and limit!=null"> limit #{start},#{limit} </if> </select> <select id="getTotalBlogComments" parameterType="Map" resultType="int"> select count(*) from tb_blog_comment where is_deleted=0 </select>
SQL 语句在 BlogCommentMapper.xml 文件中,一般的分页也就是使用 limit 关键字实现,获取响应条数的记录和总数之后再进行数据封装,这个接口就是根据前端传的分页参数进行查询并返回分页数据以供前端页面进行数据渲染,评论管理页面的列表是获取全部数据,并不会根据 blog_id 进行过滤。
评论审核接口
审核接口负责接收前端的审核请求,处理前端传输过来的数据后,将这些评论的状态值改为已审核,我们将接受的参数设置为一个数组,可以同时审核多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/checkDone ,请求方法为 POST,代码如下:
@PostMapping("/comments/checkDone") @ResponseBody public Result checkDone(@RequestBody Integer[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.checkDone(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("审核失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean checkDone(Integer[] ids) { return blogCommentMapper.checkDone(ids) > 0; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<update id="checkDone"> update tb_blog_comment set comment_status=1 where comment_id in <foreach item="id" collection="array" open="(" separator="," close=")"> #{id} </foreach> and comment_status = 0 </update>
接口的请求路径为 /comments/checkDone,并使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 checkDone() 批量审核方法修改这些记录的审核状态字段。
评论回复接口
评论回复接口可以在评论审核通过之后调用,目的就是给需要回复的评论添加回复字段内容,代码如下
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/checkDone ,请求方法为 POST,代码如下:
@PostMapping("/comments/reply") @ResponseBody public Result checkDone(@RequestParam("commentId") Long commentId, @RequestParam("replyBody") String replyBody) { if (commentId == null || commentId < 1 || StringUtils.isEmpty(replyBody)) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.reply(commentId, replyBody)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("回复失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean reply(Long commentId, String replyBody) { BlogComment blogComment = blogCommentMapper.selectByPrimaryKey(commentId); //blogComment不为空且状态为已审核,则继续后续操作 if (blogComment != null && blogComment.getCommentStatus().intValue() == 1) { blogComment.setReplyBody(replyBody); blogComment.setReplyCreateTime(new Date()); return blogCommentMapper.updateByPrimaryKeySelective(blogComment) > 0; } return false; }
接口的请求路径为 /comments/reply,入参设置为选中的评论记录 id 以及需要回复的内容字段,参数验证通过后则调用 reply() 对表中的回复相关字段做修改,这里是做成了单条记录的回复,如果有需要的话可以适当的修改代码进行功能扩展。
删除评论接口
删除接口负责接收前端的评论删除请求,处理前端传输过来的数据后,将这些记录从数据库中删除,这里的“删除”功能并不是真正意义上的删除,而是逻辑删除,我们将接受的参数设置为一个数组,可以同时删除多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/delete,请求方法为 POST,代码如下:
@PostMapping("/comments/delete") @ResponseBody public Result delete(@RequestBody Integer[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.deleteBatch(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("刪除失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean deleteBatch(Integer[] ids) { return blogCommentMapper.deleteBatch(ids) > 0; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<update id="deleteBatch"> update tb_blog_comment set is_deleted=1 where comment_id in <foreach item="id" collection="array" open="(" separator="," close=")"> #{id} </foreach> </update>
使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 deleteBatch() 批量删除方法进行数据库操作,否则将向前端返回错误信息。
前端页面实现
功能按钮
评论管理模块我们也设计了常用的几个功能:评论审核、评论回复、评论删除,因此在页面中添加对应的功能按钮以及触发事件,代码如下:(注:完整代码位于 resources/templates/admin/comment.html)
<div class="grid-btn"> <button class="btn btn-success" onclick="checkDoneComments()"> <i class="fa fa-check"></i> 批量审核 </button> <button class="btn btn-info" onclick="reply()"> <i class="fa fa-reply"></i> 回复 </button> <button class="btn btn-danger" onclick="deleteComments()"> <i class="fa fa-trash-o"></i> 批量删除 </button> </div>
分别是批量审核按钮对应的触发事件是 checkDoneComments() 方法,回复按钮对应的触发事件是 reply() 方法,删除按钮对应的触发事件是 deleteComments() 方法,这些方法我们将在后续的代码实现中给出。
分页信息展示区域
页面中已经引入 JqGrid 的相关静态资源文件,需要在页面中展示分页数据的区域增加如下代码:
<table id="jqGrid" class="table table-bordered"></table> <div id="jqGridPager"></div>
页面效果
那么最终的静态页面效果展示如下图所示:
包括功能按钮、数据列表展示区域,此时只是静态效果展示,并没有与后端进行数据交互,接下来我们将结合 Ajax 和后端接口实现具体的功能。
评论管理模块前端功能实现
完成了页面展示的实现,接着完成相关的功能实现。请大家一定自行对照源码完成代码编写。
分页功能
在 resources/static/admin/dist/js 目录下新增 comment.js 文件,并添加如下代码:
$(function () { $('#jqGrid').jqGrid({ url: '/admin/comments/list', datatype: 'json', colModel: [ { label: 'id', name: 'commentId', index: 'commentId', width: 50, key: true, hidden: true, }, { label: '评论内容', name: 'commentBody', index: 'commentBody', width: 120, }, { label: '评论时间', name: 'commentCreateTime', index: 'commentCreateTime', width: 60, }, { label: '评论人名称', name: 'commentator', index: 'commentator', width: 60, }, { label: '评论人邮箱', name: 'email', index: 'email', width: 90 }, { label: '状态', name: 'commentStatus', index: 'commentStatus', width: 60, formatter: statusFormatter, }, { label: '回复内容', name: 'replyBody', index: 'replyBody', width: 120 }, ], height: 700, rowNum: 10, rowList: [10, 20, 50], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: '#jqGridPager', jsonReader: { root: 'data.list', page: 'data.currPage', total: 'data.totalPage', records: 'data.totalCount', }, prmNames: { page: 'page', rows: 'limit', order: 'order', }, gridComplete: function () { //隐藏grid底部滚动条 $('#jqGrid').closest('.ui-jqgrid-bdiv').css({ 'overflow-x': 'hidden' }); }, }); $(window).resize(function () { $('#jqGrid').setGridWidth($('.card-body').width()); }); function statusFormatter(cellvalue) { if (cellvalue == 0) { return '<button type="button" class="btn btn-block btn-secondary btn-sm" style="width: 80%;">待审核</button>'; } else if (cellvalue == 1) { return '<button type="button" class="btn btn-block btn-success btn-sm" style="width: 80%;">已审核</button>'; } } });
以上代码的主要功能为分页数据展示、字段格式化 jqGrid DOM 宽度的自适应,在页面加载时,调用 JqGrid 的初始化方法,将页面中 id 为 jqGrid 的 DOM 渲染为分页表格,并向后端发送请求,请求路径为 /admin/comments/list,该路径即友链分页列表接口,之后按照后端返回的 json 数据填充表格以及表格下方的分页按钮,可以参考第 15 个实验课程中 jqgrid 分页功能整合进行知识的理解,之后可以重启项目验证一下评论信息分页功能是否正常。
批量审核功能
游客在博客详情页提交的评论初始状态为未审核,因为这些游客输入的内容有些可能是无意义的字符串或者一些不适合显示的内容,这种就不需要显示在博客页面了,因此做了审核这个功能。
批量审核按钮的点击触发事件为 checkDoneComments(),在 comment.js 文件中新增如下代码:
/** * 批量审核 */ function checkDoneComments() { var ids = getSelectedRows(); if (ids == null) { return; } swal({ title: '确认弹框', text: '确认审核通过吗?', icon: 'warning', buttons: true, dangerMode: true, }).then((flag) => { if (flag) { $.ajax({ type: 'POST', url: '/admin/comments/checkDone', contentType: 'application/json', data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { swal('审核成功', { icon: 'success', }); $('#jqGrid').trigger('reloadGrid'); } else { swal(r.message, { icon: 'error', }); } }, }); } }); }
获取用户在 jqgrid 表格中选择的需要审核的所有记录的 id,之后将参数封装并向后端发送 Ajax 请求,请求地址为 comments/checkDone。
回复功能实现
回复按钮绑定了触发事件,我们需要在 comment.js 文件中新增 reply() 方法,方法中的实现为打开信息编辑框,下面我们来实现信息编辑框触发事件,代码如下:
“`html
Spring Boot 博客系统项目开发之评论模块功能实现
本项目的开源仓库地址为:https://github.com/ZHENFENG13/My-Blog
除 My-Blog 项目外,我也在维护另外一个开源项目,仓库地址为:https://github.com/newbee-ltd/newbee-mall
感兴趣的朋友可以去关注一下。
文章总览
在文章详情页面制作的那节实验课程中,我们提到了文章详情页的页面中还会有文章的评论列表区域和文章评论的输入区域,但是在那个实验中并没有直接讲解和介绍,主要是考虑到篇幅的缘故,评论模块不仅仅是详情页的评论添加和列表,还有后台管理系统中的评论管理功能模块,因此就选择单独用一个章节来讲解博客系统的评论模块,包括详情页的功能开发和后台管理系统中的评论管理页面和相关接口的开发。
知识点
- 评论模块介绍
- 评论模块表结构设计及接口实现
- 评论模块页面设计及编码
- 评论功能测试
环境
- JDK 1.8 或者更高版本
- Spring Boot 2.1.0-RELEASE
- Maven 3+
评论模块简介
相信大家对于评论模块并不陌生,不管是电商网站还是视频及音乐网站,评论模块都是不可或缺的,我们通过评论可以看到大家对于一个当前这个事物的评价和建议,通过这些评论可以大致的了解该事物的一些属性,甚至你会觉得评论数据也和详情模块一样,帮助我们更加清晰的了解到眼前的这个事物,但是二者的区别还是很大的,详情模块是运营人员设置的,而评论数据则是用户留下的数据,二者的来源就有很大的不同。回到我们的博客系统中来,论坛、博客、公众号等也都会有的模块,用户在文章下通过文字或者图片来留下自己的评论信息,通过这些文案来表达自己的问题和建议,甚至对文章中提到的内容进行拓展和完善,因此评论模块的开发也是必须的。
在简单的介绍了评论模块后,我们来看一下本系统中的评论页面是怎样的,评论的相关功能模块主要出现在两个地方:
-
其一是博客详情页中会展示评论信息,主要包括评论的列表展示和评论提交;
评论列表:
评论提交:
-
其二是后台管理系统中会有评论数据的管理页面,主要包括评论列表、评论审核和评论回复的功能。
表结构设计及 Mapper 文件自动生成
在进行接口设计和具体的功能实现前,首先将表结构确定下来,主要通过观察前文中几张评论模块的图片来确认一些字段的设置,首先是详情页中的评论展示功能,可以看出评论信息是在某一篇博客下评论,因此需要关联博客主键 id, 同时该系统并没有设置用户体系,而是谁都可以评论,用户评论时需要填写一些信息和验证,评论提交就是普通字符串信息提交,包括一些基础的字段填写,之后是评论信息管理页面,有审核功能,所以评论数据应该有状态字段,有回复功能但是并没有开发多级回复,所以还有一个回复内容字段。
总结下来,评论表中重要的字段如下:
- 关联的文章主键 blogId
- 评论者名称
- 评论人的邮箱
- 评论内容
- 回复内容
- 审核状态
基于以上分析设计的评论表的 SQL 语句如下,直接执行如下 SQL 语句即可:
USE `my_blog_db`; DROP TABLE IF EXISTS `tb_blog_comment`; CREATE TABLE `tb_blog_comment` ( `comment_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id', `blog_id` bigint(20) NOT NULL DEFAULT 0 COMMENT '关联的blog主键', `commentator` varchar(50) NOT NULL DEFAULT '' COMMENT '评论者名称', `email` varchar(100) NOT NULL DEFAULT '' COMMENT '评论人的邮箱', `website_url` varchar(50) NOT NULL DEFAULT '' COMMENT '网址', `comment_body` varchar(200) NOT NULL DEFAULT '' COMMENT '评论内容', `comment_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '评论提交时间', `commentator_ip` varchar(20) NOT NULL DEFAULT '' COMMENT '评论时的ip地址', `reply_body` varchar(200) NOT NULL DEFAULT '' COMMENT '回复内容', `reply_create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '回复时间', `comment_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否审核通过 0-未审核 1-审核通过', `is_deleted` tinyint(4) DEFAULT '0' COMMENT '是否删除 0-未删除 1-已删除', PRIMARY KEY (`comment_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
加了一些时间字段和一些固定的字段,把表结构导入到数据库中即可,接下来使用 MyBatis-Generator 插件将该表对应的 Mapper 文件及对应的实体类和 Dao 层接口生成出来并针对 is_deleted 字段对部分 SQL 进行修改即可,可参考前文中的知识点进行修改。
评论管理页面制作
导航栏
首先在左侧导航栏中新增评论管理页的导航按钮,在 sidebar.html 文件中新增如下代码:(注:完整代码位于 resources/templates/admin/sidebar.html)
<li class="nav-item"> <a th:href="@{/admin/comments}" th:class="${path}=='comments'?'nav-link active':'nav-link'" > <i class="fa fa-comments nav-icon" aria-hidden="true"></i> <p> 评论管理 </p> </a> </li>
点击后的跳转路径为 /admin/comments,之后我们新建 Controller 来处理该路径并跳转到对应的页面。
Controller 处理跳转
首先在 controller/admin 包下新建 CommentController.java,之后新增如下代码:
package com.site.blog.my.core.controller.admin; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; @Controller @RequestMapping("/admin") public class CommentController { @GetMapping("/comments") public String list(HttpServletRequest request) { request.setAttribute("path", "comments"); return "admin/comment"; } }
该方法用于处理 /admin/comments 请求,并设置 path 字段,之后跳转到 admin 目录下的 comment.html 中。
comment.html 页面制作
接下来就是博客编辑页面的模板文件制作了,在 resources/templates/admin 目录下新建 comment.html,并引入对应的 js 文件和 css 样式文件,代码如下:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <header th:replace="admin/header::header-fragment"></header> <body class="hold-transition sidebar-mini"> <div class="wrapper"> <!-- 引入页面头header-fragment --> <div th:replace="admin/header::header-nav"></div> <!-- 引入工具栏sidebar-fragment --> <div th:replace="admin/sidebar::sidebar-fragment(${path})"></div> <!-- Content Wrapper. Contains page content --> <div class="content-wrapper"> <!-- Content Header (Page header) --> <div class="content-header"> <div class="container-fluid"></div> <!-- /.container-fluid --> </div> <!-- Main content --> <div class="content"> <div class="container-fluid"> <div class="card card-primary card-outline"> <div class="card-header"> <h3 class="card-title">评论管理</h3> </div> <!-- /.card-body --> </div> </div> </div> </div> <div th:replace="admin/footer::footer-fragment"></div> </div> <!-- jQuery --> <script th:src="@{/admin/plugins/jquery/jquery.min.js}"></script> <!-- jQuery UI 1.11.4 --> <script th:src="@{/admin/plugins/jQueryUI/jquery-ui.min.js}"></script> <!-- Bootstrap 4 --> <script th:src="@{/admin/plugins/bootstrap/js/bootstrap.bundle.min.js}" ></script> <!-- AdminLTE App --> <script th:src="@{/admin/dist/js/adminlte.min.js}"></script> </body> </html>
至此,跳转逻辑处理完毕,演示效果如下:
评论模块接口设计及实现
接口介绍
关于接口的设计以及前后端交互数据的设计大家可以参考一下第 12 课中的内容,友链模块在后台管理系统中有 4 个接口,分别是:
- 评论列表分页接口
- 评论审核接口
- 评论回复接口
- 删除评论接口
接下来讲解每个接口具体的实现代码,首先在 controller/admin 包下新建 CommentController.java,并在 service 包下新建业务层代码 CommentService.java 及实现类,之后参照接口分别进行功能实现。
评论列表分页接口
列表接口负责接收前端传来的分页参数,如 page 、limit 等参数,之后将数据总数和对应页面的数据列表查询出来并封装为分页数据返回给前端。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/list,请求方法为 GET,代码如下:
/** * 评论列表 */ @GetMapping("/comments/list") @ResponseBody public Result list(@RequestParam Map<String, Object> params) { if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) { return ResultGenerator.genFailResult("参数异常!"); } PageQueryUtil pageUtil = new PageQueryUtil(params); return ResultGenerator.genSuccessResult(commentService.getCommentsPage(pageUtil)); }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public PageResult getCommentsPage(PageQueryUtil pageUtil) { List<BlogComment> comments = blogCommentMapper.findBlogCommentList(pageUtil); int total = blogCommentMapper.getTotalBlogComments(pageUtil); PageResult pageResult = new PageResult(comments, total, pageUtil.getLimit(), pageUtil.getPage()); return pageResult; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<select id="findBlogCommentList" parameterType="Map" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from tb_blog_comment where is_deleted=0 order by comment_id desc <if test="start!=null and limit!=null"> limit #{start},#{limit} </if> </select> <select id="getTotalBlogComments" parameterType="Map" resultType="int"> select count(*) from tb_blog_comment where is_deleted=0 </select>
SQL 语句在 BlogCommentMapper.xml 文件中,一般的分页也就是使用 limit 关键字实现,获取响应条数的记录和总数之后再进行数据封装,这个接口就是根据前端传的分页参数进行查询并返回分页数据以供前端页面进行数据渲染,评论管理页面的列表是获取全部数据,并不会根据 blog_id 进行过滤。
评论审核接口
审核接口负责接收前端的审核请求,处理前端传输过来的数据后,将这些评论的状态值改为已审核,我们将接受的参数设置为一个数组,可以同时审核多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/checkDone ,请求方法为 POST,代码如下:
@PostMapping("/comments/checkDone") @ResponseBody public Result checkDone(@RequestBody Integer[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.checkDone(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("审核失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean checkDone(Integer[] ids) { return blogCommentMapper.checkDone(ids) > 0; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<update id="checkDone"> update tb_blog_comment set comment_status=1 where comment_id in <foreach item="id" collection="array" open="(" separator="," close=")"> #{id} </foreach> and comment_status = 0 </update>
接口的请求路径为 /comments/checkDone,并使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 checkDone() 批量审核方法修改这些记录的审核状态字段。
评论回复接口
评论回复接口可以在评论审核通过之后调用,目的就是给需要回复的评论添加回复字段内容,代码如下
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/checkDone ,请求方法为 POST,代码如下:
@PostMapping("/comments/reply") @ResponseBody public Result checkDone(@RequestParam("commentId") Long commentId, @RequestParam("replyBody") String replyBody) { if (commentId == null || commentId < 1 || StringUtils.isEmpty(replyBody)) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.reply(commentId, replyBody)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("回复失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean reply(Long commentId, String replyBody) { BlogComment blogComment = blogCommentMapper.selectByPrimaryKey(commentId); //blogComment不为空且状态为已审核,则继续后续操作 if (blogComment != null && blogComment.getCommentStatus().intValue() == 1) { blogComment.setReplyBody(replyBody); blogComment.setReplyCreateTime(new Date()); return blogCommentMapper.updateByPrimaryKeySelective(blogComment) > 0; } return false; }
接口的请求路径为 /comments/reply,入参设置为选中的评论记录 id 以及需要回复的内容字段,参数验证通过后则调用 reply() 对表中的回复相关字段做修改,这里是做成了单条记录的回复,如果有需要的话可以适当的修改代码进行功能扩展。
删除评论接口
删除接口负责接收前端的评论删除请求,处理前端传输过来的数据后,将这些记录从数据库中删除,这里的“删除”功能并不是真正意义上的删除,而是逻辑删除,我们将接受的参数设置为一个数组,可以同时删除多条记录,只需要在前端将用户选择的记录 id 封装好再传参到后端即可。
- 控制层代码(注:完整代码位于 com.site.blog.my.core.controller.admin.CommentController.java):
接口的映射地址为 /comments/delete,请求方法为 POST,代码如下:
@PostMapping("/comments/delete") @ResponseBody public Result delete(@RequestBody Integer[] ids) { if (ids.length < 1) { return ResultGenerator.genFailResult("参数异常!"); } if (commentService.deleteBatch(ids)) { return ResultGenerator.genSuccessResult(); } else { return ResultGenerator.genFailResult("刪除失败"); } }
- 业务层代码(注:完整代码位于 com.site.blog.my.core.service.impl.CommentServiceImpl.java):
public Boolean deleteBatch(Integer[] ids) { return blogCommentMapper.deleteBatch(ids) > 0; }
- BlogCommentMapper.xml (注:完整代码位于 resources/mapper/BlogCommentMapper.xml):
<update id="deleteBatch"> update tb_blog_comment set is_deleted=1 where comment_id in <foreach item="id" collection="array" open="(" separator="," close=")"> #{id} </foreach> </update>
使用 @RequestBody 将前端传过来的参数封装为 id 数组,参数验证通过后则调用 deleteBatch() 批量删除方法进行数据库操作,否则将向前端返回错误信息。
前端页面实现
功能按钮
评论管理模块我们也设计了常用的几个功能:评论审核、评论回复、评论删除,因此在页面中添加对应的功能按钮以及触发事件,代码如下:(注:完整代码位于 resources/templates/admin/comment.html)
<div class="grid-btn"> <button class="btn btn-success" onclick="checkDoneComments()"> <i class="fa fa-check"></i> 批量审核 </button> <button class="btn btn-info" onclick="reply()"> <i class="fa fa-reply"></i> 回复 </button> <button class="btn btn-danger" onclick="deleteComments()"> <i class="fa fa-trash-o"></i> 批量删除 </button> </div>
分别是批量审核按钮对应的触发事件是 checkDoneComments() 方法,回复按钮对应的触发事件是 reply() 方法,删除按钮对应的触发事件是 deleteComments() 方法,这些方法我们将在后续的代码实现中给出。
分页信息展示区域
页面中已经引入 JqGrid 的相关静态资源文件,需要在页面中展示分页数据的区域增加如下代码:
<table id="jqGrid" class="table table-bordered"></table> <div id="jqGridPager"></div>
页面效果
那么最终的静态页面效果展示如下图所示:
包括功能按钮、数据列表展示区域,此时只是静态效果展示,并没有与后端进行数据交互,接下来我们将结合 Ajax 和后端接口实现具体的功能。
评论管理模块前端功能实现
完成了页面展示的实现,接着完成相关的功能实现。请大家一定自行对照源码完成代码编写。
分页功能
在 resources/static/admin/dist/js 目录下新增 comment.js 文件,并添加如下代码:
$(function () { $('#jqGrid').jqGrid({ url: '/admin/comments/list', datatype: 'json', colModel: [ { label: 'id', name: 'commentId', index: 'commentId', width: 50, key: true, hidden: true, }, { label: '评论内容', name: 'commentBody', index: 'commentBody', width: 120, }, { label: '评论时间', name: 'commentCreateTime', index: 'commentCreateTime', width: 60, }, { label: '评论人名称', name: 'commentator', index: 'commentator', width: 60, }, { label: '评论人邮箱', name: 'email', index: 'email', width: 90 }, { label: '状态', name: 'commentStatus', index: 'commentStatus', width: 60, formatter: statusFormatter, }, { label: '回复内容', name: 'replyBody', index: 'replyBody', width: 120 }, ], height: 700, rowNum: 10, rowList: [10, 20, 50], styleUI: 'Bootstrap', loadtext: '信息读取中...', rownumbers: false, rownumWidth: 20, autowidth: true, multiselect: true, pager: '#jqGridPager', jsonReader: { root: 'data.list', page: 'data.currPage', total: 'data.totalPage', records: 'data.totalCount', }, prmNames: { page: 'page', rows: 'limit', order: 'order', }, gridComplete: function () { //隐藏grid底部滚动条 $('#jqGrid').closest('.ui-jqgrid-bdiv').css({ 'overflow-x': 'hidden' }); }, }); $(window).resize(function () { $('#jqGrid').setGridWidth($('.card-body').width()); }); function statusFormatter(cellvalue) { if (cellvalue == 0) { return '<button type="button" class="btn btn-block btn-secondary btn-sm" style="width: 80%;">待审核</button>'; } else if (cellvalue == 1) { return '<button type="button" class="btn btn-block btn-success btn-sm" style="width: 80%;">已审核</button>'; } } });
以上代码的主要功能为分页数据展示、字段格式化 jqGrid DOM 宽度的自适应,在页面加载时,调用 JqGrid 的初始化方法,将页面中 id 为 jqGrid 的 DOM 渲染为分页表格,并向后端发送请求,请求路径为 /admin/comments/list,该路径即友链分页列表接口,之后按照后端返回的 json 数据填充表格以及表格下方的分页按钮,可以参考第 15 个实验课程中 jqgrid 分页功能整合进行知识的理解,之后可以重启项目验证一下评论信息分页功能是否正常。
批量审核功能
游客在博客详情页提交的评论初始状态为未审核,因为这些游客输入的内容有些可能是无意义的字符串或者一些不适合显示的内容,这种就不需要显示在博客页面了,因此做了审核这个功能。
批量审核按钮的点击触发事件为 checkDoneComments(),在 comment.js 文件中新增如下代码:
/** * 批量审核 */ function checkDoneComments() { var ids = getSelectedRows(); if (ids == null) { return; } swal({ title: '确认弹框', text: '确认审核通过吗?', icon: 'warning', buttons: true, dangerMode: true, }).then((flag) => { if (flag) { $.ajax({ type: 'POST', url: '/admin/comments/checkDone', contentType: 'application/json', data: JSON.stringify(ids), success: function (r) { if (r.resultCode == 200) { swal('审核成功', { icon: 'success', }); $('#jqGrid').trigger('reloadGrid'); } else { swal(r.message, { icon: 'error', }); } }, }); } }); }
获取用户在 jqgrid 表格中选择的需要审核的所有记录的 id,之后将参数封装并向后端发送 Ajax 请求,请求地址为 comments/checkDone。
回复功能实现
回复按钮绑定了触发事件,我们需要在 comment.js 文件中新增 reply() 方法,方法中的实现为打开信息编辑框,下面我们来实现信息编辑框触发事件,代码如下:
“`html
部分转自互联网,侵权删除联系
最新评论