前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >评论模块 - 后端数据库设计及功能实现

评论模块 - 后端数据库设计及功能实现

作者头像
solocoder
发布2022-04-06 13:06:27
8051
发布2022-04-06 13:06:27
举报
文章被收录于专栏:大前端客栈

2018-11-21 更新

感谢大佬们在评论区提的优化建议,根据建议优化了数据表的设计和代码结构。

新的文章:

评论模块优化 - 数据表优化、添加缓存及用 Feign 与用户服务通信

原文:

评论模块在很多系统中都有,CodeRiver河码 作为类似程序员客栈的沟通协作平台自然也不会少。

前端界面是参考了简书的评论模块,专门写了一篇文章介绍实现步骤:

vue + element-ui + scss 仿简书评论模块

感兴趣的可以看看。

项目地址:https://github.com/cachecats/coderiver

代码在 根目录/java/comments-service

文章将分三部分介绍:

  1. 前端界面分析
  2. 数据库设计
  3. 功能实现

一、前端界面分析

先看看前端界面长什么样,知道了前端需要什么数据,就知道数据库该怎么设计了。

首先评论的主体可以是人、项目、资源,所以要有一个 type 字段标明这条评论的类型。

以项目为例,一个项目下面可能会有多条评论。每条评论其实分为两种,一种是直接对项目的评论,称之为父评论吧;另一种是对已有评论的评论,称为子评论。

梳理一下关系,每个项目可能有多个父评论,每个父评论可能有多个子评论。项目与父评论,父评论与子评论,都是一对多的关系。

由此可知数据库应该分为两个表,一个存储父评论,一个存储子评论。

再看都需要什么字段,先分析主评论。必须要有的是项目id,得知道是对谁评论的,叫 ownerId 吧。还有评论者的头像、昵称、id,还有评论时间、内容、点赞个数等。

子评论跟父评论的字段差不多,只是不要点赞数量。

二、数据库设计

分析了界面,知道需要什么字段,就开始设计数据库吧。

评论主表(父评论表)

