package article import ( "net/http" "time" "toutoukan/init/databaseInit" "github.com/gin-gonic/gin" ) // OptionItem 选项子结构 type OptionItem struct { Content string `json:"content" binding:"required,min=1,max=200"` // 选项内容 SortOrder int `json:"sort_order" binding:"required,min=0"` // 排序值 } // CreateArticleRequest 创建文章的请求参数结构 type CreateArticleRequest struct { PublishUserID string `json:"publish_user_id" binding:"required,min=1,max=40"` Title string `json:"title" binding:"required,min=1,max=255"` VoteType string `json:"vote_type" binding:"required,min=1,max=60"` EndTime time.Time `json:"end_time" binding:"required"` Options []OptionItem `json:"options" binding:"required,min=1,max=3"` } // 数据库文章记录结构体(与表结构完全对应) type ArticleList struct { ArticleID int64 `gorm:"column:articleId;primaryKey;autoIncrement"` // 明确主键和自增 PublishUserID string `gorm:"column:publish_user_id"` Title string `gorm:"column:title"` VoteType string `gorm:"column:vote_type"` EndTime time.Time `gorm:"column:end_time"` IsEnded bool `gorm:"column:is_ended"` TotalVotersNum int `gorm:"column:total_voters_num"` CreateTime time.Time `gorm:"column:create_time"` } // 自定义表名(如果结构体名与表名不一致) func (ArticleList) TableName() string { return "article_list" } // CreateArticle 创建文章(包含选项) func CreateArticle(c *gin.Context) { var req CreateArticleRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "参数解析失败", "detail": err.Error(), }) return } 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() } }() // 1. 创建文章主记录 newArticle := ArticleList{ PublishUserID: req.PublishUserID, Title: req.Title, VoteType: req.VoteType, EndTime: req.EndTime, IsEnded: false, TotalVotersNum: 0, CreateTime: time.Now(), } // 插入文章并获取ID(使用GORM的Create方法,自动填充自增ID) if err := tx.Create(&newArticle).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "创建文章失败: " + err.Error()}) return } // 验证文章ID是否有效(必须大于0) if newArticle.ArticleID <= 0 { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "无法获取文章ID,创建失败"}) return } // 2. 批量创建选项(确保vote_article_id正确关联) var options []map[string]interface{} for _, opt := range req.Options { options = append(options, map[string]interface{}{ "vote_article_id": newArticle.ArticleID, // 使用刚创建的文章ID "option_content": opt.Content, "option_votes_num": 0, "sort_order": opt.SortOrder, }) } // 插入选项前先验证文章ID是否存在(额外保险) var count int64 if err := tx.Model(&ArticleList{}).Where("articleId = ?", newArticle.ArticleID).Count(&count).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "验证文章ID失败: " + err.Error()}) return } if count == 0 { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "文章ID不存在,外键约束失败"}) return } // 批量插入选项 if err := tx.Table("article_options").CreateInBatches(options, len(options)).Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "创建选项失败: " + err.Error()}) return } // 提交事务 if err := tx.Commit().Error; err != nil { tx.Rollback() c.JSON(http.StatusInternalServerError, gin.H{"error": "提交数据失败: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "success": true, "message": "文章及选项创建成功", "data": gin.H{ "article_id": newArticle.ArticleID, "option_count": len(req.Options), }, }) }