一、概述
书籍系统框架如图:
文件内容持续更新在GitHub上,可自行查看。
本篇主要是介绍:评论和阅读量中的评论
二、评论
思路
1.对文章的评论
2.对评论的回复
3.让数据呈现树状结构
数据库设计
parent_id指向父评论id,如果是文章的评论默认为0.
代码
1.因为评论是树状结构,所以在微服务部分做了树数据的嵌套。
action.proto
syntax = "proto3";
package action;
option go_package = "action";
message Request {
string ping = 1;
}
message Response {
bool ok = 1;
string message = 2;
}
message CommentReq{
int64 Id = 1;
int64 ParentId = 2;
int64 BookContentId = 3;
string Comment = 4;
int64 CommentByUserId = 5;
string CommentByNickname = 6;
int64 CommentToUserId = 7;
string CommentToNickname = 8;
}
message CommentResp{
int64 Id = 1;
int64 ParentId = 2;
int64 BookContentId = 3;
string Comment = 4;
int64 CommentByUserId = 5;
string CommentByNickname = 6;
int64 CommentToUserId = 7;
string CommentToNickname = 8;
}
message CommentsNodeResp{
CommentResp Comments = 1;
repeated CommentsNodeResp CommentsNode = 2;
}
message CommentsTreeResp{
repeated CommentsNodeResp CommentsTree = 1;
}
service Action {
//Comments
rpc GetCommentsByBookContentId(CommentReq) returns(CommentsTreeResp);
rpc CreateComment(CommentReq) returns(Response);
rpc UpdateComment(CommentReq) returns(Response);
rpc DeleteComment(CommentReq) returns(Response);
}
复制代码
2.在逻辑代码中把数据库中的数据组合成树数据
getcommentsbybookcontentidlogic.go
// Comments
func (l *GetCommentsByBookContentIdLogic) GetCommentsByBookContentId(in *action.CommentReq) (*action.CommentsTreeResp, error) {
comments, err := l.svcCtx.CommentModel.FindCommentsByBookContentId(in.BookContentId)
if err != nil {
return nil, err
}
fmt.Println("comments", comments)
f := func(cs []*model.Comment) *action.CommentsTreeResp {
//树结构
var res = action.CommentsTreeResp{
CommentsTree: make([]*action.CommentsNodeResp, 0),
}
for i := 0; i < len(cs); i++ {
if cs[i].ParentId == 0 {
res.CommentsTree = append(res.CommentsTree, &action.CommentsNodeResp{
Comments: &action.CommentResp{
Id: cs[i].Id,
ParentId: cs[i].ParentId,
BookContentId: cs[i].BookContentId,
Comment: cs[i].Comment,
CommentByUserId: cs[i].CommentByUserId,
CommentByNickname: cs[i].CommentByNickname,
CommentToUserId: cs[i].CommentToUserId,
CommentToNickname: cs[i].CommentToNickname,
},
})
} else {
node := FindCommentNodeByParentId(res.CommentsTree, cs[i].ParentId)
if node != nil {
node.CommentsNode = append(node.CommentsNode, &action.CommentsNodeResp{
Comments: &action.CommentResp{
Id: cs[i].Id,
ParentId: cs[i].ParentId,
BookContentId: cs[i].BookContentId,
Comment: cs[i].Comment,
CommentByUserId: cs[i].CommentByUserId,
CommentByNickname: cs[i].CommentByNickname,
CommentToUserId: cs[i].CommentToUserId,
CommentToNickname: cs[i].CommentToNickname,
},
})
}
}
}
return &res
}
return f(comments), nil
}
//找到id对应的节点
func FindCommentNodeByParentId(res []*action.CommentsNodeResp, id int64) *action.CommentsNodeResp {
for i := 0; i < len(res); i++ {
if id == res[i].Comments.Id {
return res[i]
} else {
if r := FindCommentNodeByParentId(res[i].CommentsNode, id); r != nil {
return r
}
}
}
return nil
}
复制代码
3.在做前端的时候,发现评论一般都是只有父子两级,所以在WebApi中把所有的对评论的回复又组合成子级。--!有苦说不出。
get_comment_handler.go
package action
import (
"WebApi/Pb/action"
"WebApi/Svc"
"context"
"github.com/gin-gonic/gin"
"net/http"
"strconv"
)
func GetCommentsByBookContentIdHandler(c *gin.Context) {
bookContentId, err := strconv.ParseInt(c.Query("bookContentId"), 10, 64)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ctx := context.Background()
//树状结构(评论)
res, err := Svc.SvcContext.Grpc.ActionGrpc.GetCommentsByBookContentId(ctx, &action.CommentReq{
BookContentId: bookContentId,
})
//树状结构 平铺为 只有父子节点结构(评论) 方便前端使用
tn := func(t *action.CommentsTreeResp) action.CommentsTreeResp {
var tree = action.CommentsTreeResp{}
for i := 0; i < len(t.CommentsTree); i++ {
//组合父节点
tree.CommentsTree = append(tree.CommentsTree, &action.CommentsNodeResp{
Comments: t.CommentsTree[i].Comments,
})
//组合父节点下所有的节点
combChildComment(t.CommentsTree[i].CommentsNode, &tree.CommentsTree[i].CommentsNode)
}
return tree
}
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
c.JSON(http.StatusOK, tn(res))
}
}
func combChildComment(src []*action.CommentsNodeResp, dest *[]*action.CommentsNodeResp) {
for i := 0; i < len(src); i++ {
*dest = append(*dest, &action.CommentsNodeResp{
Comments: src[i].Comments,
})
if src[i].CommentsNode != nil {
combChildComment(src[i].CommentsNode, dest)
}
}
}
复制代码
4.POSTman的结果展示
{
"CommentsTree": [
{
"Comments": {
"Id": 1,
"BookContentId": 2,
"Comment": "写得好。",
"CommentByUserId": 1,
"CommentByNickname": "书店老板",
"CommentToUserId": 1,
"CommentToNickname": "书店老板"
},
"CommentsNode": [
{
"Comments": {
"Id": 3,
"ParentId": 1,
"BookContentId": 2,
"Comment": "马屁精。",
"CommentByUserId": 2,
"CommentByNickname": "张三",
"CommentToUserId": 1,
"CommentToNickname": "书店老板"
}
},
{
"Comments": {
"Id": 4,
"ParentId": 3,
"BookContentId": 2,
"Comment": "来来来,笔给你,你来写",
"CommentByUserId": 3,
"CommentByNickname": "李四",
"CommentToUserId": 2,
"CommentToNickname": "张三"
}
}
]
},
{
"Comments": {
"Id": 5,
"BookContentId": 2,
"Comment": "写得好1。",
"CommentByUserId": 1,
"CommentByNickname": "书店老板",
"CommentToUserId": 1,
"CommentToNickname": "书店老板"
},
"CommentsNode": [
{
"Comments": {
"Id": 8,
"ParentId": 5,
"BookContentId": 2,
"Comment": "不好",
"CommentByUserId": 4,
"CommentByNickname": "王五",
"CommentToUserId": 1,
"CommentToNickname": "书店老板"
}
},
{
"Comments": {
"Id": 9,
"ParentId": 8,
"BookContentId": 2,
"Comment": "不好才怪",
"CommentByUserId": 4,
"CommentByNickname": "王五",
"CommentToUserId": 4,
"CommentToNickname": "王五"
}
}
]
},
{
"Comments": {
"Id": 10,
"BookContentId": 2,
"Comment": "我是第一",
"CommentByUserId": 4,
"CommentByNickname": "王五",
"CommentToUserId": 4,
"CommentToNickname": "王五"
}
},
{
"Comments": {
"Id": 11,
"BookContentId": 2,
"Comment": "第二",
"CommentByUserId": 4,
"CommentByNickname": "王五",
"CommentToUserId": 4,
"CommentToNickname": "王五"
}
},
{
"Comments": {
"Id": 12,
"BookContentId": 2,
"Comment": "文章YYDS。",
"CommentByUserId": 1,
"CommentByNickname": "书店老板",
"CommentToUserId": 1,
"CommentToNickname": "书店老板"
}
}
]
}
复制代码
5.前端成果展示
三、Tips
最近工作中忙了起来,更新可能会比之前慢一些,请多多包涵。