代码语言:javascript
复制
CREATE TABLE `comments_info` (
  `id` varchar(32) NOT NULL COMMENT '评论主键id',
  `type` tinyint(1) NOT NULL COMMENT '评论类型:对人评论,对项目评论,对资源评论',
  `owner_id` varchar(32) NOT NULL COMMENT '被评论者id,可以是人、项目、资源',
  `from_id` varchar(32) NOT NULL COMMENT '评论者id',
  `from_name` varchar(32) NOT NULL COMMENT '评论者名字',
  `from_avatar` varchar(512) DEFAULT '' COMMENT '评论者头像',
  `like_num` int(11) DEFAULT '0' COMMENT '点赞的数量',
  `content` varchar(512) DEFAULT NULL COMMENT '评论内容',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  KEY `owner_id` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论主表';

评论回复表(子评论表)

代码语言:javascript
复制
CREATE TABLE `comments_reply` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `comment_id` varchar(32) NOT NULL COMMENT '评论主表id',
  `from_id` varchar(32) NOT NULL COMMENT '评论者id',
  `from_name` varchar(32) NOT NULL COMMENT '评论者名字',
  `from_avatar` varchar(512) DEFAULT '' COMMENT '评论者头像',
  `to_id` varchar(32) NOT NULL COMMENT '被评论者id',
  `to_name` varchar(32) NOT NULL COMMENT '被评论者名字',
  `to_avatar` varchar(512) DEFAULT '' COMMENT '被评论者头像',
  `content` varchar(512) DEFAULT NULL COMMENT '评论内容',
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (`id`),
  KEY `comment_id` (`comment_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COMMENT='评论回复表';

三、功能实现

项目采用 SpringCloud 微服务架构,评论模块跟其他模块的关联性不强,可以抽出为一个单独的服务 comments-service

数据实体对象

数据实体对象 CommentsInfo

代码语言:javascript
复制
package com.solo.coderiver.comments.dataobject;

import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.Id;
import java.util.Date;
/**

评论表主表

 */
@Entity
@Data
@DynamicUpdate
public class CommentsInfo {
<span class="hljs-comment">//评论主键id</span>
<span class="hljs-meta">@Id</span>
<span class="hljs-keyword">private</span> String id;

<span class="hljs-comment">//评论类型。1用户评论,2项目评论,3资源评论</span>
<span class="hljs-keyword">private</span> Integer type;

<span class="hljs-comment">//被评论者的id</span>
<span class="hljs-keyword">private</span> String ownerId;

<span class="hljs-comment">//评论者id</span>
<span class="hljs-keyword">private</span> String fromId;

<span class="hljs-comment">//评论者名字</span>
<span class="hljs-keyword">private</span> String fromName;

<span class="hljs-comment">//评论者头像</span>
<span class="hljs-keyword">private</span> String fromAvatar;

<span class="hljs-comment">//获得点赞的数量</span>
<span class="hljs-keyword">private</span> Integer likeNum;

<span class="hljs-comment">//评论内容</span>
<span class="hljs-keyword">private</span> String content;

<span class="hljs-comment">//创建时间</span>
<span class="hljs-keyword">private</span> Date createTime;

<span class="hljs-comment">//更新时间</span>
<span class="hljs-keyword">private</span> Date updateTime;

}

数据实体对象 CommentsReply

代码语言:javascript
复制
package com.solo.coderiver.comments.dataobject;

import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Date;
/**

评论回复表

 */
@Entity
@Data
@DynamicUpdate
public class CommentsReply {
<span class="hljs-meta">@Id</span>
<span class="hljs-meta">@GeneratedValue</span>(strategy = GenerationType.IDENTITY)
<span class="hljs-keyword">private</span> Integer id;

<span class="hljs-comment">//评论主表id</span>
<span class="hljs-keyword">private</span> String commentId;

<span class="hljs-comment">//评论者id</span>
<span class="hljs-keyword">private</span> String fromId;

<span class="hljs-comment">//评论者名字</span>
<span class="hljs-keyword">private</span> String fromName;

<span class="hljs-comment">//评论者头像</span>
<span class="hljs-keyword">private</span> String fromAvatar;

<span class="hljs-comment">//被评论者id</span>
<span class="hljs-keyword">private</span> String toId;

<span class="hljs-comment">//被评论者名字</span>
<span class="hljs-keyword">private</span> String toName;

<span class="hljs-comment">//被评论者头像</span>
<span class="hljs-keyword">private</span> String toAvatar;

<span class="hljs-comment">//评论内容</span>
<span class="hljs-keyword">private</span> String content;

<span class="hljs-comment">//创建时间</span>
<span class="hljs-keyword">private</span> Date createTime;

<span class="hljs-comment">//更新时间</span>
<span class="hljs-keyword">private</span> Date updateTime;

}

数据库操作仓库 repository

操作数据库暂时用的是 Jpa ,后期可能会增加一份 mybatis 的实现。

CommentsInfoRepository

代码语言:javascript
复制
package com.solo.coderiver.comments.repository;

import com.solo.coderiver.comments.dataobject.CommentsInfo;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface CommentsInfoRepository extends JpaRepository<CommentsInfo, String> {
<span class="hljs-function">List&lt;CommentsInfo&gt; <span class="hljs-title">findByOwnerId</span><span class="hljs-params">(String ownerId)</span></span>;

}

CommentsReplyRepository

代码语言:javascript
复制
package com.solo.coderiver.comments.repository;

import com.solo.coderiver.comments.dataobject.CommentsReply;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface CommentsReplyRepository extends JpaRepository<CommentsReply, Integer> {
<span class="hljs-function">List&lt;CommentsReply&gt; <span class="hljs-title">findByCommentId</span><span class="hljs-params">(String commentId)</span></span>;

}

Service 接口封装

为了代码更健壮,要把数据库的操作封装一下

CommentsInfoService

代码语言:javascript
复制
package com.solo.coderiver.comments.service;

import com.solo.coderiver.comments.dataobject.CommentsInfo;
import java.util.List;
public interface CommentsInfoService {
<span class="hljs-comment">/**
 * 保存评论
 * <span class="hljs-doctag">@param</span> info
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-function">CommentsInfo <span class="hljs-title">save</span><span class="hljs-params">(CommentsInfo info)</span></span>;

<span class="hljs-comment">/**
 * 根据被评论者的id查询评论列表
 * <span class="hljs-doctag">@param</span> ownerId
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-function">List&lt;CommentsInfo&gt; <span class="hljs-title">findByOwnerId</span><span class="hljs-params">(String ownerId)</span></span>;

}

CommentsReplyService

代码语言:javascript
复制
package com.solo.coderiver.comments.service;

import com.solo.coderiver.comments.dataobject.CommentsReply;
import java.util.List;
public interface CommentsReplyService {
<span class="hljs-comment">/**
 * 保存评论回复
 * <span class="hljs-doctag">@param</span> reply
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-function">CommentsReply <span class="hljs-title">save</span><span class="hljs-params">(CommentsReply reply)</span></span>;

<span class="hljs-comment">/**
 * 根据评论id查询回复
 * <span class="hljs-doctag">@param</span> commentId
 * <span class="hljs-doctag">@return</span>
 */</span>
<span class="hljs-function">List&lt;CommentsReply&gt; <span class="hljs-title">findByCommentId</span><span class="hljs-params">(String commentId)</span></span>;

}

接口的实现类

CommentsInfoServiceImpl

代码语言:javascript
复制
package com.solo.coderiver.comments.service.impl;

import com.solo.coderiver.comments.dataobject.CommentsInfo;
import com.solo.coderiver.comments.repository.CommentsInfoRepository;
import com.solo.coderiver.comments.service.CommentsInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CommentsInfoServiceImpl implements CommentsInfoService {
<span class="hljs-meta">@Autowired</span>
CommentsInfoRepository repository;

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> CommentsInfo <span class="hljs-title">save</span><span class="hljs-params">(CommentsInfo info)</span> </span>{
    <span class="hljs-keyword">return</span> repository.save(info);
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;CommentsInfo&gt; <span class="hljs-title">findByOwnerId</span><span class="hljs-params">(String ownerId)</span> </span>{
    <span class="hljs-keyword">return</span> repository.findByOwnerId(ownerId);
}

}

CommentsReplyServiceImpl

代码语言:javascript
复制
package com.solo.coderiver.comments.service.impl;

import com.solo.coderiver.comments.dataobject.CommentsReply;
import com.solo.coderiver.comments.repository.CommentsReplyRepository;
import com.solo.coderiver.comments.service.CommentsReplyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CommentsReplyServiceImpl implements CommentsReplyService {
<span class="hljs-meta">@Autowired</span>
CommentsReplyRepository repository;

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> CommentsReply <span class="hljs-title">save</span><span class="hljs-params">(CommentsReply reply)</span> </span>{
    <span class="hljs-keyword">return</span> repository.save(reply);
}

<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;CommentsReply&gt; <span class="hljs-title">findByCommentId</span><span class="hljs-params">(String commentId)</span> </span>{
    <span class="hljs-keyword">return</span> repository.findByCommentId(commentId);
}

}

控制层 Controller

Controller 层分发请求,并返回前端需要的数据

代码语言:javascript
复制
package com.solo.coderiver.comments.controller;

@RestController
@Api(description = "评论相关接口")
public class CommentsController {
<span class="hljs-meta">@Autowired</span>
CommentsInfoService infoService;

<span class="hljs-meta">@Autowired</span>
CommentsReplyService replyService;

<span class="hljs-meta">@PostMapping</span>(<span class="hljs-string">"/save"</span>)
<span class="hljs-meta">@ApiOperation</span>(<span class="hljs-string">"保存评论"</span>)
<span class="hljs-meta">@Transactional</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> ResultVO <span class="hljs-title">saveComments</span><span class="hljs-params">(@Valid CommentsInfoForm form, BindingResult bindingResult)</span> </span>{
    <span class="hljs-keyword">if</span> (bindingResult.hasErrors()) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> CommentsException(ResultEnums.PARAMS_ERROR.getCode(), bindingResult.getFieldError().getDefaultMessage());
    }
    <span class="hljs-comment">//将 CommentsInfoForm 里的数据拷贝到 CommentsInfo</span>
    CommentsInfo info = <span class="hljs-keyword">new</span> CommentsInfo();
    BeanUtils.copyProperties(form, info);
    <span class="hljs-comment">// 生成并设置评论的主键id</span>
    info.setId(KeyUtils.genUniqueKey());
    CommentsInfo result = infoService.save(info);
    <span class="hljs-keyword">if</span> (result == <span class="hljs-keyword">null</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> CommentsException(ResultEnums.SAVE_COMMENTS_FAIL);
    }
    <span class="hljs-keyword">return</span> ResultVOUtils.success();
}

<span class="hljs-meta">@GetMapping</span>(<span class="hljs-string">"/get/{ownerId}"</span>)
<span class="hljs-meta">@ApiOperation</span>(<span class="hljs-string">"根据 ownerId 查询评论"</span>)
<span class="hljs-meta">@ApiImplicitParam</span>(name = <span class="hljs-string">"ownerId"</span>, value = <span class="hljs-string">"被评论者id"</span>)
<span class="hljs-function"><span class="hljs-keyword">public</span> ResultVO <span class="hljs-title">getCommentsByOwnerId</span><span class="hljs-params">(@PathVariable(<span class="hljs-string">"ownerId"</span>)</span> String ownerId) </span>{
    List&lt;CommentsInfo&gt; infoList = infoService.findByOwnerId(ownerId);
    <span class="hljs-comment">//将 CommentsInfo 转换为 CommentsInfoDTO</span>
    List&lt;CommentsInfoDTO&gt; infoDTOS = infoList.stream().map(info -&gt; {
        CommentsInfoDTO dto = <span class="hljs-keyword">new</span> CommentsInfoDTO();
        BeanUtils.copyProperties(info, dto);
        <span class="hljs-keyword">return</span> dto;
    }).collect(Collectors.toList());
    <span class="hljs-keyword">return</span> ResultVOUtils.success(infoDTOS);
}

<span class="hljs-meta">@PostMapping</span>(<span class="hljs-string">"/save-reply"</span>)
<span class="hljs-meta">@ApiOperation</span>(<span class="hljs-string">"保存评论回复"</span>)
<span class="hljs-meta">@Transactional</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> ResultVO <span class="hljs-title">saveReply</span><span class="hljs-params">(@Valid CommentsReplyForm form, BindingResult bindingResult)</span> </span>{
    <span class="hljs-keyword">if</span> (bindingResult.hasErrors()) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> CommentsException(ResultEnums.PARAMS_ERROR.getCode(), bindingResult.getFieldError().getDefaultMessage());
    }
    CommentsReply reply = <span class="hljs-keyword">new</span> CommentsReply();
    BeanUtils.copyProperties(form, reply);
    CommentsReply result = replyService.save(reply);
    <span class="hljs-keyword">if</span> (result == <span class="hljs-keyword">null</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> CommentsException(ResultEnums.SAVE_COMMENTS_FAIL);
    }
    <span class="hljs-keyword">return</span> ResultVOUtils.success();
}

<span class="hljs-meta">@GetMapping</span>(<span class="hljs-string">"/get-reply/{commentId}"</span>)
<span class="hljs-meta">@ApiOperation</span>(<span class="hljs-string">"通过commentId获取评论回复"</span>)
<span class="hljs-function"><span class="hljs-keyword">public</span> ResultVO <span class="hljs-title">getReplyByCommentId</span><span class="hljs-params">(@PathVariable(<span class="hljs-string">"commentId"</span>)</span> String commentId) </span>{
    List&lt;CommentsReply&gt; replyList = replyService.findByCommentId(commentId);
    <span class="hljs-comment">//将 CommentsReply 转换为 CommentsReplyDTO</span>
    List&lt;CommentsReplyDTO&gt; replyDTOS = replyList.stream().map(reply -&gt; {
        CommentsReplyDTO dto = <span class="hljs-keyword">new</span> CommentsReplyDTO();
        BeanUtils.copyProperties(reply, dto);
        <span class="hljs-keyword">return</span> dto;
    }).collect(Collectors.toList());

    <span class="hljs-keyword">return</span> ResultVOUtils.success(replyDTOS);
}

}

代码中工具类和枚举类请到 github 上查看源码。

以上就是对评论模块的设计与功能实现,欢迎各位大佬提出代码优化建议,共同成长~

代码出自开源项目 CodeRiver,致力于打造全平台型全栈精品开源项目。

coderiver 中文名 河码,是一个为程序员和设计师提供项目协作的平台。无论你是前端、后端、移动端开发人员,或是设计师、产品经理,都可以在平台上发布项目,与志同道合的小伙伴一起协作完成项目。

coderiver河码 类似程序员客栈,但主要目的是方便各细分领域人才之间技术交流,共同成长,多人协作完成项目。暂不涉及金钱交易。

计划做成包含 pc端(Vue、React)、移动H5(Vue、React)、ReactNative混合开发、Android原生、微信小程序、java后端的全平台型全栈项目。

项目地址:https://github.com/cachecats/coderiver

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018/11/07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前端界面分析
  • 二、数据库设计
  • 三、功能实现
    • 数据实体对象
      • 数据库操作仓库 repository
        • Service 接口封装
          • 控制层 Controller
          相关产品与服务
          数据库
          云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档