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

栏目: 数据库 · 发布时间: 6年前

内容简介:评论模块在很多系统中都有,前端界面是参考了简书的评论模块,专门写了一篇文章介绍实现步骤:

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

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

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

感兴趣的可以看看。

项目地址: github.com/cachecats/c…

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

文章将分三部分介绍:

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

一、前端界面分析

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

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

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

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

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

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

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

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

二、数据库设计

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

评论主表(父评论表)

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='评论主表';
复制代码

评论回复表(子评论表)

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

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 {

    //评论主键id
    @Id
    private String id;

    //评论类型。1用户评论,2项目评论,3资源评论
    private Integer type;

    //被评论者的id
    private String ownerId;

    //评论者id
    private String fromId;

    //评论者名字
    private String fromName;

    //评论者头像
    private String fromAvatar;

    //获得点赞的数量
    private Integer likeNum;

    //评论内容
    private String content;

    //创建时间
    private Date createTime;

    //更新时间
    private Date updateTime;

}
复制代码

数据实体对象 CommentsReply

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 {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    //评论主表id
    private String commentId;

    //评论者id
    private String fromId;

    //评论者名字
    private String fromName;

    //评论者头像
    private String fromAvatar;

    //被评论者id
    private String toId;

    //被评论者名字
    private String toName;

    //被评论者头像
    private String toAvatar;

    //评论内容
    private String content;

    //创建时间
    private Date createTime;

    //更新时间
    private Date updateTime;

}
复制代码

数据库操作仓库 repository

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

CommentsInfoRepository
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> {

    List<CommentsInfo> findByOwnerId(String ownerId);
}
复制代码
CommentsReplyRepository
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> {

    List<CommentsReply> findByCommentId(String commentId);
}
复制代码

Service 接口封装

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

CommentsInfoService
package com.solo.coderiver.comments.service;

import com.solo.coderiver.comments.dataobject.CommentsInfo;
import java.util.List;

public interface CommentsInfoService {

    /**
     * 保存评论
     * @param info
     * @return
     */
    CommentsInfo save(CommentsInfo info);

    /**
     * 根据被评论者的id查询评论列表
     * @param ownerId
     * @return
     */
    List<CommentsInfo> findByOwnerId(String ownerId);
}
复制代码
CommentsReplyService
package com.solo.coderiver.comments.service;

import com.solo.coderiver.comments.dataobject.CommentsReply;
import java.util.List;

public interface CommentsReplyService {

    /**
     * 保存评论回复
     * @param reply
     * @return
     */
    CommentsReply save(CommentsReply reply);

    /**
     * 根据评论id查询回复
     * @param commentId
     * @return
     */
    List<CommentsReply> findByCommentId(String commentId);
}
复制代码

接口的实现类

CommentsInfoServiceImpl
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 {

    @Autowired
    CommentsInfoRepository repository;

    @Override
    public CommentsInfo save(CommentsInfo info) {
        return repository.save(info);
    }

    @Override
    public List<CommentsInfo> findByOwnerId(String ownerId) {
        return repository.findByOwnerId(ownerId);
    }
}
复制代码
CommentsReplyServiceImpl
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 {

    @Autowired
    CommentsReplyRepository repository;

    @Override
    public CommentsReply save(CommentsReply reply) {
        return repository.save(reply);
    }

    @Override
    public List<CommentsReply> findByCommentId(String commentId) {
        return repository.findByCommentId(commentId);
    }
}
复制代码

控制层 Controller

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

package com.solo.coderiver.comments.controller;

@RestController
@Api(description = "评论相关接口")
public class CommentsController {

    @Autowired
    CommentsInfoService infoService;

    @Autowired
    CommentsReplyService replyService;

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

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

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

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

        return ResultVOUtils.success(replyDTOS);
    }
}
复制代码

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

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

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

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

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

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

项目地址: github.com/cachecats/c…

您的鼓励是我前行最大的动力,欢迎点赞,欢迎送小星星:sparkles: ~


以上所述就是小编给大家介绍的《评论模块 - 后端数据库设计及功能实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Approximation Algorithms

Approximation Algorithms

Vijay V. Vazirani / Springer / 2001-07-02 / USD 54.95

'This book covers the dominant theoretical approaches to the approximate solution of hard combinatorial optimization and enumeration problems. It contains elegant combinatorial theory, useful and inte......一起来看看 《Approximation Algorithms》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码