This commit is contained in:
www.xueximeng.com
2025-07-15 11:08:36 +08:00
parent 7df16b209c
commit e76560edb7

View File

@@ -17,11 +17,11 @@ import (
)
// 优先关键词列表
var priorityKeywords = []string{"全", "合集", "系列", "完", "最新", "附", "花园墙外"}
var priorityKeywords = []string{"合集", "系列", "全", "完", "最新", "附"}
// 全局缓存实例和缓存是否初始化标志
var (
twoLevelCache *cache.TwoLevelCache
twoLevelCache *cache.TwoLevelCache
cacheInitialized bool
)
@@ -37,7 +37,7 @@ func init() {
}
// SearchService 搜索服务
type SearchService struct{
type SearchService struct {
pluginManager *plugin.PluginManager
}
@@ -51,7 +51,7 @@ func NewSearchService(pluginManager *plugin.PluginManager) *SearchService {
cacheInitialized = true
}
}
return &SearchService{
pluginManager: pluginManager,
}
@@ -64,7 +64,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
if sourceType == "" {
sourceType = "all"
}
// 插件参数规范化处理
if sourceType == "tg" {
// 对于只搜索Telegram的请求忽略插件参数
@@ -82,7 +82,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
break
}
}
// 如果全是空字符串,视为未指定
if !hasNonEmpty {
plugins = nil
@@ -93,7 +93,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
for _, p := range allPlugins {
allPluginNames = append(allPluginNames, strings.ToLower(p.Name()))
}
// 创建请求的插件名称集合(忽略空字符串)
requestedPlugins := make([]string, 0, len(plugins))
for _, p := range plugins {
@@ -101,7 +101,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
requestedPlugins = append(requestedPlugins, strings.ToLower(p))
}
}
// 如果请求的插件数量与所有插件数量相同,检查是否包含所有插件
if len(requestedPlugins) == len(allPluginNames) {
// 创建映射以便快速查找
@@ -109,7 +109,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
for _, p := range requestedPlugins {
pluginMap[p] = true
}
// 检查是否包含所有插件
allIncluded := true
for _, name := range allPluginNames {
@@ -118,7 +118,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
break
}
}
// 如果包含所有插件统一设为nil
if allIncluded {
plugins = nil
@@ -130,11 +130,11 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
// 立即生成缓存键并检查缓存
cacheKey := cache.GenerateCacheKey(keyword, channels, sourceType, plugins)
// 如果未启用强制刷新,尝试从缓存获取结果
if !forceRefresh && twoLevelCache != nil && config.AppConfig.CacheEnabled {
data, hit, err := twoLevelCache.Get(cacheKey)
if err == nil && hit {
var response model.SearchResponse
if err := cache.DeserializeWithPool(data, &response); err == nil {
@@ -143,16 +143,16 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
}
}
}
// 获取所有可用插件
var availablePlugins []plugin.SearchPlugin
if s.pluginManager != nil && (sourceType == "all" || sourceType == "plugin") {
allPlugins := s.pluginManager.GetPlugins()
// 确保plugins不为nil并且有非空元素
hasPlugins := plugins != nil && len(plugins) > 0
hasNonEmptyPlugin := false
if hasPlugins {
for _, p := range plugins {
if p != "" {
@@ -161,7 +161,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
}
}
}
// 只有当plugins数组包含非空元素时才进行过滤
if hasPlugins && hasNonEmptyPlugin {
pluginMap := make(map[string]bool)
@@ -170,7 +170,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
pluginMap[strings.ToLower(p)] = true
}
}
for _, p := range allPlugins {
if pluginMap[strings.ToLower(p.Name())] {
availablePlugins = append(availablePlugins, p)
@@ -181,16 +181,16 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
availablePlugins = allPlugins
}
}
// 控制并发数:如果用户没有指定有效值,则默认使用"频道数+插件数+10"的并发数
pluginCount := len(availablePlugins)
// 根据sourceType决定是否搜索Telegram频道
channelCount := 0
if sourceType == "all" || sourceType == "tg" {
channelCount = len(channels)
}
if concurrency <= 0 {
concurrency = channelCount + pluginCount + 10
if concurrency < 1 {
@@ -200,7 +200,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
// 计算任务总数(频道数 + 插件数)
totalTasks := channelCount + pluginCount
// 如果没有任务要执行,返回空结果
if totalTasks == 0 {
return model.SearchResponse{
@@ -212,7 +212,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
// 使用工作池执行并行搜索
tasks := make([]pool.Task, 0, totalTasks)
// 添加频道搜索任务(如果需要)
if sourceType == "all" || sourceType == "tg" {
for _, channel := range channels {
@@ -226,7 +226,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
})
}
}
// 添加插件搜索任务(如果需要)
for _, p := range availablePlugins {
plugin := p // 创建副本,避免闭包问题
@@ -238,13 +238,13 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
return results
})
}
// 使用带超时控制的工作池执行所有任务并获取结果
results := pool.ExecuteBatchWithTimeout(tasks, concurrency, config.AppConfig.PluginTimeout)
// 预估每个任务平均返回22个结果
allResults := make([]model.SearchResult, 0, totalTasks*22)
// 合并所有结果
for _, result := range results {
if result != nil {
@@ -252,13 +252,13 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
allResults = append(allResults, channelResults...)
}
}
// 过滤结果,确保标题包含搜索关键词
filteredResults := filterResultsByKeyword(allResults, keyword)
// 按照优化后的规则排序结果
sortResultsByTimeAndKeywords(filteredResults)
// 过滤结果只保留有时间的结果或包含优先关键词的结果到Results中
filteredForResults := make([]model.SearchResult, 0, len(filteredResults))
for _, result := range filteredResults {
@@ -267,10 +267,10 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
filteredForResults = append(filteredForResults, result)
}
}
// 合并链接按网盘类型分组(使用所有过滤后的结果)
mergedLinks := mergeResultsByType(filteredResults)
// 构建响应
var total int
if resultType == "merged_by_type" {
@@ -283,7 +283,7 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
// 只计算filteredForResults的数量
total = len(filteredForResults)
}
response := model.SearchResponse{
Total: total,
Results: filteredForResults, // 使用进一步过滤的结果
@@ -297,12 +297,12 @@ func (s *SearchService) Search(keyword string, channels []string, concurrency in
if err != nil {
return
}
ttl := time.Duration(config.AppConfig.CacheTTLMinutes) * time.Minute
twoLevelCache.Set(cacheKey, data, ttl)
}(response)
}
// 根据resultType过滤返回结果
return filterResponseByType(response, resultType), nil
}
@@ -340,18 +340,18 @@ func filterResponseByType(response model.SearchResponse, resultType string) mode
func filterResultsByKeyword(results []model.SearchResult, keyword string) []model.SearchResult {
// 预估过滤后会保留80%的结果
filteredResults := make([]model.SearchResult, 0, len(results)*8/10)
// 将关键词转为小写,用于不区分大小写的比较
lowerKeyword := strings.ToLower(keyword)
// 将关键词按空格分割,用于支持多关键词搜索
keywords := strings.Fields(lowerKeyword)
for _, result := range results {
// 将标题和内容转为小写
lowerTitle := strings.ToLower(result.Title)
lowerContent := strings.ToLower(result.Content)
// 检查每个关键词是否在标题或内容中
matched := true
for _, kw := range keywords {
@@ -360,7 +360,7 @@ func filterResultsByKeyword(results []model.SearchResult, keyword string) []mode
// 检查标题、内容
pwdInTitle := strings.Contains(lowerTitle, kw)
pwdInContent := strings.Contains(lowerContent, kw)
// 检查链接中是否包含pwd参数
pwdInLinks := false
for _, link := range result.Links {
@@ -369,7 +369,7 @@ func filterResultsByKeyword(results []model.SearchResult, keyword string) []mode
break
}
}
// 只要有一个包含pwd就算匹配
if pwdInTitle || pwdInContent || pwdInLinks {
continue // 匹配成功,检查下一个关键词
@@ -385,12 +385,12 @@ func filterResultsByKeyword(results []model.SearchResult, keyword string) []mode
}
}
}
if matched {
filteredResults = append(filteredResults, result)
}
}
return filteredResults
}
@@ -400,7 +400,7 @@ func sortResultsByTimeAndKeywords(results []model.SearchResult) {
// 检查是否有零值时间
iZeroTime := results[i].Datetime.IsZero()
jZeroTime := results[j].Datetime.IsZero()
// 如果两者都是零值时间,按关键词优先级排序
if iZeroTime && jZeroTime {
iPriority := getKeywordPriority(results[i].Title)
@@ -411,7 +411,7 @@ func sortResultsByTimeAndKeywords(results []model.SearchResult) {
// 如果优先级也相同,按标题字母顺序排序
return results[i].Title < results[j].Title
}
// 如果只有一个是零值时间,将其排在后面
if iZeroTime {
return false // i排在后面
@@ -419,30 +419,30 @@ func sortResultsByTimeAndKeywords(results []model.SearchResult) {
if jZeroTime {
return true // j排在后面i排在前面
}
// 两者都有正常时间,使用原有逻辑
// 计算两个结果的时间差(以天为单位)
timeDiff := daysBetween(results[i].Datetime, results[j].Datetime)
// 如果时间差超过30天按时间排序新的在前面
if abs(timeDiff) > 30 {
return results[i].Datetime.After(results[j].Datetime)
}
// 如果时间差在30天内先检查时间差是否超过1天
if abs(timeDiff) > 1 {
return results[i].Datetime.After(results[j].Datetime)
}
// 如果时间差在1天内检查关键词优先级
iPriority := getKeywordPriority(results[i].Title)
jPriority := getKeywordPriority(results[j].Title)
// 如果优先级不同,优先级高的排在前面
if iPriority != jPriority {
return iPriority > jPriority
}
// 如果优先级相同且时间差在1天内仍然按时间排序新的在前面
return results[i].Datetime.After(results[j].Datetime)
})
@@ -478,39 +478,39 @@ func getKeywordPriority(title string) int {
func (s *SearchService) searchChannel(keyword string, channel string) ([]model.SearchResult, error) {
// 构建搜索URL
url := util.BuildSearchURL(channel, keyword, "")
// 使用全局HTTP客户端已配置代理
client := util.GetHTTPClient()
// 创建一个带超时的上下文
ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
defer cancel()
// 创建请求
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
// 发送请求
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
// 读取响应体
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// 解析响应
results, _, err := util.ParseSearchResults(string(body), channel)
if err != nil {
return nil, err
}
return results, nil
}
@@ -518,10 +518,10 @@ func (s *SearchService) searchChannel(keyword string, channel string) ([]model.S
func mergeResultsByType(results []model.SearchResult) model.MergedLinks {
// 创建合并结果的映射
mergedLinks := make(model.MergedLinks, 10) // 预分配容量假设有10种不同的网盘类型
// 用于去重的映射键为URL
uniqueLinks := make(map[string]model.MergedLink)
// 遍历所有搜索结果
for _, result := range results {
for _, link := range result.Links {
@@ -532,7 +532,7 @@ func mergeResultsByType(results []model.SearchResult) model.MergedLinks {
Note: result.Title,
Datetime: result.Datetime,
}
// 检查是否已存在相同URL的链接
if existingLink, exists := uniqueLinks[link.URL]; exists {
// 如果已存在,只有当当前链接的时间更新时才替换
@@ -545,7 +545,7 @@ func mergeResultsByType(results []model.SearchResult) model.MergedLinks {
}
}
}
// 将去重后的链接按类型分组
for url, mergedLink := range uniqueLinks {
// 获取链接类型
@@ -561,16 +561,16 @@ func mergeResultsByType(results []model.SearchResult) model.MergedLinks {
break
}
}
// 如果没有找到类型,使用"unknown"
if linkType == "" {
linkType = "unknown"
}
// 添加到对应类型的列表中
mergedLinks[linkType] = append(mergedLinks[linkType], mergedLink)
}
// 对每种类型的链接按时间排序(新的在前面)
for linkType, links := range mergedLinks {
sort.Slice(links, func(i, j int) bool {
@@ -578,11 +578,11 @@ func mergeResultsByType(results []model.SearchResult) model.MergedLinks {
})
mergedLinks[linkType] = links
}
return mergedLinks
}
}
// GetPluginManager 获取插件管理器
func (s *SearchService) GetPluginManager() *plugin.PluginManager {
return s.pluginManager
}
}