从0开始码第一个Spring Boot项目(javaweb个人博客系统)之完成文章一级/二级评论功能

1. 效果预览

1.1进入首页

进入首页后点击一篇文章进入:
点击文章进入

1.2 测试评论功能

进入文章详细列表后,上方为文章内容区域,下方为文章评论区域
文章评论区域
对此评论框做了数据校验,如果评论内容为空,给出提示并返回,否则评论成功,我们输入一条评论信息,然后点击回复,如下:
评论成功
测试二级评论功能:
显示二级评论
我们点击一级评论下的评论图标,如果有二级评论则显示二级评论和二级评论框,如果没有只显示二级评论框
二级评论框
测试二级评论:
二级评论成功
评论数增加

2. 功能实现

2.1 文章详情页布局(articleDetail.html)
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- 踩过的坑,springboot引入静态资源路径不要加/static/,否则会报404-->
    <title th:text="${ArticleDetail.title}"></title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" href="/bootstrap-3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="/css/community.css">
    <link rel="stylesheet" href="//at.alicdn.com/t/font_1643567_yxz5icboc4.css">
    <link rel="stylesheet" href="/bootstrap-3.3.7/css/bootstrap-theme.min.css">
    <script src="/jquery-1.12.4/jquery-1.12.4.min.js"></script>
    <script src="/bootstrap-3.3.7/js/bootstrap.min.js"></script>
    <script src="/js/community.js"></script>

</head>
<body>

<div th:replace="navigation :: nav"></div>

<div class="row main">
    <div class="col-lg-9 col-md-12 col-sm-12 col-xs-12 col-left">
        <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
            <div class=""> <h4 th:text="${ArticleDetail.title}"></h4></div>
            <hr>
            <div class="detail-msg">
            作者: <span th:text="${ArticleDetail.user.name}"></span> | 发布时间:<span th:text="${#dates.format(ArticleDetail.createTime,'yyyy-MM-dd')}"></span> | 阅读数: <span th:text="${ArticleDetail.readCount}"></span>
                <div class="link-items" th:if="${session.user !=null && ArticleDetail.user.id==session.user.id}" >
                    <a th:href="@{/article/edit(id=${ArticleDetail.getId()})}"><i class="iconfont icon-fabu2"></i>&nbsp;编辑</a>
                </div>
            </div>
            <div>
                <span th:text="${ArticleDetail.description}"></span>
            </div>
        </div>
        <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
            <div class="comment">
                <h4><span th:text="${ArticleDetail.getAnswerCount()}"></span> 个回复</h4>
            </div>

            <div class="media media_list" th:each="comment :${comments}">
                <div class="comment-head">
                    <div class="media-left">
                        <a href="#">
                            <img class="img-rounded img-comment" th:src="${comment.getUser().getAvatarUrl()}"  >
                        </a>
                    </div>
                    <div class="media-body">
                        <span class="media-heading" th:text="${comment.getUser().getName()}"></span>
                        <span class="float-right" th:text="${#dates.format(comment.createTime,'yyyy-MM-dd')}"></span>
                    </div>

                </div>
                <div class="comment-style comment-body" th:text="${comment.getContent()}">

                </div>
                <div class="comment-style comment-footer">
                    <div>
                        <span class="operate like">
                        <i class="iconfont icon-z-like" th:like-id="${comment.getId()}" onclick="like(this)"></i>
                        <span id="likeCount" th:text="${comment.getLikeCount()==0?'':comment.getLikeCount()}"></span>
                        </span>
                        <span class="operate answer" th:data-id="${comment.getId()}" onclick="showChildComment(this)">
                        <i class="iconfont icon-pinglun"></i>
                        <span th:id="${'commentCount-'+comment.getId()}"  th:text="${comment.commentCount==0?'':comment.commentCount}"></span>
                        </span>
                    </div>

                    <div class="collapse childCol col-lg-12 col-md-12 col-sm-12 col-xs-12" th:id="${'comment-'+comment.getId()}">
                        <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 comment-comment"   th:if="${session.user!=null}">
                            <input type="text" class="form-control" th:id="${'childComment-'+comment.getId()}" name="childComment" placeholder="说说你的看法...">
                            <button type="button" class="btn btn-success btn-comment" th:data-parentId="${comment.getId()}" onclick="subComment(this)">评论</button>
                        </div>
                    </div>
                </div>
            </div>


        </div>
        <!-- 评论 -->
        <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 ">

            <div class="comment" th:if="${session.user!=null}">
                <input type="hidden" id="parentId" th:value="${ArticleDetail.getId()}">
                <div class="d-flex">
                    <div class="media">
                        <div class="media-comment">
                            <a href="#">
                                <img class="img-rounded img-comment" th:src="${ArticleDetail.user.getAvatarUrl()}"  >
                            </a>
                        </div>
                    </div>
                    <textarea id="commentArea" name="description"  class="comment-content open" maxlength="1000"></textarea>
                </div>
                <button type="button" class="btn btn-success btn-comment" onclick="postComment();">回复</button>
            </div>
            <div class="notLogin" th:if="${session.user==null}">
                要回复问题请先<a onclick="toLogin()">登录</a>
            </div>
        </div>
    </div>
    <div class="col-lg-3 col-md-12 col-sm-12 col-xs-12">
        <div>
            <p>发起人</p>
            <div class="media">
                <div class="media-left">
                    <a href="#">
                        <img class="media-object img-rounded" th:src="${ArticleDetail.user.getAvatarUrl()}"  >
                    </a>
                </div>
                <div class="media-body">
                    <span class="media-heading" th:text="${ArticleDetail.user.name}"></span>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>
