JAVA设计评论列表数据格式

需求:

用户可以评论,也可以回复评论,其他用户还可以评论、回复评论。

评论表实体(ArticleComment)

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * 评论表
 *
 * @author create by xiegege
 * @date 2021/6/17 16:00
 * 表中的levelFlag为评论等级,1表示一级,主要是文章的直接评论, 2表示二级,主要是主题评论的回复
 **/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_article_comment")
@ApiModel(value = "ArticleComment对象", description = "评论表")
public class ArticleComment {

    @ApiModelProperty(value = "评论主键")
    @TableId("id")
    private Long id;

    @ApiModelProperty(value = "评论的文章Id")
    @TableField("article_id")
    private Long articleId;

    @ApiModelProperty(value = "父级评论Id")
    @TableField("parent_id")
    private Long parentId;

    @ApiModelProperty(value = "评论内容")
    @TableField("comment_content")
    private String commentContent;

    @ApiModelProperty(value = "评论等级")
    @TableField("level_flag")
    private Integer levelFlag;

    @ApiModelProperty(value = "点赞数")
    @TableField("upvote_count")
    private Integer upvoteCount;

    @ApiModelProperty(value = "是否置顶评论(0否,1是)")
    @TableField("whether_top")
    private Integer whetherTop;

    @ApiModelProperty(value = "用户Id")
    @TableField("user_id")
    private Long userId;

    @ApiModelProperty(value = "创建时间")
    @TableField("create_time")
    private String createTime;
}

建表sql语句

CREATE TABLE `sys_article_comment` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '评论主键',
  `article_id` bigint(20) NOT NULL COMMENT '评论的文章Id',
  `parent_id` bigint(20) NOT NULL COMMENT '父级评论Id',
  `comment_content` varchar(255) NOT NULL COMMENT '评论内容',
  `level_flag` int(10) NOT NULL COMMENT '评论等级:1主评论,2子评论',
  `upvote_count` int(10) NOT NULL DEFAULT '0' COMMENT '评论点赞数量',
  `whether_top` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否置顶评论(0否,1是)',
  `user_id` bigint(20) NOT NULL COMMENT '用户id',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`),
  KEY `index_id` (`article_id`,`parent_id`,`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

我的表设计的很简单,因为本身项目就很简单,而且估计流量也不多,需求没有那么的大,别的话不说,如果你自己有更复杂的需求,可以自己再设计,我只是提供一个思路;

前端需求:
类似于这种:

/**
          张三 评论:你好
                谢哥哥 回复 张三: 你好
                    王五 回复 谢哥哥: 很好很好
                赵六 回复 张三:你好
          田七 评论:辣鸡
                谢哥哥 回复 田七: 你才是辣鸡
**/

抱歉,我以这种简陋的方式大家展示(手动捂脸)~

既然需求搞清楚了,基本就可以操作了,看这个前端需求,和一个数据结构有点像,就是树形结构,具体看下面的图(其实是手打的)

/**
*                                root
*                          /      |     \
*                         1       2      3
*                     / | | | \
*                    4  5 6 7  8
**/

所以就直接以树的形式进行组装就行了

评论表数据结构组装VO(CommentNodeTreeVo)

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

/**
 * 评论表数据结构组装
 *
 * @author create by xiegege
 * @date 2021/6/17 16:10
 **/
@Data
public class CommentNodeTreeVo {

    @ApiModelProperty(value = "评论表id")
    private Long commentId;

    @ApiModelProperty(value = "用户Id")
    private Long userId;

    @ApiModelProperty(value = "用户头像")
    private String avatar;

    @ApiModelProperty(value = "用户昵称")
    private String nickName;

    @ApiModelProperty(value = "评论内容")
    private String commentContent;

    @ApiModelProperty(value = "点赞数")
    private Integer upvoteCount;

    @ApiModelProperty(value = "是否置顶评论(0否,1是)")
    private Integer whetherTop;

    @ApiModelProperty(value = "创建时间")
    private String createTime;

    private List<CommentNodeTreeVo> commentNodeTreeVos;
}

其中commentNodeTreeVos为root的下一层的节点集合;

Controller

