增加filter过滤

This commit is contained in:
www.xueximeng.com
2025-11-18 19:08:04 +08:00
parent 3d61e6e33e
commit 905c94ad6b
4 changed files with 180 additions and 0 deletions

View File

@@ -404,6 +404,7 @@ curl -X POST http://localhost:8888/api/auth/logout
| plugins | string[] | 否 | 指定搜索的插件列表,不指定则搜索全部插件 | | plugins | string[] | 否 | 指定搜索的插件列表,不指定则搜索全部插件 |
| cloud_types | string[] | 否 | 指定返回的网盘类型列表支持baidu、aliyun、quark、tianyi、uc、mobile、115、pikpak、xunlei、123、magnet、ed2k不指定则返回所有类型 | | cloud_types | string[] | 否 | 指定返回的网盘类型列表支持baidu、aliyun、quark、tianyi、uc、mobile、115、pikpak、xunlei、123、magnet、ed2k不指定则返回所有类型 |
| ext | object | 否 | 扩展参数,用于传递给插件的自定义参数,如{"title_en":"English Title", "is_all":true} | | ext | object | 否 | 扩展参数,用于传递给插件的自定义参数,如{"title_en":"English Title", "is_all":true} |
| filter | object | 否 | 过滤配置,用于过滤返回结果。格式:{"include":["关键词1","关键词2"],"exclude":["排除词1","排除词2"]}。include为包含关键词列表OR关系exclude为排除关键词列表AND关系 |
**GET请求参数** **GET请求参数**
@@ -418,6 +419,7 @@ curl -X POST http://localhost:8888/api/auth/logout
| plugins | string | 否 | 指定搜索的插件列表,使用英文逗号分隔多个插件名,不指定则搜索全部插件 | | plugins | string | 否 | 指定搜索的插件列表,使用英文逗号分隔多个插件名,不指定则搜索全部插件 |
| cloud_types | string | 否 | 指定返回的网盘类型列表使用英文逗号分隔多个类型支持baidu、aliyun、quark、tianyi、uc、mobile、115、pikpak、xunlei、123、magnet、ed2k不指定则返回所有类型 | | cloud_types | string | 否 | 指定返回的网盘类型列表使用英文逗号分隔多个类型支持baidu、aliyun、quark、tianyi、uc、mobile、115、pikpak、xunlei、123、magnet、ed2k不指定则返回所有类型 |
| ext | string | 否 | JSON格式的扩展参数用于传递给插件的自定义参数如{"title_en":"English Title", "is_all":true} | | ext | string | 否 | JSON格式的扩展参数用于传递给插件的自定义参数如{"title_en":"English Title", "is_all":true} |
| filter | string | 否 | JSON格式的过滤配置用于过滤返回结果。格式{"include":["关键词1","关键词2"],"exclude":["排除词1","排除词2"]} |
**POST请求示例** **POST请求示例**
@@ -448,6 +450,17 @@ curl -X POST http://localhost:8888/api/search \
"kw": "速度与激情", "kw": "速度与激情",
"res": "merge" "res": "merge"
}' }'
# 使用过滤器(只返回包含“合集”或“全集”,且不包含“预告”的结果)
curl -X POST http://localhost:8888/api/search \
-H "Content-Type: application/json" \
-d '{
"kw": "唐朝诡事录",
"filter": {
"include": ["合集", "全集"],
"exclude": ["预告", "花絮"]
}
}'
``` ```
**GET请求示例** **GET请求示例**
@@ -459,6 +472,9 @@ curl "http://localhost:8888/api/search?kw=速度与激情&res=merge&src=tg"
# 启用认证时需要添加Authorization头 # 启用认证时需要添加Authorization头
curl "http://localhost:8888/api/search?kw=速度与激情&res=merge" \ curl "http://localhost:8888/api/search?kw=速度与激情&res=merge" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
# 使用过滤器GET方式需要URL编码JSON
curl "http://localhost:8888/api/search?kw=唐朝诡事录&filter=%7B%22include%22%3A%5B%22合集%22%2C%22全集%22%5D%2C%22exclude%22%3A%5B%22预告%22%5D%7D"
``` ```
**成功响应** **成功响应**

140
api/filter.go Normal file
View File

