隨著社群媒體和內容平台的興起,多層次評論系統成為了各類平台進行使用者互動和社群互動的重要方式。在前端實現多層評論系統相對來說比較簡單,但是後端實作起來卻相對複雜。本文將介紹如何使用golang實現多層評論。
多層評論其實就是樹狀結構的展示,每個評論可以作為節點。可以使用樹狀結構的基本資料結構—樹和二元樹來實現多層評論系統。在本文中,我們選擇使用樹狀結構的二元樹來實現。
二元樹的節點由左右兩個子節點組成,左節點為目前節點的第一個子節點,右節點為目前節點的第二個子節點。因此,每次增加評論的時候,我們只需要關注當前評論的父節點以及它的左右子節點。
為了實現多層評論系統,需要在資料庫中儲存每個評論的父子關係。一般情況下,我們可以使用兩種方式來儲存樹狀結構:
#在本文中,我們選擇使用第二種方式,即在單表中使用自引用關係。
評論表(comment)的結構如下:
CREATE TABLE `comment` ( `id` int(11) NOT NULL AUTO_INCREMENT, `parent_id` int(11) DEFAULT NULL COMMENT '父级评论id', `content` varchar(255) DEFAULT NULL COMMENT '评论内容', `created_at` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`), KEY `parent_id` (`parent_id`), CONSTRAINT `comment_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `comment` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='评论表'
在上述表格結構中,parent_id
表示目前評論的父節點id,如果目前評論是一級評論,則parent_id
為null。 id
為目前評論的id,content
為評論內容,created_at
為建立時間。
首先,需要使用golang中的database/sql和mysql驅動來連結資料庫:
dsn := "root:123456@tcp(127.0.0.1:3306)/test" db, err := sql.Open("mysql", dsn) if err != nil { fmt.Printf("mysql connect error %s", err.Error()) return }
插入評論時,需要判斷當前評論是作為父節點還是子節點來處理。
如果目前評論是作為父節點,直接插入資料庫中,如果目前評論是子節點,則需要更新父節點的右節點。
// 添加评论 func AddComment(comment Comment) error { if comment.ParentID == 0 { _, err := db.Exec("INSERT INTO comment(parent_id, content, created_at) VALUES(?, ?, ?)", nil, comment.Content, comment.CreatedAt) if err != nil { return err } } else { var rightNode *int err := db.QueryRow("SELECT right_node FROM comment WHERE id = ?", comment.ParentID).Scan(&rightNode) if err != nil { return err } tx, err := db.Begin() if err != nil { return err } // 更新右节点 _, err = tx.Exec("UPDATE comment SET right_node = right_node + 2 WHERE right_node > ?", rightNode) if err != nil { tx.Rollback() return err } _, err = tx.Exec("UPDATE comment SET left_node = left_node + 2 WHERE left_node > ?", rightNode) if err != nil { tx.Rollback() return err } _, err = tx.Exec("INSERT INTO comment(parent_id, left_node, right_node, content, created_at) VALUES(?, ?, ?, ?, ?)", comment.ParentID, rightNode, rightNode+1, comment.Content, comment.CreatedAt) if err != nil { tx.Rollback() return err } tx.Commit() } return nil }
查詢註解時,需要依照節點的左右順序進行排序,以取得到樹狀結構的註解清單。在查詢評論的時候,由於需要使用左右節點進行排序,所以需要將左右節點列加入查詢條件。此外,還需要使用一個level字段,表示當前節點是第幾個節點,方便前端展示。
type Comment struct { ID int `json:"id"` ParentID int `json:"parent_id"` Content string `json:"content"` LeftNode int `json:"left_node"` RightNode int `json:"right_node"` Level int `json:"level"` CreatedAt string `json:"created_at"` } // 获取评论列表 func GetComments() ([]Comment, error) { rows, err := db.Query("SELECT id, parent_id, content, created_at, left_node, right_node, (COUNT (parent.id) -1) AS level FROM comment AS node, comment AS parent WHERE node.left_node BETWEEN parent.left_node AND parent.right_node GROUP BY node.id ORDER BY left_node") if err != nil { return nil, err } defer rows.Close() var comments []Comment for rows.Next() { var comment Comment err := rows.Scan(&comment.ID, &comment.ParentID, &comment.Content, &comment.CreatedAt, &comment.LeftNode, &comment.RightNode, &comment.Level) if err != nil { return nil, err } comments = append(comments, comment) } return comments, nil }
刪除註解的時候,需要判斷目前註解是父節點還是子節點,如果是父節點,需要刪除整個子樹。
// 删除评论 func DeleteComment(id int) error { tx, err := db.Begin() if err != nil { return err } var leftNode int var rightNode int err = tx.QueryRow("SELECT left_node, right_node FROM comment WHERE id = ?", id).Scan(&leftNode, &rightNode) if err != nil { tx.Rollback() return err } if leftNode == 1 && rightNode > 1 { // 删除子树 _, err = tx.Exec("DELETE FROM comment WHERE left_node >= ? AND right_node <= ?;", leftNode, rightNode) if err != nil { tx.Rollback() return err } err = tx.Commit() if err != nil { tx.Rollback() return err } } else { // 删除单个节点 _, err = tx.Exec("DELETE FROM comment WHERE id = ?", id) if err != nil { tx.Rollback() return err } err = tx.Commit() if err != nil { tx.Rollback() return err } } return nil }
透過上述程式碼實現,我們可以快速地建構出一個功能完整的多層評論系統。當然,這只是一個基礎的實現方式,在實際場景中,還需要根據具體的需求做出相應的最佳化和擴展。
以上是golang實現多層評論的詳細內容。更多資訊請關注PHP中文網其他相關文章!