增加内容可搜索字段,屏蔽各种标记。
This commit is contained in:
@@ -44,3 +44,6 @@ go.work.sum
|
||||
|
||||
# vendor 目录(如使用 go mod 则无需提交)
|
||||
vendor/
|
||||
|
||||
# 命令行工具
|
||||
cmd/
|
||||
|
||||
@@ -199,15 +199,16 @@ func CreatePost(c *gin.Context) {
|
||||
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 && isAdmin(c), // 非管理员不允许置顶
|
||||
Title: req.Title,
|
||||
Slug: generateSlug(req.Title),
|
||||
Content: req.Content,
|
||||
SearchableText: string(utils.GenerateSearchableText(req.Content)),
|
||||
Summary: req.Summary,
|
||||
Cover: req.Cover,
|
||||
AuthorID: userID.(uint),
|
||||
CategoryID: req.CategoryID,
|
||||
Status: req.Status,
|
||||
IsTop: req.IsTop && isAdmin(c), // 非管理员不允许置顶
|
||||
}
|
||||
|
||||
if req.Status == "published" {
|
||||
@@ -269,6 +270,7 @@ func UpdatePost(c *gin.Context) {
|
||||
}
|
||||
if req.Content != "" {
|
||||
updates["content"] = req.Content
|
||||
updates["searchable_text"] = string(utils.GenerateSearchableText(req.Content))
|
||||
}
|
||||
if req.Summary != "" {
|
||||
updates["summary"] = req.Summary
|
||||
|
||||
@@ -447,7 +447,7 @@ func SearchView(c *gin.Context) {
|
||||
|
||||
db := database.DB.Model(&models.Post{}).Preload("Category").Preload("Tags").Preload("Author").
|
||||
Where("status = ?", "published").
|
||||
Where("title LIKE ? OR content LIKE ? OR summary LIKE ?",
|
||||
Where("title LIKE ? OR searchable_text LIKE ? OR summary LIKE ?",
|
||||
"%"+keyword+"%", "%"+keyword+"%", "%"+keyword+"%")
|
||||
|
||||
var total int64
|
||||
|
||||
+25
-24
@@ -71,30 +71,31 @@ func (u *User) RecordLogin(db *gorm.DB) error {
|
||||
// ==================== 文章模型 ====================
|
||||
|
||||
type Post struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
Title string `gorm:"size:200;not null" json:"title"`
|
||||
Slug string `gorm:"uniqueIndex;size:200" json:"slug"`
|
||||
Content string `gorm:"type:text" json:"content"`
|
||||
Summary string `gorm:"size:500" json:"summary"`
|
||||
Cover string `gorm:"size:255" json:"cover"`
|
||||
AuthorID uint `gorm:"index" json:"author_id"` // 增加索引
|
||||
Author User `gorm:"foreignKey:AuthorID" json:"author"`
|
||||
CategoryID uint `gorm:"index" json:"category_id"` // 增加索引
|
||||
Category Category `gorm:"foreignKey:CategoryID" json:"category"`
|
||||
Tags []Tag `gorm:"many2many:post_tags;" json:"tags"`
|
||||
Files []File `gorm:"many2many:post_files;" json:"files,omitempty"` // 关联文件
|
||||
Views int `gorm:"default:0" json:"views"`
|
||||
Status string `gorm:"index;size:20;default:'draft'" json:"status"` // 增加索引
|
||||
IsTop bool `gorm:"default:false" json:"is_top"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
Password string `gorm:"size:255" json:"password"` // 访问密码保护
|
||||
AllowComment bool `gorm:"default:true" json:"allow_comment"` // 是否允许评论
|
||||
AllowPing bool `gorm:"default:true" json:"allow_ping"` // 是否允许Pingback
|
||||
Template string `gorm:"size:100" json:"template"` // 自定义模板文件
|
||||
Comments []Comment `json:"comments,omitempty"`
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
Title string `gorm:"size:200;not null" json:"title"`
|
||||
Slug string `gorm:"uniqueIndex;size:200" json:"slug"`
|
||||
Content string `gorm:"type:text" json:"content"`
|
||||
SearchableText string `gorm:"size:255" json:"searchable_text"`
|
||||
Summary string `gorm:"size:500" json:"summary"`
|
||||
Cover string `gorm:"size:255" json:"cover"`
|
||||
AuthorID uint `gorm:"index" json:"author_id"` // 增加索引
|
||||
Author User `gorm:"foreignKey:AuthorID" json:"author"`
|
||||
CategoryID uint `gorm:"index" json:"category_id"` // 增加索引
|
||||
Category Category `gorm:"foreignKey:CategoryID" json:"category"`
|
||||
Tags []Tag `gorm:"many2many:post_tags;" json:"tags"`
|
||||
Files []File `gorm:"many2many:post_files;" json:"files,omitempty"` // 关联文件
|
||||
Views int `gorm:"default:0" json:"views"`
|
||||
Status string `gorm:"index;size:20;default:'draft'" json:"status"` // 增加索引
|
||||
IsTop bool `gorm:"default:false" json:"is_top"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
Password string `gorm:"size:255" json:"password"` // 访问密码保护
|
||||
AllowComment bool `gorm:"default:true" json:"allow_comment"` // 是否允许评论
|
||||
AllowPing bool `gorm:"default:true" json:"allow_ping"` // 是否允许Pingback
|
||||
Template string `gorm:"size:100" json:"template"` // 自定义模板文件
|
||||
Comments []Comment `json:"comments,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
|
||||
@@ -3,6 +3,8 @@ package utils
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/extension"
|
||||
@@ -29,3 +31,86 @@ func RenderMarkdown(source string) template.HTML {
|
||||
}
|
||||
return template.HTML(buf.String())
|
||||
}
|
||||
|
||||
// 全局预编译正则(保持不变,但移除冗余)
|
||||
var (
|
||||
// 标题
|
||||
reHeading = regexp.MustCompile(`(?m)^\s*#{1,6}\s+`)
|
||||
// 无序列表
|
||||
reUnorderedList = regexp.MustCompile(`(?m)^\s*[-*+]\s+`)
|
||||
// 有序列表
|
||||
reOrderedList = regexp.MustCompile(`(?m)^\s*\d+[.)]\s+`)
|
||||
// 块引用
|
||||
reBlockquote = regexp.MustCompile(`(?m)^\s*>\s+`)
|
||||
// 水平线
|
||||
reHr = regexp.MustCompile(`(?m)^\s*[-*_]{3,}\s*$`)
|
||||
// 脚注定义
|
||||
reFootnoteDef = regexp.MustCompile(`(?m)^\[\^.+?\]:\s.*$`)
|
||||
// 引用定义
|
||||
reRefDef = regexp.MustCompile(`(?m)^\s*\[.+?\]:\s*(.*)$`)
|
||||
// 图片
|
||||
reImage = regexp.MustCompile(`(?s)!\[.*?\]\(.*?\)`)
|
||||
// 引用图片
|
||||
reRefImage = regexp.MustCompile(`!\[.*?\](\[.*?\])?`)
|
||||
// 链接
|
||||
reLink = regexp.MustCompile(`(?s)\[(.*?)\]\(.*?\)`)
|
||||
// 引用式链接
|
||||
reRefLink = regexp.MustCompile(`\[([^\s=\[\]/]+)\](\[.*?\])?`)
|
||||
// 粗体
|
||||
reBoldStar = regexp.MustCompile(`(?s)\*\*(.*?)\*\*`)
|
||||
// 斜体
|
||||
reItalicStar = regexp.MustCompile(`(?s)\*(.*?)\*`)
|
||||
// 下划线粗体
|
||||
reBoldUnderscore = regexp.MustCompile(`(?s)_(.*?)_`)
|
||||
// 下划线斜体
|
||||
reItalicUnderscore = regexp.MustCompile(`(?s)_(.*?)_`)
|
||||
|
||||
// HTML 标签
|
||||
reHTMLTag = regexp.MustCompile(`<[^>]+>`)
|
||||
// 多余空白
|
||||
reMultiSpace = regexp.MustCompile(`\s+`)
|
||||
)
|
||||
|
||||
// GenerateSearchableText 提取 Markdown 中的纯文本
|
||||
func GenerateSearchableText(raw string) string {
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
text := raw
|
||||
|
||||
// 全角空格转半角
|
||||
text = strings.ReplaceAll(text, "\u3000", " ")
|
||||
|
||||
// 移除块级标记(一次性替换多个)
|
||||
text = reHr.ReplaceAllString(text, "")
|
||||
text = reBlockquote.ReplaceAllString(text, "")
|
||||
text = reUnorderedList.ReplaceAllString(text, "")
|
||||
text = reOrderedList.ReplaceAllString(text, "")
|
||||
text = reHeading.ReplaceAllString(text, "")
|
||||
|
||||
// 移除引用定义
|
||||
text = reFootnoteDef.ReplaceAllString(text, "")
|
||||
text = reRefDef.ReplaceAllString(text, "")
|
||||
|
||||
// 移除图片
|
||||
text = reImage.ReplaceAllString(text, "")
|
||||
text = reRefImage.ReplaceAllString(text, "")
|
||||
|
||||
// 处理链接:保留可见文字(先引用式后内联)
|
||||
text = reRefLink.ReplaceAllString(text, "$1")
|
||||
text = reLink.ReplaceAllString(text, "$1")
|
||||
|
||||
// 移除粗体
|
||||
text = reBoldStar.ReplaceAllString(text, "")
|
||||
text = reBoldUnderscore.ReplaceAllString(text, "")
|
||||
// 移除斜体
|
||||
text = reItalicStar.ReplaceAllString(text, "")
|
||||
text = reItalicUnderscore.ReplaceAllString(text, "")
|
||||
|
||||
// 移除 HTML 标签
|
||||
text = reHTMLTag.ReplaceAllString(text, "")
|
||||
|
||||
// 压缩空白
|
||||
text = reMultiSpace.ReplaceAllString(text, " ")
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user