Golang实现多级评论以及通过递归删除评论、子孙评论功能(Gorm的自引用)

需求分析

使用golang及gorm制作一个用户的评论以及回复评论,同时删除评论后评论数通过递归查询所有的子孙评论发生改变

实现思路

在前端传来用户的数据,即我评论了哪个楼主,我上一级的评论是谁,
评论:先判断是否存在楼主,其次判断是否存在父级评论,之后添加评论,最后增加评论数,储存数据库
删除评论:获取删除评论的楼主树洞,判断是否是本人或者楼主删除评论,判断是否又该评论的父级评论,通过递归算法查询所有的子孙评论,删除子孙评论,评论数减少,最后存入数据库。(在递归中,要查找子评论的子评论,同时删除子评论,减少评论数)

实现代码

递归删除所有的评论的子孙评论

//递归删除所有评论的子评论
func DeleteCommentAndReplays(comment *model.TreeHoleComment) (err error) {
	comment, err = mysql.GetTreeHoleCommentByID(int(comment.ID))
	if err != nil {
		zap.L().Error("数据库查询回复失败", zap.Error(err))
		return err
	}
	for _, replay := range comment.CommentReplays {
		err := DeleteCommentAndReplays(replay)       //此处递归
		if err != nil {
			zap.L().Error("递归查询评论失败", zap.Error(err))
			return err
		}
		//删除评论
		err = mysql.DeleteTreeHoleComment(int(replay.ID))
		if err != nil {
			zap.L().Error("删除评论失败", zap.Error(err))
			return err
		}
		//查询顶部树洞
		treeHoleByID, err := mysql.GetTreeHoleByID(int(comment.TreeHoleID))
		if err != nil {
			zap.L().Error("通过id查询树洞错误", zap.Error(err))
			return err
		}
		treeHoleByID.CommentNum--
		//存数据库
		err = mysql.UpdateTreeHole(treeHoleByID)
		if err != nil {
			zap.L().Error("数据库更新树洞失败", zap.Error(err))
			return err
		}
	}
	return nil
}

结构体

//树洞结构体
type TreeHole struct {
	gorm.Model
	User        User
	UserID      uint              `json:"userID"`           //用户ID
	Content     string            `json:"content"`          //树洞内容
	LikeNum     uint              `json:"likeNum"`          //点赞数
	CommentNum  uint              `json:"comment_num"`      //评论数
	Replays     []TreeHoleComment `json:"replays"`          //回复的评论
	IsLike      bool              `gorm:"-" json:"is_like"` //喜欢或者不喜欢
}
//树洞评论结构体
type TreeHoleComment struct {
	gorm.Model
	CommentReplays    []*TreeHoleComment `gorm:"foreignkey:ParentID"`  //自引用
	User              User
	UserID            uint             `json:"userID" ` //用户ID
	Parent            *TreeHoleComment `gorm:"foreignkey:ParentID"`
	ParentID          uint             `json:"parent_id"`           //评论父级ID  0
	Content           string           `json:"content"`             //内容
	IsAnonymityReplay uint             `json:"is_anonymity_replay"` //是否匿名回复
	TreeHoleID        uint             `json:"tree_hole_id"`        //树洞ID
}

评论树洞的方法

