How to implement blog comment and reply function

Implementation ideas:

Database design: The comment table needs to define the current blog id for association. Because the comment needs to have a reply function, it is necessary to define whether the current comment has an upper-level comment, and the upper-level comment id needs to be defined; the blogger needs to have a label to reply to the comment, Therefore, it is necessary to define the Boolean type to determine whether it is a blogger.

In terms of code: Click the comment to get the current blog id and insert your own comment data. Click the reply button to get the id of the previous comment and the user name as a reply. After the reply is successful, refresh the page, and the background will find all parentCommentId in the database. It is -1 to traverse, because the superior id is -1, which proves that the current comment has no parent node. By traversing the id of the parent node, all the child nodes corresponding to the comments are queried.

page display

Blog entity class

    private Long id;
    private String nickname;
    private String email;
    private String content;

    //头像
    private String avatar;
    private Date createTime;

    private Long blogId;  //关联博客id
    private Long parentCommentId;  //上级评论id
    private String parentNickname;

    //回复评论
    private List<Comment> replyComments = new ArrayList<>();
    private Comment parentComment;
    private boolean adminComment;  //是不是博主

    private DetailedBlog blog;

Blog form html for comments

<div id="comment-form" class="ui form">
		<!--获取当前博客id进行评论  使用隐藏域-->
        <input type="hidden" name="blogId" th:value="${blog.id}">
        <input type="hidden" name="parentComment.id" value="-1">
        <div class="field">
          <textarea name="content" placeholder="请输入评论信息..."></textarea>
        </div>
        <div class="fields">
          <div class="field m-mobile-wide m-margin-bottom-small">
            <div class="ui left icon input">
              <i class="user icon"></i>
              <input type="text" name="nickname" placeholder="姓名" th:value="${session.user}!=null ? ${session.user.nickname}">
            </div>
          </div>
          <div class="field m-mobile-wide m-margin-bottom-small">
            <div class="ui left icon input">
              <i class="mail icon"></i>
              <input type="text" name="email" placeholder="邮箱" th:value="${session.user}!=null ? ${session.user.email}">
            </div>
          </div>
          <div class="field  m-margin-bottom-small m-mobile-wide">
            <button id="commentpost-btn" type="button" class="ui teal button m-mobile-wide"><i class="edit icon"></i>发布</button>
          </div>
        </div>

Triggered on click of publish button


$('#commentpost-btn').click(function () {
        var boo = $('.ui.form').form('validate form');
        if (boo) {
            console.log('校验成功');
            postData();
        } else {
            console.log('校验失败');
        }
    });

//评论表单验证
    $('.ui.form').form({
        fields: {
            title: {
                identifier: 'content',
                rules: [{
                    type: 'empty',
                    prompt: '请输入评论内容'
                }
                ]
            },
            content: {
                identifier: 'nickname',
                rules: [{
                    type: 'empty',
                    prompt: '请输入你的大名'
                }]
            },
            type: {
                identifier: 'email',
                rules: [{
                    type: 'email',
                    prompt: '请填写正确的邮箱地址'
                }]
            }
        }
    });   

After the form is validated, the request is sent to the backend, and the form is cleared after the submission is successful

function postData() {
        $("#comment-container").load(/*[[@{/comments}]]*/"",{
            "parentComment.id" : $("[name='parentComment.id']").val(),
            "blogId" : $("[name='blogId']").val(),
            "nickname": $("[name='nickname']").val(),
            "email"   : $("[name='email']").val(),
            "content" : $("[name='content']").val()
        },function (responseTxt, statusTxt, xhr) {
            $(window).scrollTo($('#goto'),500);  //提交成功之后滚动到评论位置 
            clearContent();
        });
    }
     function clearContent() {
        $("[name='nickname']").val('');
        $("[name='email']").val('');
        $("[name='content']").val('');
        $("[name='parentComment.id']").val(-1);
        $("[name='content']").attr("placeholder", "请输入评论信息...");
    }

