從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;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章