//评论树洞
func TreeHoleComments(c *gin.Context) {
	userid := GetUserIDByToken(c)
	var comment *model.TreeHoleComment
	err := c.ShouldBindJSON(&comment)
	if err != nil {
		zap.L().Error("绑定JSON失败", zap.Error(err))
		ResponseError(c, CodeParamError)
		return
	}
	//判断是否是本人发布的评论
	if userid != comment.UserID {
		zap.L().Error("userid与发布评论的用户不一致", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "非本人操作")
		return
	}
	//判断是否有评论的树洞
	treeHoleIsExist, err := mysql.GetTreeHoleIsExist(int(comment.TreeHoleID))
	if err != nil {
		zap.L().Error("数据库查询树洞失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "查询树洞失败")
		return
	}
	if treeHoleIsExist == 0 {
		zap.L().Error("数据库中没有该树洞", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "数据库中没有该树洞")
		return
	}
	//判断是否有该评论的父级评论
	treeHoleCommentIsExist, err := mysql.GetTreeHoleCommentIsExist(int(comment.ParentID))
	if err != nil {
		zap.L().Error("数据库查询父级评论失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "数据库查询父级评论失败")
		return
	}
	if treeHoleCommentIsExist == 0 && (comment.ParentID) != 0 {
		zap.L().Error("数据库中没有该评论的父级评论", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "数据库中没有该评论的父级评论")
		return
	}
	//添加评论
	err = mysql.CreateTreeHoleComment(comment)
	if err != nil {
		zap.L().Error("创建树洞评论失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "创建树洞评论失败")
		return
	}
	//评论数增加
	treeHoleByID, err := mysql.GetTreeHoleByID(int(comment.TreeHoleID))
	if err != nil {
		zap.L().Error("通过id查询树洞错误", zap.Error(err))
		ResponseErrorWithMsg(c, CodeServerBusy, "没有该树洞")
		return
	}
	treeHoleByID.CommentNum++
	//存数据库
	err = mysql.UpdateTreeHole(treeHoleByID)
	if err != nil {
		zap.L().Error("数据库更新树洞失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "更新失败")
		return
	}
	ResponseSuccess(c, comment)
}

删除评论

//删除评论
func DeleteTreeHoleComment(c *gin.Context) {
	userid := GetUserIDByToken(c)
	commentID := c.Query("commentsID")
	commentIDNum, err := strconv.Atoi(commentID)
	if err != nil {
		zap.L().Error("转数字失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "转数字失败")
		return
	}
	comment, err := mysql.GetTreeHoleCommentByID(commentIDNum)
	if err != nil {
		zap.L().Error("数据库查询回复失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "数据库查询回复失败")
		return
	}
	//查询发表该树洞的楼主
	topTreeHole, err := mysql.GetTreeHoleByID(int(comment.TreeHoleID))
	//判断是否是本人发布的评论或者楼主删除
	if userid != comment.UserID && userid != topTreeHole.UserID {
		zap.L().Error("userid与发布评论的用户不一致", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "非本人或楼主操作")
		return
	}
	//判断是否有评论的树洞
	treeHoleIsExist, err := mysql.GetTreeHoleIsExist(int(comment.TreeHoleID))
	if err != nil {
		zap.L().Error("数据库查询树洞失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "查询树洞失败")
		return
	}
	if treeHoleIsExist == 0 {
		zap.L().Error("数据库中没有该树洞", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "数据库中没有该树洞")
		return
	}
	//判断是否有该评论的父级评论
	treeHoleCommentIsExist, err := mysql.GetTreeHoleCommentIsExist(int(comment.ParentID))
	if err != nil {
		zap.L().Error("数据库查询父级评论失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "数据库查询父级评论失败")
		return
	}
	if treeHoleCommentIsExist == 0 && (comment.ParentID) != 0 {
		zap.L().Error("数据库中没有该评论的父级评论", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "数据库中没有该评论的父级评论")
		return
	}

	err = DeleteCommentAndReplays(comment)
	if err != nil {
		zap.L().Error("递归删除所有评论的子评论失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "递归删除所有评论的子评论失败")
		return
	}

	//删除评论
	err = mysql.DeleteTreeHoleComment(int(comment.ID))
	if err != nil {
		zap.L().Error("删除评论失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "删除评论失败")
		return
	}
	//评论数减少
	//查询顶部树洞
	treeHoleByID, err := mysql.GetTreeHoleByID(int(comment.TreeHoleID))
	if err != nil {
		zap.L().Error("通过id查询树洞错误", zap.Error(err))
		ResponseErrorWithMsg(c, CodeServerBusy, "没有该树洞")
		return
	}
	treeHoleByID.CommentNum--
	//存数据库
	err = mysql.UpdateTreeHole(treeHoleByID)
	if err != nil {
		zap.L().Error("数据库更新树洞失败", zap.Error(err))
		ResponseErrorWithMsg(c, CodeParamError, "更新失败")
		return
	}
	ResponseSuccess(c, comment)
}

你可能感兴趣的:(golang,开发语言,后端)