import com.xgg.common.result.AjaxResult;
import com.xgg.javacode.model.vo.CommentNodeTreeVo;
import com.xgg.javacode.service.ArticleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author xiegege
 * @date 2021/6/17 17:20
 */
@Api(tags = "文章相关api")
@RequestMapping("/article")
@RestController
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Slf4j
public class ArticleController {

    private final ArticleService articleService;

    @ApiOperation(value = "获取评论列表", response = CommentNodeTreeVo.class)
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "path", name = "articleId", dataType = "long", required = true, value = "文章id"),
            @ApiImplicitParam(paramType = "path", name = "pageNo", dataType = "int", required = true, value = "页码"),
            @ApiImplicitParam(paramType = "path", name = "pageSize", dataType = "int", required = true, value = "每页大小")
    })
    @GetMapping("findComment/{articleId}/{pageNo}/{pageSize}")
    public AjaxResult findComment(@PathVariable Long articleId, @PathVariable Integer pageNo, @PathVariable Integer pageSize) {
        return AjaxResult.success(articleService.findComment(articleId, pageNo, pageSize));
    }
}

Service(具体业务实现)

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xgg.common.result.PageInfoResult;
import com.xgg.javacode.mapper.ArticleMapper;
import com.xgg.javacode.model.vo.CommentNodeTreeVo;
import com.xgg.javacode.service.ArticleService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;

/**
 * @author xiegege
 * @date 2021/6/17 17:25
 */
@Service
@Transactional(rollbackFor = Exception.class)
@AllArgsConstructor
public class ArticleServiceImpl implements ArticleService {

    private final ArticleMapper articleMapper;

    @Override
    public PageInfoResult<CommentNodeTreeVo> findComment(Long articleId, Integer pageNo, Integer pageSize) {
        PageHelper.startPage(pageNo, pageSize);
        List<CommentNodeTreeVo> list = articleMapper.findComment(articleId);
        PageInfo<CommentNodeTreeVo> pageInfo = new PageInfo<>(list);
        List<CommentNodeTreeVo> pageInfoList = pageInfo.getList();
        for (CommentNodeTreeVo commentNodeTreeVo : pageInfoList) {
            deepSearch(commentNodeTreeVo);
        }
        return PageInfoResult.getPageInfoResult(pageInfo);
    }

    /**
     * 递归进行求树形节点
     *
     * @param commentNodeTreeVo 评论树
     */
    private void deepSearch(CommentNodeTreeVo commentNodeTreeVo) {
        if (commentNodeTreeVo != null) {
            // 查询子评论集合
            List<CommentNodeTreeVo> commentNodeTreeVos = articleMapper.findChildComment(commentNodeTreeVo.getCommentId());
            if (!CollectionUtils.isEmpty(commentNodeTreeVos)) {
                for (CommentNodeTreeVo vo : commentNodeTreeVos) {
                    commentNodeTreeVo.setCommentNodeTreeVos(commentNodeTreeVos);
                    deepSearch(vo);
                }
            }
        }
    }
}

Mapper.xml(sql语句)

<?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.xgg.javacode.mapper.ArticleMapper">

    <sql id="res">
        ac.id comment_id,
        u.user_id,
        u.avatar,
        u.nick_name,
        ac.comment_content,
        ac.upvote_count,
        ac.whether_top,
        DATE_FORMAT(ac.create_time,'%Y-%m-%d %H:%i') create_time
    </sql>

    <select id="findComment" resultType="com.xgg.javacode.model.vo.CommentNodeTreeVo">
        select
        <include refid="res"/>
        from sys_article_comment ac
        left join sys_user u on ac.user_id=u.user_id
        where ac.level_flag=1 and ac.article_id=#{articleId}
        order by ac.whether_top desc,ac.create_time desc
    </select>

    <select id="findChildComment" resultType="com.xgg.javacode.model.vo.CommentNodeTreeVo">
        select
        <include refid="res"/>
        from sys_article_comment ac
        left join sys_user u on ac.user_id=u.user_id
        where ac.level_flag=2 and ac.parent_id=#{commentId}
        order by ac.create_time desc
    </select>
</mapper>

自己简易封装返回的分页结果集(PageInfoResult)

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.pagehelper.PageInfo;
import lombok.Data;

import java.util.List;

/**
 * 分页结果集
 *
 * @author xiegege
 * @date 2020/4/17
 */
