Files
soul-yongping/soul-api/internal/handler/search.go

82 lines
2.1 KiB
Go
Raw Normal View History

package handler
import (
"net/http"
"strings"
"unicode/utf8"
"soul-api/internal/database"
"soul-api/internal/model"
"github.com/gin-gonic/gin"
)
// escapeLike 转义 LIKE 中的 % _ \,防止注入与通配符滥用
func escapeLike(s string) string {
s = strings.ReplaceAll(s, "\\", "\\\\")
s = strings.ReplaceAll(s, "%", "\\%")
s = strings.ReplaceAll(s, "_", "\\_")
return s
}
// SearchGet GET /api/search?q= 从 chapters 表搜索GORM参数化
func SearchGet(c *gin.Context) {
q := strings.TrimSpace(c.Query("q"))
if q == "" {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "请输入搜索关键词"})
return
}
pattern := "%" + escapeLike(q) + "%"
var list []model.Chapter
err := database.DB().Model(&model.Chapter{}).
Where("section_title LIKE ? OR content LIKE ?", pattern, pattern).
Order("sort_order ASC, id ASC").
Limit(50).
Find(&list).Error
if err != nil {
c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{"keyword": q, "total": 0, "results": []interface{}{}}})
return
}
lowerQ := strings.ToLower(q)
results := make([]gin.H, 0, len(list))
for _, ch := range list {
matchType := "content"
score := 5
if strings.Contains(strings.ToLower(ch.SectionTitle), lowerQ) {
matchType = "title"
score = 10
}
snippet := ""
pos := strings.Index(strings.ToLower(ch.Content), lowerQ)
if pos >= 0 && len(ch.Content) > 0 {
start := pos - 50
if start < 0 {
start = 0
}
end := pos + utf8.RuneCountInString(q) + 50
if end > len(ch.Content) {
end = len(ch.Content)
}
snippet = ch.Content[start:end]
if start > 0 {
snippet = "..." + snippet
}
if end < len(ch.Content) {
snippet = snippet + "..."
}
}
price := 1.0
if ch.Price != nil {
price = *ch.Price
}
results = append(results, gin.H{
"id": ch.ID, "title": ch.SectionTitle, "partTitle": ch.PartTitle, "chapterTitle": ch.ChapterTitle,
"price": price, "isFree": ch.IsFree, "matchType": matchType, "score": score, "snippet": snippet,
})
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": gin.H{"keyword": q, "total": len(results), "results": results},
})
}