更新管理后台布局,优化菜单项标签,新增支付配置项。同时,调整API响应字段命名,确保一致性,提升代码可读性和维护性。
This commit is contained in:
81
soul-api/internal/handler/search.go
Normal file
81
soul-api/internal/handler/search.go
Normal file
@@ -0,0 +1,81 @@
|
||||
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},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user