mirror of
https://github.com/fish2018/pansou.git
synced 2025-11-25 03:14:59 +08:00
update
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user