mirror of
https://github.com/fish2018/pansou.git
synced 2025-11-25 03:14:59 +08:00
294 lines
7.5 KiB
Go
294 lines
7.5 KiB
Go
package cache
|
||
|
||
import (
|
||
"crypto/md5"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"sort"
|
||
"strings"
|
||
"sync"
|
||
|
||
"pansou/plugin"
|
||
)
|
||
|
||
// 预计算的哈希值映射
|
||
var (
|
||
channelHashCache sync.Map // 存储频道列表哈希
|
||
pluginHashCache sync.Map // 存储插件列表哈希
|
||
|
||
// 预先计算的常用列表哈希值
|
||
precomputedHashes sync.Map
|
||
|
||
// 所有插件名称的哈希值
|
||
allPluginsHash string
|
||
// 所有频道名称的哈希值
|
||
allChannelsHash string
|
||
)
|
||
|
||
// 初始化预计算的哈希值
|
||
func init() {
|
||
// 预计算空列表的哈希值
|
||
precomputedHashes.Store("empty_channels", "all")
|
||
|
||
// 预计算常用的频道组合哈希值
|
||
commonChannels := [][]string{
|
||
{"dongman", "anime"},
|
||
{"movie", "film"},
|
||
{"music", "audio"},
|
||
{"book", "ebook"},
|
||
}
|
||
|
||
for _, channels := range commonChannels {
|
||
key := strings.Join(channels, ",")
|
||
hash := calculateListHash(channels)
|
||
precomputedHashes.Store("channels:"+key, hash)
|
||
}
|
||
|
||
// 预计算常用的插件组合哈希值
|
||
commonPlugins := [][]string{
|
||
{"pan666", "panta"},
|
||
{"aliyun", "baidu"},
|
||
}
|
||
|
||
for _, plugins := range commonPlugins {
|
||
key := strings.Join(plugins, ",")
|
||
hash := calculateListHash(plugins)
|
||
precomputedHashes.Store("plugins:"+key, hash)
|
||
}
|
||
|
||
// 预计算所有插件的哈希值
|
||
allPlugins := plugin.GetRegisteredPlugins()
|
||
allPluginNames := make([]string, 0, len(allPlugins))
|
||
for _, p := range allPlugins {
|
||
allPluginNames = append(allPluginNames, p.Name())
|
||
}
|
||
sort.Strings(allPluginNames)
|
||
allPluginsHash = calculateListHash(allPluginNames)
|
||
precomputedHashes.Store("all_plugins", allPluginsHash)
|
||
|
||
// 预计算所有频道的哈希值(这里假设有一个全局频道列表)
|
||
// 注意:如果没有全局频道列表,可以使用一个默认值
|
||
allChannelsHash = "all"
|
||
precomputedHashes.Store("all_channels", allChannelsHash)
|
||
}
|
||
|
||
// GenerateCacheKey 根据所有影响搜索结果的参数生成缓存键
|
||
func GenerateCacheKey(keyword string, channels []string, sourceType string, plugins []string) string {
|
||
// 关键词标准化
|
||
normalizedKeyword := strings.ToLower(strings.TrimSpace(keyword))
|
||
|
||
// 获取频道列表哈希
|
||
channelsHash := getChannelsHash(channels)
|
||
|
||
// 源类型处理
|
||
if sourceType == "" {
|
||
sourceType = "all"
|
||
}
|
||
|
||
// 插件参数规范化处理
|
||
var pluginsHash string
|
||
if sourceType == "tg" {
|
||
// 对于只搜索Telegram的请求,忽略插件参数
|
||
pluginsHash = "none"
|
||
} else {
|
||
// 获取插件列表哈希
|
||
pluginsHash = getPluginsHash(plugins)
|
||
}
|
||
|
||
// 生成最终缓存键
|
||
keyStr := fmt.Sprintf("%s:%s:%s:%s", normalizedKeyword, channelsHash, sourceType, pluginsHash)
|
||
hash := md5.Sum([]byte(keyStr))
|
||
return hex.EncodeToString(hash[:])
|
||
}
|
||
|
||
// 获取或计算频道哈希
|
||
func getChannelsHash(channels []string) string {
|
||
if channels == nil || len(channels) == 0 {
|
||
// 使用预计算的所有频道哈希
|
||
if hash, ok := precomputedHashes.Load("all_channels"); ok {
|
||
return hash.(string)
|
||
}
|
||
return allChannelsHash
|
||
}
|
||
|
||
// 对于小型列表,直接使用字符串连接
|
||
if len(channels) < 5 {
|
||
channelsCopy := make([]string, len(channels))
|
||
copy(channelsCopy, channels)
|
||
sort.Strings(channelsCopy)
|
||
|
||
// 检查是否有预计算的哈希
|
||
key := strings.Join(channelsCopy, ",")
|
||
if hash, ok := precomputedHashes.Load("channels:"+key); ok {
|
||
return hash.(string)
|
||
}
|
||
|
||
return strings.Join(channelsCopy, ",")
|
||
}
|
||
|
||
// 生成排序后的字符串用作键
|
||
channelsCopy := make([]string, len(channels))
|
||
copy(channelsCopy, channels)
|
||
sort.Strings(channelsCopy)
|
||
key := strings.Join(channelsCopy, ",")
|
||
|
||
// 尝试从缓存获取
|
||
if hash, ok := channelHashCache.Load(key); ok {
|
||
return hash.(string)
|
||
}
|
||
|
||
// 计算哈希
|
||
hash := calculateListHash(channelsCopy)
|
||
|
||
// 存入缓存
|
||
channelHashCache.Store(key, hash)
|
||
return hash
|
||
}
|
||
|
||
// 获取或计算插件哈希
|
||
func getPluginsHash(plugins []string) string {
|
||
// 检查是否为空列表
|
||
if plugins == nil || len(plugins) == 0 {
|
||
// 使用预计算的所有插件哈希
|
||
if hash, ok := precomputedHashes.Load("all_plugins"); ok {
|
||
return hash.(string)
|
||
}
|
||
return allPluginsHash
|
||
}
|
||
|
||
// 检查是否有空字符串元素
|
||
hasNonEmptyPlugin := false
|
||
for _, p := range plugins {
|
||
if p != "" {
|
||
hasNonEmptyPlugin = true
|
||
break
|
||
}
|
||
}
|
||
|
||
// 如果全是空字符串,也视为空列表
|
||
if !hasNonEmptyPlugin {
|
||
if hash, ok := precomputedHashes.Load("all_plugins"); ok {
|
||
return hash.(string)
|
||
}
|
||
return allPluginsHash
|
||
}
|
||
|
||
// 对于小型列表,直接使用字符串连接
|
||
if len(plugins) < 5 {
|
||
pluginsCopy := make([]string, 0, len(plugins))
|
||
for _, p := range plugins {
|
||
if p != "" { // 忽略空字符串
|
||
pluginsCopy = append(pluginsCopy, p)
|
||
}
|
||
}
|
||
sort.Strings(pluginsCopy)
|
||
|
||
// 检查是否有预计算的哈希
|
||
key := strings.Join(pluginsCopy, ",")
|
||
if hash, ok := precomputedHashes.Load("plugins:"+key); ok {
|
||
return hash.(string)
|
||
}
|
||
|
||
return strings.Join(pluginsCopy, ",")
|
||
}
|
||
|
||
// 生成排序后的字符串用作键,忽略空字符串
|
||
pluginsCopy := make([]string, 0, len(plugins))
|
||
for _, p := range plugins {
|
||
if p != "" { // 忽略空字符串
|
||
pluginsCopy = append(pluginsCopy, p)
|
||
}
|
||
}
|
||
sort.Strings(pluginsCopy)
|
||
key := strings.Join(pluginsCopy, ",")
|
||
|
||
// 尝试从缓存获取
|
||
if hash, ok := pluginHashCache.Load(key); ok {
|
||
return hash.(string)
|
||
}
|
||
|
||
// 计算哈希
|
||
hash := calculateListHash(pluginsCopy)
|
||
|
||
// 存入缓存
|
||
pluginHashCache.Store(key, hash)
|
||
return hash
|
||
}
|
||
|
||
// 计算列表的哈希值
|
||
func calculateListHash(items []string) string {
|
||
h := md5.New()
|
||
for _, item := range items {
|
||
h.Write([]byte(item))
|
||
}
|
||
return hex.EncodeToString(h.Sum(nil))
|
||
}
|
||
|
||
// GenerateCacheKeyV2 根据所有影响搜索结果的参数生成缓存键
|
||
// 为保持向后兼容,保留原函数,但标记为已弃用
|
||
func GenerateCacheKeyV2(keyword string, channels []string, sourceType string, plugins []string) string {
|
||
// 关键词标准化:去除首尾空格,转为小写
|
||
normalizedKeyword := strings.ToLower(strings.TrimSpace(keyword))
|
||
|
||
// 频道处理
|
||
var channelsStr string
|
||
if channels != nil && len(channels) > 0 {
|
||
channelsCopy := make([]string, len(channels))
|
||
copy(channelsCopy, channels)
|
||
sort.Strings(channelsCopy)
|
||
channelsStr = strings.Join(channelsCopy, ",")
|
||
} else {
|
||
channelsStr = "all"
|
||
}
|
||
|
||
// 插件处理
|
||
var pluginsStr string
|
||
if plugins != nil && len(plugins) > 0 {
|
||
pluginsCopy := make([]string, len(plugins))
|
||
copy(pluginsCopy, plugins)
|
||
sort.Strings(pluginsCopy)
|
||
pluginsStr = strings.Join(pluginsCopy, ",")
|
||
} else {
|
||
pluginsStr = "all"
|
||
}
|
||
|
||
// 源类型处理
|
||
if sourceType == "" {
|
||
sourceType = "all"
|
||
}
|
||
|
||
// 生成缓存键字符串
|
||
keyStr := fmt.Sprintf("v2:%s:%s:%s:%s", normalizedKeyword, channelsStr, sourceType, pluginsStr)
|
||
|
||
// 计算MD5哈希
|
||
hash := md5.Sum([]byte(keyStr))
|
||
return hex.EncodeToString(hash[:])
|
||
}
|
||
|
||
// GenerateCacheKeyLegacy 根据查询和过滤器生成缓存键
|
||
// 为保持向后兼容,保留原函数,但重命名为更清晰的名称
|
||
func GenerateCacheKeyLegacy(query string, filters map[string]string) string {
|
||
// 如果只需要基于关键词的缓存,不考虑过滤器,调用新函数
|
||
if filters == nil || len(filters) == 0 {
|
||
return GenerateCacheKey(query, nil, "", nil)
|
||
}
|
||
|
||
// 创建包含查询和所有过滤器的字符串
|
||
keyStr := query
|
||
|
||
// 按字母顺序排序过滤器键,确保相同的过滤器集合总是产生相同的键
|
||
var keys []string
|
||
for k := range filters {
|
||
keys = append(keys, k)
|
||
}
|
||
sort.Strings(keys)
|
||
|
||
// 添加过滤器到键字符串
|
||
for _, k := range keys {
|
||
keyStr += "|" + k + "=" + filters[k]
|
||
}
|
||
|
||
// 计算MD5哈希
|
||
hash := md5.Sum([]byte(keyStr))
|
||
return hex.EncodeToString(hash[:])
|
||
} |