Click the reply button to display which user the reply is to in the comment area

<a class="reply" data-messageid="1" data-messagenickname="Matt" th:attr="data-messageid=${reply.id},data-messagenickname=${reply.nickname}" onclick="reply(this)">回复</a>

corresponding function

 function reply(obj) {
        var messageId = $(obj).data('messageid');
        var messageNickname = $(obj).data('messagenickname');
        $("[name='content']").attr("placeholder", "@"+messageNickname).focus();//在回复时显示  @对应人
        $("[name='parentMessage.id']").val(messageId);//为隐藏域赋值
        $(window).scrollTo(0,500);  //滚动
    }

Corresponding back-end code

@Controller
public class CommentController {

    @Autowired
    private CommentService commentService;

    @Autowired
    private BlogService blogService;

    @Value("${comment.avatar}")
    private String avatar;

//    查询评论列表
    @GetMapping("/comments/{blogId}")
    public String comments(@PathVariable Long blogId, Model model) {
        List<Comment> comments = commentService.listCommentByBlogId(blogId);
        model.addAttribute("comments", comments);
        return "blog :: commentList"; //返回到blog下面的commentList片段
    }

//    新增评论
    @PostMapping("/comments")
    public String post(Comment comment, HttpSession session,Model model) {
        Long blogId = comment.getBlogId();
        User user = (User) session.getAttribute("user");
        if (user != null) {
            comment.setAvatar(user.getAvatar());
            comment.setAdminComment(true);
        } else {
            //设置头像
            comment.setAvatar(avatar);
        }
		//页面中定义为-1 不会出现空指针
        if (comment.getParentComment().getId() != null) {
        //获取父对象设置对应关系
            comment.setParentCommentId(comment.getParentComment().getId());
        }
        commentService.saveComment(comment);
        List<Comment> comments = commentService.listCommentByBlogId(blogId);
        model.addAttribute("comments", comments);
        return "blog :: commentList";
    }

List settings, front-end traversal to display hierarchical relationships

 <div  class="ui bottom attached segment" th:if="${blog.commentabled}">
      <!--评论区域列表-->
      <div id="comment-container"  class="ui teal segment">
        <div th:fragment="commentList">
          <div class="ui threaded comments" style="max-width: 100%;">
            <h3 class="ui dividing header">评论</h3>
            <div class="comment" th:each="comment : ${comments}">
              <a class="avatar">
                <img src="https://unsplash.it/100/100?image=1005" th:src="@{${comment.avatar}}">
              </a>
              <div class="content">
                <a class="author" >
                  <span th:text="${comment.nickname}">Matt</span>
                  <--判断是不是博主-->
                  <div class="ui mini basic teal left pointing label m-padded-mini" th:if="${comment.adminComment}">栈主</div>
                </a>
                <div class="metadata">
                  <span class="date" th:text="${#dates.format(comment.createTime,'yyyy-MM-dd HH:mm')}">Today at 5:42PM</span>
                </div>
                <div class="text" th:text="${comment.content}">
                  How artistic!
                </div>
                <div class="actions">
                  <a class="reply" data-commentid="1" data-commentnickname="Matt" th:attr="data-commentid=${comment.id},data-commentnickname=${comment.nickname}" onclick="reply(this)">回复</a>
                  <a class="delete" href="#" th:href="@{/comment/{param1}/{param2}/delete(param1=${comment.blogId},param2=${comment.id})}" onclick="return confirm('确定要删除该评论吗?')" th:if="${session.user}">删除</a>       
                </div>
              </div>
              <!--子集评论-->
              <div class="comments" th:if="${#arrays.length(comment.replyComments)}>0">
                <div class="comment" th:each="reply : ${comment.replyComments}">
                  <a class="avatar">
                    <img src="https://unsplash.it/100/100?image=1005" th:src="@{${reply.avatar}}">
                  </a>
                  <div class="content">
                    <a class="author" >
                      <span th:text="${reply.nickname}">小红</span>
                      <!--若是博主 给标签-->
                      <div class="ui mini basic teal left pointing label m-padded-mini" th:if="${reply.adminComment}">栈主</div>
                      &nbsp;<span th:text="|@ ${reply.parentNickname}|" class="m-teal">@ 小白</span>
                    </a>
                    <div class="metadata">
                      <span class="date" th:text="${#dates.format(reply.createTime,'yyyy-MM-dd HH:mm')}">Today at 5:42PM</span>
                    </div>
                    <div class="text" th:text="${reply.content}">
                      How artistic!
                    </div>
                    <div class="actions">
                      <a class="reply" data-commentid="1" data-commentnickname="Matt" th:attr="data-commentid=${reply.id},data-commentnickname=${reply.nickname}" onclick="reply(this)">回复</a>
                      <a class="delete" href="#" th:href="@{/comment/{param1}/{param2}/delete(param1=${reply.blogId},param2=${reply.id})}" onclick="return confirm('确定要删除该评论吗!')" th:if="${session.user}">删除</a>