2.2 一级/二级评论回复功能

点击回复按钮事件

function postComment() {
    var parentId = $("#parentId").val();
    var content = $("#commentArea").val();
    var data = {"parentId":parentId,"content":content,"type":1};
    if(content==undefined||content==""){
        alert("评论内容不能为空");
        return;
    }
    $.ajax({
        type:"POST",
        url:"/comment",
        data:JSON.stringify(data),
        dataType:"json",
        contentType:"application/json",
        success:function (result) {
            if(result.code==200){
                //$("#commentArea").val("");
                window.location.reload();
                alert(result.message);
            }else {
                alert(result.message)
            }
        }
    });

}

通过ajax请求到后端“/comment”路径(CommentController.java)

//控制层处理前端提交过来的回复评论请求
@PostMapping("/comment")
    @ResponseBody
    public RespDTO doComment(@RequestBody Comment comment,
                             HttpServletRequest request){
        User user = (User) request.getSession().getAttribute("user");
        if(user==null){
            return RespDTO.error(CustomizeErrorCode.NOT_LOGIN.getMessage());
        }
        comment.setCreateTime(System.currentTimeMillis());
        comment.setModifiedTime(System.currentTimeMillis());
        comment.setCommentor(user.getId());
        comment.setLikeCount(0);
        comment.setCommentCount(0);
        //做提交操作
        commentService.insert(comment);
        return RespDTO.ok(CustomizeErrorCode.COMMENT_SUCCESS.getMessage(),user);
    }

Service层进行提交逻辑处理(CommentService.java)

/**
通用的评论回复处理,可以回复一级评论和二级评论
*/
//使用事务
@Transactional
public void insert(Comment comment){
   Long parentId = null;
   Article article1 = articleMapper.selectByPrimaryKey(comment.getParentId());
   if(comment.getType()==null || !CommentType.isExist(comment.getType())){
       throw new CustomizeException(CustomizeErrorCode.COMMENT_TYPE_WRONG);
   }

   //类型为ARTICLE,对文章进行评论
   if(comment.getType()==CommentType.ARTICLE.getType()){
       if(comment.getParentId()==null || articleMapper.selectByPrimaryKey(comment.getParentId())==null){
           throw new CustomizeException(CustomizeErrorCode.COMMENT_ARTICLE_ID_NOT_FOUND);
       }
       parentId = comment.getParentId();
   }else {
   //否则处理二级评论
       Comment parentComment = commentMapper.selectByPrimaryKey(comment.getParentId());
       if(parentComment!=null){
           parentId = parentComment.getParentId();
       }else {
           throw new CustomizeException(CustomizeErrorCode.COMMENT_ARTICLE_ID_NOT_FOUND);
       }
       Comment updateComment = new Comment();
       updateComment.setCommentCount(1);
       updateComment.setId(comment.getParentId());
       commentExtMapper.incSubCommentCount(updateComment);
       if(comment.getParentId()==null || commentMapper.selectByPrimaryKey(comment.getParentId())==null){
           throw new CustomizeException(CustomizeErrorCode.COMMENT_NOT_FOUND);
       }
   }
   //添加评论内容
   commentMapper.insert(comment);
   Article article = new Article();
   //文章总评论数递增
   article.setId(parentId);
   article.setAnswerCount(1);
   articleExtMapper.incComment(article);
}
2.3 二级评论显示功能

