評論模組 - 後端資料庫設計及功能實現
評論模組在很多系統中都有, ofollow,noindex">CodeRiver河碼 作為類似程式員客棧的溝通協作平臺自然也不會少。
前端介面是參考了簡書的評論模組,專門寫了一篇文章介紹實現步驟:
vue + element-ui + scss 仿簡書評論模組
感興趣的可以看看。
專案地址: github.com/cachecats/c…
程式碼在 根目錄/java/comments-service
文章將分三部分介紹:
一、前端介面分析
先看看前端介面長什麼樣,知道了前端需要什麼資料,就知道資料庫該怎麼設計了。

首先評論的主體可以是人、專案、資源,所以要有一個 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: ~