Java single table implements comment reply function

1 Introduction

Recently, when writing a graduation project, I found that a comment function needs to be implemented, and then I looked at the comment areas of Nuggets and csdn. How to implement the comment function?

There are several ways to implement the comment function:

  • single layer
  • Matryoshka type (multi-layer type)
  • Two-layer type

Single layer type:
insert image description here
Matryoshka type:
insert image description here

Two-layer type:
insert image description here

2. Function realization diagram

insert image description here

3. Database design

There is an answer_id in this place, which is easy to confuse: which user's id is the reply

CREATE TABLE `tb_blog_comments`  (
  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
  `user_id` bigint(20) UNSIGNED NOT NULL COMMENT '用户id',
  `blog_id` bigint(20) UNSIGNED NOT NULL COMMENT '探店id',
  `parent_id` bigint(20) UNSIGNED NOT NULL COMMENT '关联的1级评论id,如果是一级评论,则值为0',
  `answer_id` bigint(20) UNSIGNED NOT NULL COMMENT '回复的评论id',
  `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '回复的内容',
  `liked` int(8) UNSIGNED NULL DEFAULT 0 COMMENT '点赞数',
  `status` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '状态,0:正常,1:被举报,2:禁止查看',
  `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  `update_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;

SET FOREIGN_KEY_CHECKS = 1;

4. Entity class

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 
 * </p>
 *
 * @author 尹稳健
 * @since 2022-11-09
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_blog_comments")
public class BlogComments implements Serializable {
    
    

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 用户id
     */
    private Long userId;

    /**
     * 探店id
     */
    private Long blogId;

    /**
     * 关联的1级评论id,如果是一级评论,则值为0
     */
    private Long parentId;

    /**
     * 回复的评论id
     */
    private Long answerId;

    /**
     * 回复的内容
     */
    private String content;

    /**
     * 点赞数
     */
    private Integer liked;

    /**
     * 状态,0:正常,1:被举报,2:禁止查看
     */
    private Boolean status;

    /**
     * 创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;

    /**
     * 是否点赞
     */
    @TableField(exist = false)
    private Boolean isLike;

    /**
     * 子评论
     */
    @TableField(exist = false)
    List<BlogComments> children;

    /**
     * 评论者的昵称
     */
    @TableField(exist = false)
    private String nickName;

    /** 评论者的头像 */
    @TableField(exist = false)
    private String icon;

    /** 评论者的上级昵称 */
    @TableField(exist = false)
    private String pNickName;

    /** 评论者的的上级头像 */
    @TableField(exist = false)
    private String pIcon;
}

5. Realize ideas

  • Because there are commentstwo floors, so there must be a parent_id , so that you can know which comment you are replying to, if you continue to comment, then the parent_id of that comment is the parent_id of the previous comment, not the id of the sub-comment.
  • The reply content is also a comment entity class, but it is just agather, so use List storage, generic use entity class
  • My function implementation also uses the username and avatar of the parent comment, so that you can better see who commented and who to reply to

6. Function realization

6.1 Get Sql

First of all, we need to know what data we need and return it to the front end

If you don't even know mybatis, then look at the idea.
From here, you can get all the data of the comment form, as well as the comments.information about people, such as nickname, avatar, id, can be displayed on the front end

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.BlogCommentsMapper">
    <select id="findCommentDetail" resultType="com.sky.pojo.BlogComments">
        SELECT
            bl.*,
            u.icon,
            u.nick_name
        FROM `tb_blog_comments` bl
        left join tb_user u
        on u.id = bl.user_id
        where bl.blog_id = #{blogId}
        order by bl.id desc
    </select>
</mapper>

Corresponding mapper interface

package com.sky.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sky.pojo.BlogComments;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author 尹稳健
 * @since 2022-11-09
 */
@Mapper
public interface BlogCommentsMapper extends BaseMapper<BlogComments> {
    
    

    /**
     * 查询所有的评论信息
     * @param blogId
     * @return
     */
    List<BlogComments> findCommentDetail(Long blogId);
}

6.2 Business Realization

  • 1. First we need to get all the data from the data
  • 2. Then we need to find allLevel 1 Review, the first-level comment is the highest level, if he is not under anyone, he is the biggest. When I added a comment here, the front-end processed it. As long as it is a first-level comment, his paren_id = 0
  • 3. Then we need to add all sub-comments below him from below the first-level comment
  • The most important thing is how to add the information of the parent's comments to its own data?
  • 4. Find the parent comment through the paren_id in the child comment , and then pass the answer_id of the child comment == the user_id of the parent comment , so that you can get the data of the parent comment
  • Finally, add it to the data of the child comment through Optional
@Override
public Result showBlogComments(Long blogId) {
    
    
    // 先将数据库中的数据全部查询出来,包括评论作者的信息,昵称和头像
    List<BlogComments> blogComments = blogCommentsMapper.findCommentDetail(blogId);
    // 获取所有的一级评论
    List<BlogComments> rootComments = blogComments.stream().filter(blogComments1 -> blogComments1.getParentId() == 0).collect(Collectors.toList());
    // 从一级评论中获取回复评论
    for (BlogComments rootComment : rootComments) {
    
    
        // 回复的评论
        List<BlogComments> comments = blogComments.stream()
                .filter(blogComment -> blogComment.getParentId().equals(rootComment.getId()))
                .collect(Collectors.toList());
        // 回复评论中含有父级评论的信息
        comments.forEach(comment -> {
    
    
            // 无法判断pComment是否存在,可以使用Optional
            Optional<BlogComments> pComment
                    = blogComments
                    .stream()
                    // 获取所有的评论的回复id也就是父级id的userid,这样就可以获取到父级评论的信息
                    .filter(blogComment -> comment.getAnswerId().equals(blogComment.getUserId())).findFirst();
            // 这里使用了Optional 只有pcomment!=null 的时候才会执行下面的代码
            pComment.ifPresent(v -> {
    
    
                comment.setPNickName(v.getNickName());
                comment.setPIcon(v.getIcon());
            });
            // 判断是否点赞
            isBlogCommentLiked(comment);
        });
        rootComment.setChildren(comments);
        // 判断是否点赞
        isBlogCommentLiked(rootComment);
    }
    return Result.ok(rootComments);
}

7. Front-end implementation

Because there are a lot of front-end codes, only the key codes are copied

<html>
<body>
<div class="comment-list">
          <div class="comment-box" style="display: block" v-for="comment in commnetList" :key="comment.id">
            <div style="display:flex">
              <!-- 评论者头像 -->
              <div class="comment-icon">
                <img :src="comment.icon" alt="">
              </div>
              <!-- 评论div -->
              <div class="comment-info">
                <!-- 评论者昵称 -->
                <div class="comment-user">
                  {
   
   {comment.nickName}}
                </div>
                <!-- 评论时间 -->
                <div style="display: flex">
                  {
   
   {comment.createTime}}
                  <!-- 评论点赞,回复按钮 -->
                  <div style="margin-left: 42%;display: flex;">
                    <div @click="addCommnetLike(comment)" style="display: flex">
                      <svg t="1646634642977" class="icon" viewBox="0 0 1024 1024" version="1.1"
                        xmlns="http://www.w3.org/2000/svg" p-id="2187" width="14" height="14">
                        <path
                          d="M160 944c0 8.8-7.2 16-16 16h-32c-26.5 0-48-21.5-48-48V528c0-26.5 21.5-48 48-48h32c8.8 0 16 7.2 16 16v448zM96 416c-53 0-96 43-96 96v416c0 53 43 96 96 96h96c17.7 0 32-14.3 32-32V448c0-17.7-14.3-32-32-32H96zM505.6 64c16.2 0 26.4 8.7 31 13.9 4.6 5.2 12.1 16.3 10.3 32.4l-23.5 203.4c-4.9 42.2 8.6 84.6 36.8 116.4 28.3 31.7 68.9 49.9 111.4 49.9h271.2c6.6 0 10.8 3.3 13.2 6.1s5 7.5 4 14l-48 303.4c-6.9 43.6-29.1 83.4-62.7 112C815.8 944.2 773 960 728.9 960h-317c-33.1 0-59.9-26.8-59.9-59.9v-455c0-6.1 1.7-12 5-17.1 69.5-109 106.4-234.2 107-364h41.6z m0-64h-44.9C427.2 0 400 27.2 400 60.7c0 127.1-39.1 251.2-112 355.3v484.1c0 68.4 55.5 123.9 123.9 123.9h317c122.7 0 227.2-89.3 246.3-210.5l47.9-303.4c7.8-49.4-30.4-94.1-80.4-94.1H671.6c-50.9 0-90.5-44.4-84.6-95l23.5-203.4C617.7 55 568.7 0 505.6 0z"
                          p-id="2188" :fill="comment.isLike ? '#ff6633' : '#82848a'"></path>
                      </svg>
                      &nbsp;{
   
   {comment.liked}}
                    </div>
                    <!--  评论回复 -->
                    <div style=" display:flex">
                      &nbsp;&nbsp;&nbsp;&nbsp;
                      <el-dropdown trigger="click" size="mini" placement="top" type="mini">
                        <i class="el-icon-more"></i>
                        <el-dropdown-menu>
                          <el-dropdown-item>
                            <div @click="replyCommentForm(comment)">
                              回复
                            </div>
                          </el-dropdown-item>
                          <el-dropdown-item>
                            <div v-if="comment.userId == user.id" @click="deleteComment(comment.id)">
                              删除
                            </div>
                          </el-dropdown-item>
                        </el-dropdown-menu>
                      </el-dropdown>
                      &nbsp;
                    </div>
                  </div>
                </div>
                <!-- 评论主题 : 评论内容,点赞,回复 -->
                <div style="padding: 5px 0; font-size: 14px;display: flex;">
                  <!-- 评论内容 -->
                  <div>
                    {
   
   {comment.content}}
                  </div>
                </div>
              </div>
            </div>

            <!-- 回复的内容 -->
            <div v-if="comment.children.length" style="padding-left: 5px;">
              <div v-for="reply in comment.children" :key="reply.id" style="padding: 5px 10px">
                <div style="display: flex">
                  <!-- 评论者头像 -->
                  <div class="comment-icon">
                    <img :src="reply.icon" alt="">
                  </div>
                  <!-- 评论div -->
                  <div class="comment-info">
                    <!-- 评论者昵称 -->
                    <div class="comment-user">
                      {
   
   {reply.nickName}} &nbsp;回复: {
   
   {reply.pnickName}}
                    </div>
                    <!-- 评论时间 -->
                    <div style="display: flex">
                      {
   
   {reply.createTime}}
                      <!-- 评论点赞,回复按钮 -->
                      <div style="margin-left: 40%;display: flex;">
                        <div style="display: flex" @click="addCommnetLike(reply)">
                          <svg t="1646634642977" class="icon" viewBox="0 0 1024 1024" version="1.1"
                            xmlns="http://www.w3.org/2000/svg" p-id="2187" width="14" height="14">
                            <path
                              d="M160 944c0 8.8-7.2 16-16 16h-32c-26.5 0-48-21.5-48-48V528c0-26.5 21.5-48 48-48h32c8.8 0 16 7.2 16 16v448zM96 416c-53 0-96 43-96 96v416c0 53 43 96 96 96h96c17.7 0 32-14.3 32-32V448c0-17.7-14.3-32-32-32H96zM505.6 64c16.2 0 26.4 8.7 31 13.9 4.6 5.2 12.1 16.3 10.3 32.4l-23.5 203.4c-4.9 42.2 8.6 84.6 36.8 116.4 28.3 31.7 68.9 49.9 111.4 49.9h271.2c6.6 0 10.8 3.3 13.2 6.1s5 7.5 4 14l-48 303.4c-6.9 43.6-29.1 83.4-62.7 112C815.8 944.2 773 960 728.9 960h-317c-33.1 0-59.9-26.8-59.9-59.9v-455c0-6.1 1.7-12 5-17.1 69.5-109 106.4-234.2 107-364h41.6z m0-64h-44.9C427.2 0 400 27.2 400 60.7c0 127.1-39.1 251.2-112 355.3v484.1c0 68.4 55.5 123.9 123.9 123.9h317c122.7 0 227.2-89.3 246.3-210.5l47.9-303.4c7.8-49.4-30.4-94.1-80.4-94.1H671.6c-50.9 0-90.5-44.4-84.6-95l23.5-203.4C617.7 55 568.7 0 505.6 0z"
                              p-id="2188" :fill="reply.isLike ? '#ff6633' : '#82848a'"></path>
                          </svg>
                          &nbsp;{
   
   {reply.liked}}
                        </div>
                        <div style="display:flex">
                          &nbsp;&nbsp;&nbsp;&nbsp;
                          <el-dropdown trigger="click" size="mini" placement="top" type="mini">
                            <i class="el-icon-more"></i>
                            <el-dropdown-menu>
                              <el-dropdown-item>
                                <div @click="replyCommentForm(reply)">
                                  回复
                                </div>
                              </el-dropdown-item>
                              <el-dropdown-item>
                                <div v-if="reply.userId == user.id" @click="deleteComment(reply.id)">
                                  删除
                                </div>
                              </el-dropdown-item>
                            </el-dropdown-menu>
                          </el-dropdown>
                          &nbsp;
                        </div>
                      </div>
                    </div>
                    <!-- 评论内容 -->
                    <div style="padding: 5px 0; font-size: 14px;display: flex;">
                      <!-- 评论内容 -->
                      <div>
                        {
   
   {reply.content}}
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
<script>
    let each = function (ary, callback) {
      
      
      for (let i = 0, l = ary.length; i < l; i++) {
      
      
        if (callback(ary[i], i) === false) break
      }
    }
    const app = new Vue({
      
      
      el: "#app",
      data: {
      
      
        util,
        showPopover: false,
        actions: [{
      
       text: '回复', icon: '' }, {
      
       text: '删除', icon: '' }],
        commentsTotal: '',  // 评论总数
        checkCommentInputVisible: false,  // 判断是否点击回复
        commnetList: {
      
      },  // 评论回复列表
        placeholderText: '发条友善评论吧~~',
        commentForm: {
      
      
          content: '',
          userId: '',
          blogId: '',
          parentId: 0,
          answerId: 0,
        },   // 评论表单
        blog: {
      
      },
        shop: {
      
      },
        likes: [],
        user: {
      
      }, // 登录用户
      methods: {
      
      

        replyCommentForm(comment) {
      
      
          this.placeholderText = "回复 " + comment.nickName
          this.checkCommentInputVisible = true;
          if (comment.parentId == 0) {
      
      
            this.commentForm.parentId = comment.id;
          } else {
      
      
            this.commentForm.parentId = comment.parentId;
          }
          this.commentForm.answerId = comment.userId;
        },
        submitCommentForm() {
      
      
          this.commentForm.userId = this.user.id;
          this.commentForm.blogId = this.blog.id;
          axios.post("/blog-comments/saveBlogComment", this.commentForm)
            .then(res => {
      
      
              this.loadComments(this.blog.id);
              this.checkCommentInputVisible = false;
            })
            .catch(err => this.$message.error(err))
        },
        loadComments(id) {
      
      
          axios.get("/blog-comments/showBlogComments/" + id)
            .then(res => {
      
      
              this.commnetList = res.data
            })
            .catch(err => this.$message.err(error))
        },
        }
  </script>
  </body>

</html>

8. Final result

insert image description here
Like, Comment, Reply, Delete
insert image description here

If you need the source code, you can leave a message in the comment area, I will read it every day! grateful!

Guess you like

Origin blog.csdn.net/weixin_46073538/article/details/128149632
Recommended