@@ -0,0 +1,140 @@
package api
import (
"pansou/model"
"strings"
)
// applyResultFilter 应用过滤器到搜索响应
func applyResultFilter(response model.SearchResponse, filter *model.FilterConfig, resultType string) model.SearchResponse {
if filter == nil || (len(filter.Include) == 0 && len(filter.Exclude) == 0) {
return response
}
// 预处理关键词(转小写)
includeKeywords := make([]string, len(filter.Include))
for i, kw := range filter.Include {
includeKeywords[i] = strings.ToLower(kw)
}
excludeKeywords := make([]string, len(filter.Exclude))
for i, kw := range filter.Exclude {
excludeKeywords[i] = strings.ToLower(kw)
}
// 根据结果类型决定过滤策略
if resultType == "merged_by_type" || resultType == "" {
// 过滤 merged_by_type 的 note 字段
response.MergedByType = filterMergedByType(response.MergedByType, includeKeywords, excludeKeywords)
// 重新计算 total
total := 0
for _, links := range response.MergedByType {
total += len(links)
}
response.Total = total
} else if resultType == "all" || resultType == "results" {
// 过滤 results 的 title 和 links 的 work_title
response.Results = filterResults(response.Results, includeKeywords, excludeKeywords)
response.Total = len(response.Results)
// 如果是 all 类型,也需要过滤 merged_by_type
if resultType == "all" {
response.MergedByType = filterMergedByType(response.MergedByType, includeKeywords, excludeKeywords)
}
}
return response
}
// filterMergedByType 过滤 merged_by_type 中的链接
func filterMergedByType(mergedLinks model.MergedLinks, includeKeywords, excludeKeywords []string) model.MergedLinks {
if mergedLinks == nil {
return nil
}
filtered := make(model.MergedLinks)
for linkType, links := range mergedLinks {
filteredLinks := make([]model.MergedLink, 0)
for _, link := range links {
if matchFilter(link.Note, includeKeywords, excludeKeywords) {
filteredLinks = append(filteredLinks, link)
}
}
// 只添加非空的类型
if len(filteredLinks) > 0 {
filtered[linkType] = filteredLinks
}
}
return filtered
}
// filterResults 过滤 results 数组
func filterResults(results []model.SearchResult, includeKeywords, excludeKeywords []string) []model.SearchResult {
if results == nil {
return nil
}
filtered := make([]model.SearchResult, 0)
for _, result := range results {
// 先检查 title 是否匹配
if !matchFilter(result.Title, includeKeywords, excludeKeywords) {
continue
}
// title 匹配后,过滤 links 中的 work_title
filteredLinks := make([]model.Link, 0)
for _, link := range result.Links {
// 如果 link 有 work_title检查它否则使用 result.Title
checkText := link.WorkTitle
if checkText == "" {
checkText = result.Title
}
if matchFilter(checkText, includeKeywords, excludeKeywords) {
filteredLinks = append(filteredLinks, link)
}
}
// 只有有链接的结果才添加
if len(filteredLinks) > 0 {
result.Links = filteredLinks
filtered = append(filtered, result)
}
}
return filtered
}
// matchFilter 检查文本是否匹配过滤条件
func matchFilter(text string, includeKeywords, excludeKeywords []string) bool {
lowerText := strings.ToLower(text)
// 检查 exclude任一匹配则排除
for _, kw := range excludeKeywords {
if strings.Contains(lowerText, kw) {
return false
}
}
// 检查 include如果有 include 列表,必须至少匹配一个)
if len(includeKeywords) > 0 {
matched := false
for _, kw := range includeKeywords {
if strings.Contains(lowerText, kw) {
matched = true
break
}
}
if !matched {
return false
}
}
return true
}

View File

@@ -130,6 +130,17 @@ func SearchHandler(c *gin.Context) {
if ext == nil { if ext == nil {
ext = make(map[string]interface{}) ext = make(map[string]interface{})
} }
// 处理filter参数JSON格式
var filter *model.FilterConfig
filterStr := c.Query("filter")
if filterStr != "" && filterStr != " " {
filter = &model.FilterConfig{}
if err := jsonutil.Unmarshal([]byte(filterStr), filter); err != nil {
c.JSON(http.StatusBadRequest, model.NewErrorResponse(400, "无效的filter参数格式: "+err.Error()))
return
}
}
req = model.SearchRequest{ req = model.SearchRequest{
Keyword: keyword, Keyword: keyword,
@@ -141,6 +152,7 @@ func SearchHandler(c *gin.Context) {
Plugins: plugins, Plugins: plugins,
CloudTypes: cloudTypes, // 添加cloud_types到请求中 CloudTypes: cloudTypes, // 添加cloud_types到请求中
Ext: ext, Ext: ext,
Filter: filter,
} }
} else { } else {
// POST方式从请求体获取 // POST方式从请求体获取
@@ -200,6 +212,11 @@ func SearchHandler(c *gin.Context) {
return return
} }
// 应用过滤器
if req.Filter != nil {
result = applyResultFilter(result, req.Filter, req.ResultType)
}
// 包装SearchResponse到标准响应格式中 // 包装SearchResponse到标准响应格式中
response := model.NewSuccessResponse(result) response := model.NewSuccessResponse(result)
jsonData, _ := jsonutil.Marshal(response) jsonData, _ := jsonutil.Marshal(response)

View File

@@ -1,5 +1,11 @@
package model package model
// FilterConfig 过滤配置
type FilterConfig struct {
Include []string `json:"include,omitempty"` // 包含关键词列表OR关系
Exclude []string `json:"exclude,omitempty"` // 排除关键词列表AND关系
}
// SearchRequest 搜索请求参数 // SearchRequest 搜索请求参数
type SearchRequest struct { type SearchRequest struct {
Keyword string `json:"kw" binding:"required"` // 搜索关键词 Keyword string `json:"kw" binding:"required"` // 搜索关键词
@@ -11,4 +17,5 @@ type SearchRequest struct {
Plugins []string `json:"plugins"` // 指定搜索的插件列表,不指定则搜索全部插件 Plugins []string `json:"plugins"` // 指定搜索的插件列表,不指定则搜索全部插件
Ext map[string]interface{} `json:"ext"` // 扩展参数,用于传递给插件的自定义参数 Ext map[string]interface{} `json:"ext"` // 扩展参数,用于传递给插件的自定义参数
CloudTypes []string `json:"cloud_types"` // 指定返回的网盘类型列表,不指定则返回所有类型 CloudTypes []string `json:"cloud_types"` // 指定返回的网盘类型列表,不指定则返回所有类型
Filter *FilterConfig `json:"filter,omitempty"` // 过滤配置,用于过滤返回结果
} }