@Data
public class PageInfoResult<T> {

    /**
     * 当前页
     */
    private int pageNo;
    /**
     * 当前页的数量
     */
    private int currentPageNum;
    /**
     * 数据总数量
     */
    private long total;
    /**
     * 总页数
     */
    private int totalPages;
    /**
     * 结果集
     */
    private List<T> list;

    public static PageInfoResult getPageInfoResult(PageInfo pageInfo) {
        PageInfoResult result = new PageInfoResult();
        result.setPageNo(pageInfo.getPageNum());
        result.setCurrentPageNum(pageInfo.getSize());
        result.setTotal(pageInfo.getTotal());
        result.setTotalPages(pageInfo.getPages());
        result.setList(pageInfo.getList());
        return result;
    }

    /**
     * 分页数据转换
     * 将mybatis的分页数据转为自定义分页数据
     * @param iPage
     * @return
     */
    public static PageInfoResult convert(IPage iPage) {
        PageInfoResult result = new PageInfoResult();
        result.setPageNo(new Long(iPage.getCurrent()).intValue());
        result.setCurrentPageNum(new Long(iPage.getSize()).intValue());
        result.setTotal(iPage.getTotal());
        result.setTotalPages(new Long(iPage.getPages()).intValue());
        result.setList(iPage.getRecords());
        return result;
    }
}

使用递归进行组装评论树,这样就OK了,结果如下:
在这里插入图片描述

{
  "msg": "操作成功",
  "code": 200,
  "data": {
    "pageNo": 1,
    "currentPageNum": 3,
    "total": 3,
    "totalPages": 1,
    "list": [
      {
        "commentId": 2,
        "userId": 12,
        "avatar": "",
        "nickName": "田七",
        "commentContent": "不错不错",
        "upvoteCount": 0,
        "whetherTop": 1,
        "createTime": "2021-06-18 14:41",
        "commentNodeTreeVos": [
          {
            "commentId": 4,
            "userId": 2,
            "avatar": "",
            "nickName": "张三",
            "commentContent": "啥不错?",
            "upvoteCount": 0,
            "whetherTop": 0,
            "createTime": "2021-06-18 14:42",
            "commentNodeTreeVos": [
              {
                "commentId": 5,
                "userId": 12,
                "avatar": "",
                "nickName": "田七",
                "commentContent": "这篇文章写的不错!",
                "upvoteCount": 0,
                "whetherTop": 0,
                "createTime": "2021-06-18 14:43",
                "commentNodeTreeVos": [
                  {
                    "commentId": 7,
                    "userId": 1,
                    "avatar": "http://xxx.oss-cn-shenzhen.aliyuncs.com/upload/xxx/20210127/4441d54f-1847-4d35-a58d-a2cafc159e74.gif",
                    "nickName": "谢哥哥",
                    "commentContent": "谢谢支持",
                    "upvoteCount": 0,
                    "whetherTop": 0,
                    "createTime": "2021-06-18 16:06",
                    "commentNodeTreeVos": null
                  },
                  {
                    "commentId": 6,
                    "userId": 4,
                    "avatar": "",
                    "nickName": "李四",
                    "commentContent": "是挺不错的",
                    "upvoteCount": 0,
                    "whetherTop": 0,
                    "createTime": "2021-06-18 14:44",
                    "commentNodeTreeVos": null
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "commentId": 3,
        "userId": 5,
        "avatar": "",
        "nickName": "王五",
        "commentContent": "博主厉害呀!!!",
        "upvoteCount": 0,
        "whetherTop": 0,
        "createTime": "2021-06-18 14:42",
        "commentNodeTreeVos": null
      },
      {
        "commentId": 1,
        "userId": 11,
        "avatar": "",
        "nickName": "赵六",
        "commentContent": "写的不错",
        "upvoteCount": 0,
        "whetherTop": 0,
        "createTime": "2021-06-18 14:40",
        "commentNodeTreeVos": null
      }
    ]
  }
}

写在最后

谢谢你的点击与阅读,如果文章对你有帮助的话,请动动您发财的小手点个小赞、收藏或者关注博主一下,谢谢!一心原创,为了后人乘凉。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢咯咯剥壳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值