点击查看二级评论,通过ajax向后台请求数据:

function showChildComment(ele) {
    var parentId =$(ele).attr("data-id");
    $(ele).toggleClass("operateBgColor");
    var childComment = $("#comment-"+parentId);
    //控制二级评论div显示还是隐藏
    childComment.toggleClass("in ");
    var data = {childId:parentId};
    //如果二级评论框显示,开始请求数据
    if(childComment.hasClass("in")){
        $.ajax({
            type:"get",
            url:"/childComment",
            data:data,
            dataType:"json",
            contentType:"application/json",
            success:function (result) {
                var obj = result.obj;
                $.each(obj,function (i, o) {
                    console.log(o.createTime);
                    var time = new Date(o.createTime).format("yyyy-MM-dd");
                    pushCommentHtml(parentId,o.user.avatarUrl,o.user.name,o.content,time);
                });
            }
        });

    }else{
    	//防止评论框关闭,再次点击内容重复叠加,每次都清空二级评论
        $("#comment-"+parentId +" .comment-detail").remove()

    }

}

二级评论内容拼接到html:

function pushCommentHtml(id,imgurl,uname,content,time) {
    var html = "<div class='comment-detail col-lg-12 col-md-12 col-sm-12 col-xs-12'>" +
        "           <div class='comment-head'>" +
        "               <div class='media-left'>" +
        "                   <a href='#'>" +
        "                       <img class='img-rounded img-comment' src='" + imgurl + "'>" +
        "                   </a>" +
        "               </div>" +
        "               <div class='media-body'>" +
        "                   <span class='media-heading'>" + uname + "</span>" +
        "                   <span class='float-right'>" + time + "</span>"+
        "               </div>" +
        "           </div>" +
        "        <div class='comment-style comment-body'>" + content + "</div>" +
        "   </div>"

    var ele = $("#comment-" + id);
    ele.prepend(html);


}

前端请求“/childComment”路径开始获取二级评论内容:

 @GetMapping("/childComment")
    @ResponseBody
    public RespDTO getChildComments(@Param("childId") Long childId, HttpServletRequest request){
        User user = (User) request.getSession().getAttribute("user");
        if(user==null){
            return RespDTO.error(CustomizeErrorCode.NOT_LOGIN.getMessage());
        }
        if(StringUtils.isEmpty(childId)){
            return RespDTO.error(CustomizeErrorCode.COMMENT_NOT_FOUND.getMessage());
        }
        List<CommentDTO> commentDTOS = commentService.selectCommentsById(childId, CommentType.COMMENT.getType());
        return RespDTO.ok("获取二级评论成功",commentDTOS);
    }

Service层处理获取评论功能(包括一级评论和二级评论),一级评论是在进入详情页就开始获取,二级评论则是用户操作获取:

public List<CommentDTO> selectCommentsById(long id,int type) {

        List<CommentDTO> commentDTOS  = new ArrayList<>();
        /** 查询出当前文章的所有一级评论 **/
        CommentExample commentExample = new CommentExample();
        commentExample.createCriteria().andParentIdEqualTo(id).andTypeEqualTo(type);
        if(type==CommentType.ARTICLE.getType()){

            commentExample.setOrderByClause("create_time desc");
        }else if(type == CommentType.COMMENT.getType()){
            commentExample.setOrderByClause("create_time asc");
        }
        List<Comment> comments = commentMapper.selectByExample(commentExample);
        if(comments.size()<=0){
            return new ArrayList<>();
        }
        /** 使用lambda表达式获取用户id  **/
        Set<Long> ids = comments.stream().map(c -> c.getCommentor()).collect(Collectors.toSet());
        UserExample user = new UserExample();
        user.createCriteria().andIdIn(new ArrayList<>(ids));
        List<User> users = userMapper.selectByExample(user);
        //将获取到的用户List转为map
        Map<Long, User> userMap = users.stream().collect(Collectors.toMap(User::getId, u -> u, (k1, k2) -> k1));
        //遍历并拷贝赋值
        comments.stream().forEach(comment -> {
            CommentDTO commentDTO = new CommentDTO();
            BeanUtils.copyProperties(comment,commentDTO);
            commentDTO.setUser(userMap.get(comment.getCommentor()));
            commentDTOS.add(commentDTO);
        });

        return commentDTOS;
    }
  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值