美文网首页
评论模块 - 后端数据库设计及功能实现

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

作者: solocoder | 来源:发表于2018-11-17 11:30 被阅读0次

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

    前端界面是参考了简书的评论模块,专门写了一篇文章介绍实现步骤:
    vue + element-ui + scss 仿简书评论模块
    感兴趣的可以看看。

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

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

    文章将分三部分介绍:

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

    一、前端界面分析

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

    image

    首先评论的主体可以是人、项目、资源,所以要有一个 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后端的全平台型全栈项目,欢迎关注。

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


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

    相关文章

      网友评论

          本文标题:评论模块 - 后端数据库设计及功能实现

          本文链接:https://www.haomeiwen.com/subject/hopbfqtx.html