添加用户评论功能

This commit is contained in:
JACKYMYPERSON
2025-09-25 18:17:41 +08:00
parent 0006b61809
commit 5412122b48
4 changed files with 340 additions and 14 deletions

View File

@@ -0,0 +1,211 @@
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
}(),
},
})
}