2018-11-21 更新css
感謝大佬們在評論區提的優化建議,根據建議優化了數據表的設計和代碼結構。前端
新的文章:vue
評論模塊優化 - 數據表優化、添加緩存及用 Feign 與用戶服務通訊java
原文:git
評論模塊在不少系統中都有,CodeRiver河碼 做爲相似程序員客棧的溝通協做平臺天然也不會少。程序員
前端界面是參考了簡書的評論模塊,專門寫了一篇文章介紹實現步驟:
vue + element-ui + scss 仿簡書評論模塊
感興趣的能夠看看。github
項目地址:github.com/cachecats/c…spring
代碼在 根目錄/java/comments-service
sql
文章將分三部分介紹:數據庫
先看看前端界面長什麼樣,知道了前端須要什麼數據,就知道數據庫該怎麼設計了。
首先評論的主體能夠是人、項目、資源,因此要有一個 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;
}
複製代碼
操做數據庫暫時用的是 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);
}
複製代碼
爲了代碼更健壯,要把數據庫的操做封裝一下
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
層分發請求,並返回前端須要的數據
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後端的全平臺型全棧項目,歡迎關注。
您的鼓勵是我前行最大的動力,歡迎點贊,歡迎送小星星✨ ~