443 lines
11 KiB
Go
443 lines
11 KiB
Go
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)
|
|
}
|