package handlers import ( "html/template" "log/slog" "math" "net/http" "strconv" "time" "goblog/config" "goblog/database" "goblog/models" "goblog/utils" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // loadSidebarData 加载侧边栏数据 func loadSidebarData() (categories []models.Category, tags []models.Tag, postCount, categoryCount, tagCount int64) { database.DB.Order("name ASC").Find(&categories) database.DB.Order("name ASC").Find(&tags) database.DB.Model(&models.Post{}).Where("status = ?", "published").Count(&postCount) database.DB.Model(&models.Category{}).Count(&categoryCount) database.DB.Model(&models.Tag{}).Count(&tagCount) return } func templateFuncs() template.FuncMap { return template.FuncMap{ "add": func(a, b int) int { return a + b }, "sub": func(a, b int) int { return a - b }, "html": func(s string) template.HTML { return template.HTML(s) }, "markdown": utils.RenderMarkdown, } } // 首页 type IndexData struct { Title string SiteName string SiteDesc string Description string Posts []models.Post Pages []models.Page CurrentPage int TotalPages int Year int // Sidebar 数据 Categories []models.Category Tags []models.Tag PostCount int64 CategoryCount int64 TagCount int64 } func IndexView(c *gin.Context) { cfg := config.GetGlobalConfig() if cfg == nil { cfg = config.Load() } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) if page < 1 { page = 1 } pageSize := 10 categoryID, _ := strconv.Atoi(c.Query("category")) tagID, _ := strconv.Atoi(c.Query("tag")) db := database.DB.Model(&models.Post{}).Preload("Category").Preload("Tags").Preload("Author"). Where("status = ?", "published") userId, exists := c.Get("userID") userId, exists = 1, true if exists { db = db.Or("author_id = ?", userId) } if categoryID > 0 { db = db.Where("category_id = ?", categoryID) } if tagID > 0 { db = db.Joins("JOIN post_tags ON post_tags.post_id = posts.id"). Where("post_tags.tag_id = ?", tagID) } var total int64 db.Count(&total) // 调试日志 slog.Info("首页查询", "total", total, "page", page, "categoryID", categoryID, "tagID", tagID) var posts []models.Post offset := (page - 1) * pageSize db.Order("is_top DESC, published_at DESC, created_at DESC"). Offset(offset).Limit(pageSize).Find(&posts) // 调试日志 slog.Info("查询结果", "postCount", len(posts)) for i, p := range posts { slog.Info("文章", "index", i, "id", p.ID, "title", p.Title, "status", p.Status, "publishedAt", p.PublishedAt) } // 获取页面 var pages []models.Page database.DB.Where("status = ?", "published").Order("`order` ASC").Find(&pages) totalPages := int(math.Ceil(float64(total) / float64(pageSize))) // 加载侧边栏数据 categories, tags, postCount, categoryCount, tagCount := loadSidebarData() // 从数据库读取主题设置,如果不存在则使用配置文件中的值 siteName := cfg.App.Name siteDesc := cfg.App.Description var option models.Option if err := database.DB.Where("name = ? AND user_id = ?", "site_name", 0).First(&option).Error; err == nil { siteName = option.Value } if err := database.DB.Where("name = ? AND user_id = ?", "site_desc", 0).First(&option).Error; err == nil { siteDesc = option.Value } data := IndexData{ Title: "首页", SiteName: siteName, SiteDesc: siteDesc, Description: siteDesc, Posts: posts, Pages: pages, CurrentPage: page, TotalPages: totalPages, Year: time.Now().Year(), // Sidebar 数据 Categories: categories, Tags: tags, PostCount: postCount, CategoryCount: categoryCount, TagCount: tagCount, } c.HTML(http.StatusOK, "index", data) } // 文章详情页 type PostDetailData struct { Title string SiteName string SiteDesc string Description string Post models.Post Pages []models.Page Comments []models.Comment CommentCount int64 Year int PrevPost *models.Post // 上一篇 NextPost *models.Post // 下一篇 // Sidebar 数据 Categories []models.Category Tags []models.Tag PostCount int64 CategoryCount int64 TagCount int64 } func PostView(c *gin.Context) { cfg := config.GetGlobalConfig() if cfg == nil { cfg = config.Load() } slug := c.Param("slug") var post models.Post err := 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") }) }). Where("slug = ? AND status = ?", slug, "published"). First(&post).Error if err != nil { c.HTML(http.StatusNotFound, "index", gin.H{ "Title": "404 - 页面不存在", "SiteName": cfg.App.Name, "Year": time.Now().Year(), }) return } // 增加浏览量 database.DB.Model(&post).UpdateColumn("views", gorm.Expr("views + 1")) // 获取页面 var pages []models.Page database.DB.Where("status = ?", "published").Order("`order` ASC").Find(&pages) // 统计评论数 var commentCount int64 database.DB.Model(&models.Comment{}).Where("post_id = ? AND status = ?", post.ID, "approved").Count(&commentCount) // 获取上一篇(发布时间更早的文章) var prevPost models.Post prevErr := database.DB.Where("status = ? AND published_at < ?", "published", post.PublishedAt). Order("published_at DESC").First(&prevPost).Error // 获取下一篇(发布时间更晚的文章) var nextPost models.Post nextErr := database.DB.Where("status = ? AND published_at > ?", "published", post.PublishedAt). Order("published_at ASC").First(&nextPost).Error // 从数据库读取站点设置 siteName := cfg.App.Name siteDesc := cfg.App.Description var option models.Option if err := database.DB.Where("name = ? AND user_id = ?", "site_name", 0).First(&option).Error; err == nil { siteName = option.Value } if err := database.DB.Where("name = ? AND user_id = ?", "site_desc", 0).First(&option).Error; err == nil { siteDesc = option.Value } // 加载侧边栏数据 categories, tags, postCount, categoryCount, tagCount := loadSidebarData() data := PostDetailData{ Title: post.Title, SiteName: siteName, SiteDesc: siteDesc, Description: post.Summary, Post: post, Pages: pages, Comments: post.Comments, CommentCount: commentCount, Year: time.Now().Year(), // Sidebar 数据 Categories: categories, Tags: tags, PostCount: postCount, CategoryCount: categoryCount, TagCount: tagCount, } // 只有当查询成功时才赋值 if prevErr == nil { data.PrevPost = &prevPost } if nextErr == nil { data.NextPost = &nextPost } c.HTML(http.StatusOK, "post", data) } // 独立页面 type PageDetailData struct { Title string SiteName string SiteDesc string Description string Page models.Page Pages []models.Page Year int // Sidebar 数据 Categories []models.Category Tags []models.Tag PostCount int64 CategoryCount int64 TagCount int64 } func PageView(c *gin.Context) { cfg := config.GetGlobalConfig() if cfg == nil { cfg = config.Load() } slug := c.Param("slug") var page models.Page err := database.DB.Where("slug = ? AND status = ?", slug, "published").First(&page).Error if err != nil { c.HTML(http.StatusNotFound, "index", gin.H{ "Title": "404 - 页面不存在", "SiteName": cfg.App.Name, "Year": time.Now().Year(), }) return } // 获取所有页面 var pages []models.Page database.DB.Where("status = ?", "published").Order("`order` ASC").Find(&pages) // 从数据库读取站点设置 siteName := cfg.App.Name siteDesc := cfg.App.Description var option models.Option if err := database.DB.Where("name = ? AND user_id = ?", "site_name", 0).First(&option).Error; err == nil { siteName = option.Value } if err := database.DB.Where("name = ? AND user_id = ?", "site_desc", 0).First(&option).Error; err == nil { siteDesc = option.Value } // 加载侧边栏数据 categories, tags, postCount, categoryCount, tagCount := loadSidebarData() data := PageDetailData{ Title: page.Title, SiteName: siteName, SiteDesc: siteDesc, Description: "", Page: page, Pages: pages, Year: time.Now().Year(), // Sidebar 数据 Categories: categories, Tags: tags, PostCount: postCount, CategoryCount: categoryCount, TagCount: tagCount, } c.HTML(http.StatusOK, "page", data) } // 提交评论(表单提交) func SubmitComment(c *gin.Context) { postID, _ := strconv.Atoi(c.PostForm("post_id")) author := c.PostForm("author") email := c.PostForm("email") website := c.PostForm("website") content := c.PostForm("content") // 检查文章是否存在 var post models.Post if err := database.DB.First(&post, postID).Error; err != nil { c.String(http.StatusBadRequest, "文章不存在") return } comment := models.Comment{ PostID: uint(postID), Author: author, Email: email, Website: website, Content: content, Status: "pending", } if err := database.DB.Create(&comment).Error; err != nil { c.String(http.StatusInternalServerError, "提交失败") return } c.Redirect(http.StatusFound, "/post/"+post.Slug) } // 搜索结果页 func SearchView(c *gin.Context) { cfg := config.GetGlobalConfig() if cfg == nil { cfg = config.Load() } keyword := c.Query("keyword") if keyword == "" { c.Redirect(http.StatusFound, "/") return } page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) if page < 1 { page = 1 } pageSize := 10 db := database.DB.Model(&models.Post{}).Preload("Category").Preload("Tags").Preload("Author"). Where("status = ?", "published"). Where("title LIKE ? OR content LIKE ? OR summary LIKE ?", "%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%") var total int64 db.Count(&total) var posts []models.Post offset := (page - 1) * pageSize db.Order("published_at DESC"). Offset(offset).Limit(pageSize).Find(&posts) // 获取页面 var pages []models.Page database.DB.Where("status = ?", "published").Order("`order` ASC").Find(&pages) totalPages := int(math.Ceil(float64(total) / float64(pageSize))) // 加载侧边栏数据 categories, tags, postCount, categoryCount, tagCount := loadSidebarData() // 从数据库读取主题设置 siteName := cfg.App.Name siteDesc := cfg.App.Description var option models.Option if err := database.DB.Where("name = ? AND user_id = ?", "site_name", 0).First(&option).Error; err == nil { siteName = option.Value } if err := database.DB.Where("name = ? AND user_id = ?", "site_desc", 0).First(&option).Error; err == nil { siteDesc = option.Value } data := IndexData{ Title: "搜索: " + keyword, SiteName: siteName, SiteDesc: siteDesc, Description: "搜索关键词: " + keyword, Posts: posts, Pages: pages, CurrentPage: page, TotalPages: totalPages, Year: time.Now().Year(), Categories: categories, Tags: tags, PostCount: postCount, CategoryCount: categoryCount, TagCount: tagCount, } c.HTML(http.StatusOK, "index", data) }