Files
toutoukan/controllers/article/createarticle.go
2025-09-27 18:25:46 +08:00

167 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package article
import (
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"net/http"
"time"
"toutoukan/init/databaseInit"
)
// 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=5"`
}
// ArticleList 数据库文章记录结构体
// UserPoint 用户积分记录表结构
// UserInfo 用户信息表结构
type UserInfo struct {
UID string `gorm:"column:uid;primaryKey"`
TotalPoints int `gorm:"column:total_points"`
}
// 自定义表名GORM会根据这些结构体进行操作
func (ArticleList) TableName() string {
return "article_list"
}
func (UserPoint) TableName() string {
return "user_points"
}
func (UserInfo) TableName() string {
return "user_info"
}
// CreateArticle 创建文章(包含选项)
func CreateArticle(c *gin.Context) {
const (
POST_ARTICLE_POINTS = 2 // 发表文章获得的积分
)
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. 检查文章发布者是否存在
var userCheck UserInfo
if err := tx.Table("user_info").
Where("uid = ?", req.PublishUserID).
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
}
// 2. 创建文章主记录
newArticle := ArticleList{
PublishUserID: req.PublishUserID,
Title: req.Title,
VoteType: req.VoteType,
EndTime: req.EndTime,
IsEnded: false,
TotalVotersNum: 0,
CreateTime: time.Now(),
}
if err := tx.Create(&newArticle).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建文章失败: " + err.Error()})
return
}
if newArticle.ArticleID <= 0 {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "无法获取文章ID创建失败"})
return
}
// 3. 批量创建选项
var options []map[string]interface{}
for _, opt := range req.Options {
options = append(options, map[string]interface{}{
"vote_article_id": newArticle.ArticleID,
"option_content": opt.Content,
"option_votes_num": 0,
"sort_order": opt.SortOrder,
})
}
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
}
// 4. 记录文章发布者积分变动
userPoint := UserPoint{
UserID: req.PublishUserID,
PointsChange: POST_ARTICLE_POINTS,
Source: "publish_article",
CreateTime: time.Now(),
}
if err := tx.Table("user_points").Create(&userPoint).Error; err != nil {
tx.Rollback()
c.JSON(http.StatusInternalServerError, gin.H{"error": "记录积分变动失败: " + err.Error()})
return
}
// 5. 更新 user_info 表中的 total_points 字段
// 使用 UpdateColumn 来确保原子性,并避免 GORM 的其他回调
if err := tx.Table("user_info").
Where("uid = ?", req.PublishUserID).
UpdateColumn("total_points", gorm.Expr("total_points + ?", POST_ARTICLE_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
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"message": "文章及选项创建成功,您获得了" + fmt.Sprintf("%d", POST_ARTICLE_POINTS) + "积分",
"data": gin.H{
"article_id": newArticle.ArticleID,
"option_count": len(req.Options),
},
})
}