package publishComments import ( "database/sql" "fmt" "net/http" "time" "toutoukan/init/databaseInit" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // PublishReq 定义发布评论的请求结构体 type PublishReq struct { ArticleID int64 `json:"articleId" binding:"required,min=1"` // 必须是有效的文章ID ParentID *int64 `json:"parent_id"` // 父评论ID,指针类型,nil表示NULL Content string `json:"content" binding:"required,min=1,max=500"` UserID string `json:"user_id" binding:"required,min=1,max=40"` // 用户ID } // CommentModel 对应 article_comments 表的 GORM 模型 type CommentModel struct { ID int64 `gorm:"column:id;primaryKey;autoIncrement"` ArticleID int64 `gorm:"column:articleId"` UserID string `gorm:"column:user_id"` ParentID sql.NullInt64 `gorm:"column:parent_id"` Content string `gorm:"column:content"` LikesCount int `gorm:"column:likes_count"` CreatedAt time.Time `gorm:"column:created_time"` UpdatedAt time.Time `gorm:"column:update_time"` } // UserPoint 用户积分记录表结构 type UserPoint struct { UserID string `gorm:"column:user_id"` PointsChange int `gorm:"column:points_change"` Source string `gorm:"column:source"` CreateTime time.Time `gorm:"column:create_time"` } // TableName 为 CommentModel 结构体指定数据库表名 func (CommentModel) TableName() string { return "article_comments" } // TableName 为 UserPoint 结构体指定数据库表名 func (UserPoint) TableName() string { return "user_points" } // PublishComment 处理发布新评论的请求 func PublishComment(c *gin.Context) { const ( COMMENTER_POINTS = 1 // 评论者获得的积分 AUTHOR_COMMENT_POINTS = 2 // 文章发布者获得的积分 ) var req PublishReq // 1. 解析并验证请求参数 if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "参数校验失败", "detail": err.Error(), }) return } // 2. 开启数据库事务 tx := databaseInit.UserDB.Begin() if tx.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "开启事务失败: " + tx.Error.Error()}) return } defer func() { if r := recover(); r != nil { tx.Rollback() } }() now := time.Now() // 3. 检查文章发布者是否存在,并获取发布者ID var articleInfo struct { AuthorID string `gorm:"column:publish_user_id"` } if err := tx.Table("article_list"). Where("articleId = ?", req.ArticleID). Select("publish_user_id"). First(&articleInfo).Error; err != nil { tx.Rollback() if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "文章不存在"}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": "查询文章失败: " + err.Error()}) } return } authorID := articleInfo.AuthorID // 3.1 检查评论者UID是否存在(避免外键约束失败) // 这里的检查只需要确保 req.UserID 存在即可,articleInfo.AuthorID 存在性已在上面检查 var userCheck struct{ UID string } if err := tx.Table("user_info").Select("uid").Where("uid = ?", req.UserID).First(&userCheck).Error; err != nil { tx.Rollback() if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "评论者UID不存在"}) } else { c.JSON(http.StatusInternalServerError, gin.H{"error": "查询用户失败: " + err.Error()}) } return } // 4. 插入评论主记录 newComment := CommentModel{ ArticleID: req.ArticleID, UserID: req.UserID, Content: req.Content, LikesCount: 0, CreatedAt: now, UpdatedAt: now, } // 处理 ParentID 的 NULL 值 if req.ParentID != nil && *req.ParentID > 0 { newComment.ParentID = sql.NullInt64{Int64: *req.ParentID, Valid: true} } else { newComment.ParentID = sql.NullInt64{Valid: false} // 设为 NULL } if err := tx.Create(&newComment).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{ "error": fmt.Sprintf("评论发布失败: %s", err.Error()), }) return } // --- 5. 积分处理 (评论者 & 文章作者) --- // 5.1 记录评论者积分变动 (user_points) commenterPoint := UserPoint{ UserID: req.UserID, PointsChange: COMMENTER_POINTS, Source: "publish_comment", CreateTime: now, } if err := tx.Table("user_points").Create(&commenterPoint).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "记录评论者积分变动失败: " + err.Error()}) return } // 5.2 更新评论者总积分 if err := tx.Table("user_info"). Where("uid = ?", req.UserID). Update("total_points", gorm.Expr("total_points + ?", COMMENTER_POINTS)).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "更新评论者总积分失败: " + err.Error()}) return } // 5.3 记录并更新文章发布者积分(如果不是自己评论自己) if authorID != req.UserID { // 记录作者积分变动 authorPoint := UserPoint{ UserID: authorID, PointsChange: AUTHOR_COMMENT_POINTS, Source: "comment_received", CreateTime: now, } if err := tx.Table("user_points").Create(&authorPoint).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "记录作者积分变动失败: " + err.Error()}) return } // 更新作者总积分 if err := tx.Table("user_info"). Where("uid = ?", authorID). Update("total_points", gorm.Expr("total_points + ?", AUTHOR_COMMENT_POINTS)).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "更新作者总积分失败: " + err.Error()}) return } } // 6. 提交事务 if err := tx.Commit().Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "提交评论失败: " + err.Error()}) return } // 7. 返回成功响应 c.JSON(http.StatusOK, gin.H{ "success": true, "message": fmt.Sprintf("评论发布成功,您获得了 %d 积分", COMMENTER_POINTS), "data": gin.H{ "commentId": newComment.ID, "articleId": newComment.ArticleID, "author_points_awarded": func() int { if authorID != req.UserID { return AUTHOR_COMMENT_POINTS } return 0 }(), }, }) }