                    </div>
                  </div>
                </div>
              </div>
            </div>

          </div>
        </div>
      </div>

Back-end implementation logic: first obtain the top-level data, find and put it into the collection layer by layer

@Service
public class CommentServiceImpl implements CommentService {

    @Autowired
    private CommentDao commentDao;

    @Autowired
    private BlogDao blogDao;

    //存放迭代找出的所有子代的集合
    private List<Comment> tempReplys = new ArrayList<>();

    @Override
    public List<Comment> listCommentByBlogId(Long blogId) {
        //查询出父节点
        List<Comment> comments = commentDao.findByBlogIdParentIdNull(blogId, Long.parseLong("-1"));
        for(Comment comment : comments){
            Long id = comment.getId();
            String parentNickname1 = comment.getNickname();
            List<Comment> childComments = commentDao.findByBlogIdParentIdNotNull(blogId,id);
//            查询出子评论
            combineChildren(blogId, childComments, parentNickname1);
            comment.setReplyComments(tempReplys);
            tempReplys = new ArrayList<>();
        }
        return comments;
    }

    private void combineChildren(Long blogId, List<Comment> childComments, String parentNickname1) {
//        判断是否有一级子评论
        if(childComments.size() > 0){
//                循环找出子评论的id
            for(Comment childComment : childComments){
                String parentNickname = childComment.getNickname();
                childComment.setParentNickname(parentNickname1);
                tempReplys.add(childComment);
                Long childId = childComment.getId();
//                    查询出子二级评论
                recursively(blogId, childId, parentNickname);
            }
        }
    }

    private void recursively(Long blogId, Long childId, String parentNickname1) {
//        根据子一级评论的id找到子二级评论
        List<Comment> replayComments = commentDao.findByBlogIdAndReplayId(blogId,childId);

        if(replayComments.size() > 0){
            for(Comment replayComment : replayComments){
                String parentNickname = replayComment.getNickname();
                replayComment.setParentNickname(parentNickname1);
                Long replayId = replayComment.getId();
                tempReplys.add(replayComment);
                recursively(blogId,replayId,parentNickname);
            }
        }
    }

//    新增评论
    @Override
    public int saveComment(Comment comment) {
        comment.setCreateTime(new Date());
        int comments = commentDao.saveComment(comment);
//        文章评论计数
        blogDao.getCommentCountById(comment.getBlogId());
        return comments;
    }
}

Summary: This chapter mainly briefly introduces the implementation of the blog comment function, but the function is not comprehensive. Implementation logic: Because the blog ordinary users do not need to log in to browse, so there is no ordinary user login function. When commenting, you need to enter your own name and email address to comment. If it is a blogger's comment, it is through the field defined by the entity class in the front end. Make judgments and add tags if you are a blogger.

Guess you like

Origin blog.csdn.net/weixin_52210557/article/details/124161315