275 lines
6.6 KiB
Go
275 lines
6.6 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"goblog/database"
|
|
"goblog/models"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// 文章列表请求参数
|
|
type PostListQuery struct {
|
|
Page int `form:"page,default=1"`
|
|
PageSize int `form:"page_size,default=10"`
|
|
CategoryID uint `form:"category_id"`
|
|
TagID uint `form:"tag_id"`
|
|
Status string `form:"status"`
|
|
Keyword string `form:"keyword"`
|
|
}
|
|
|
|
// 创建文章请求
|
|
type CreatePostRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
Content string `json:"content" binding:"required"`
|
|
Summary string `json:"summary"`
|
|
Cover string `json:"cover"`
|
|
CategoryID uint `json:"category_id" binding:"required"`
|
|
Tags []string `json:"tags"`
|
|
Status string `json:"status"`
|
|
IsTop bool `json:"is_top"`
|
|
}
|
|
|
|
// 更新文章请求
|
|
type UpdatePostRequest struct {
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
Summary string `json:"summary"`
|
|
Cover string `json:"cover"`
|
|
CategoryID uint `json:"category_id"`
|
|
Tags []string `json:"tags"`
|
|
Status string `json:"status"`
|
|
IsTop bool `json:"is_top"`
|
|
}
|
|
|
|
// 生成 slug
|
|
func generateSlug(title string) string {
|
|
slug := strings.ToLower(title)
|
|
slug = strings.ReplaceAll(slug, " ", "-")
|
|
slug = strings.ReplaceAll(slug, "_", "-")
|
|
// 简化处理,实际项目中可能需要更完善的 slug 生成
|
|
return slug
|
|
}
|
|
|
|
// 获取文章列表
|
|
func GetPosts(c *gin.Context) {
|
|
var query PostListQuery
|
|
if err := c.ShouldBindQuery(&query); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
db := database.DB.Model(&models.Post{}).Preload("Category").Preload("Tags").Preload("Author")
|
|
|
|
// 前端只显示已发布的文章
|
|
if !isAdmin(c) {
|
|
db = db.Where("status = ?", "published")
|
|
} else if query.Status != "" {
|
|
db = db.Where("status = ?", query.Status)
|
|
}
|
|
|
|
if query.CategoryID > 0 {
|
|
db = db.Where("category_id = ?", query.CategoryID)
|
|
}
|
|
|
|
if query.TagID > 0 {
|
|
db = db.Joins("JOIN post_tags ON post_tags.post_id = posts.id").
|
|
Where("post_tags.tag_id = ?", query.TagID)
|
|
}
|
|
|
|
if query.Keyword != "" {
|
|
db = db.Where("title LIKE ? OR content LIKE ?", "%"+query.Keyword+"%", "%"+query.Keyword+"%")
|
|
}
|
|
|
|
var total int64
|
|
db.Count(&total)
|
|
|
|
var posts []models.Post
|
|
offset := (query.Page - 1) * query.PageSize
|
|
db.Order("is_top DESC, published_at DESC, created_at DESC").
|
|
Offset(offset).Limit(query.PageSize).Find(&posts)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"data": posts,
|
|
"total": total,
|
|
"page": query.Page,
|
|
"size": query.PageSize,
|
|
})
|
|
}
|
|
|
|
// 获取单篇文章
|
|
func GetPost(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
var post models.Post
|
|
query := database.DB.Preload("Category").Preload("Tags").Preload("Author").Preload("Comments", func(db *gorm.DB) *gorm.DB {
|
|
return db.Where("status = ? AND parent_id IS NULL", "approved").Preload("Children", func(db *gorm.DB) *gorm.DB {
|
|
return db.Where("status = ?", "approved")
|
|
})
|
|
})
|
|
|
|
// 尝试按 ID 或 slug 查找
|
|
if _, err := strconv.Atoi(id); err == nil {
|
|
query = query.Where("id = ?", id)
|
|
} else {
|
|
query = query.Where("slug = ?", id)
|
|
}
|
|
|
|
// 非管理员只能查看已发布文章
|
|
if !isAdmin(c) {
|
|
query = query.Where("status = ?", "published")
|
|
}
|
|
|
|
if err := query.First(&post).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "文章不存在"})
|
|
return
|
|
}
|
|
|
|
// 增加浏览量
|
|
database.DB.Model(&post).UpdateColumn("views", gorm.Expr("views + 1"))
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": post})
|
|
}
|
|
|
|
// 创建文章
|
|
func CreatePost(c *gin.Context) {
|
|
var req CreatePostRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
userID, _ := c.Get("userID")
|
|
|
|
post := models.Post{
|
|
Title: req.Title,
|
|
Slug: generateSlug(req.Title),
|
|
Content: req.Content,
|
|
Summary: req.Summary,
|
|
Cover: req.Cover,
|
|
AuthorID: userID.(uint),
|
|
CategoryID: req.CategoryID,
|
|
Status: req.Status,
|
|
IsTop: req.IsTop,
|
|
}
|
|
|
|
if req.Status == "published" {
|
|
now := time.Now()
|
|
post.PublishedAt = &now
|
|
}
|
|
|
|
// 处理标签
|
|
if len(req.Tags) > 0 {
|
|
var tags []models.Tag
|
|
for _, tagName := range req.Tags {
|
|
var tag models.Tag
|
|
database.DB.FirstOrCreate(&tag, models.Tag{
|
|
Name: tagName,
|
|
Slug: generateSlug(tagName),
|
|
})
|
|
tags = append(tags, tag)
|
|
}
|
|
post.Tags = tags
|
|
}
|
|
|
|
if err := database.DB.Create(&post).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "创建文章失败"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{"data": post})
|
|
}
|
|
|
|
// 更新文章
|
|
func UpdatePost(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
var post models.Post
|
|
if err := database.DB.First(&post, id).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "文章不存在"})
|
|
return
|
|
}
|
|
|
|
var req UpdatePostRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
updates := map[string]interface{}{}
|
|
if req.Title != "" {
|
|
updates["title"] = req.Title
|
|
updates["slug"] = generateSlug(req.Title)
|
|
}
|
|
if req.Content != "" {
|
|
updates["content"] = req.Content
|
|
}
|
|
if req.Summary != "" {
|
|
updates["summary"] = req.Summary
|
|
}
|
|
if req.Cover != "" {
|
|
updates["cover"] = req.Cover
|
|
}
|
|
if req.CategoryID > 0 {
|
|
updates["category_id"] = req.CategoryID
|
|
}
|
|
if req.Status != "" {
|
|
updates["status"] = req.Status
|
|
if req.Status == "published" && post.Status != "published" {
|
|
now := time.Now()
|
|
updates["published_at"] = &now
|
|
}
|
|
}
|
|
updates["is_top"] = req.IsTop
|
|
|
|
// 处理标签
|
|
if len(req.Tags) > 0 {
|
|
var tags []models.Tag
|
|
for _, tagName := range req.Tags {
|
|
var tag models.Tag
|
|
database.DB.FirstOrCreate(&tag, models.Tag{
|
|
Name: tagName,
|
|
Slug: generateSlug(tagName),
|
|
})
|
|
tags = append(tags, tag)
|
|
}
|
|
database.DB.Model(&post).Association("Tags").Replace(tags)
|
|
}
|
|
|
|
if err := database.DB.Model(&post).Updates(updates).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "更新文章失败"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"data": post})
|
|
}
|
|
|
|
// 删除文章
|
|
func DeletePost(c *gin.Context) {
|
|
id := c.Param("id")
|
|
|
|
var post models.Post
|
|
if err := database.DB.First(&post, id).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "文章不存在"})
|
|
return
|
|
}
|
|
|
|
if err := database.DB.Delete(&post).Error; err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "删除文章失败"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "删除成功"})
|
|
}
|
|
|
|
// 判断是否为管理员
|
|
func isAdmin(c *gin.Context) bool {
|
|
role, exists := c.Get("role")
|
|
return exists && role == "admin"
|
|
}
|