package share import ( "context" "fmt" "regexp" "strconv" "strings" "time" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gcache" "github.com/gogf/gf/v2/util/gconv" ) var ShareManager = newSessionManager() var ( // ErrCacheMiss 表示缓存未命中 ErrCacheMiss = gerror.New("缓存未找到") // ErrTypeConvert 表示类型转换失败 ErrTypeConvert = gerror.New("缓存值类型转换失败") ) // cacheStore 泛型缓存存储 type cacheStore[T any] struct { manager *gcache.Cache // 缓存管理器 prefix string // 缓存键前缀 } // 生成带前缀的缓存键 - 为普通字符串键使用 func (s *cacheStore[T]) formatKey(key string) string { return s.prefix + strings.TrimSpace(key) } // 生成带前缀的复合缓存键 - 为 uint32+string 组合使用 func (s *cacheStore[T]) formatCompoundKey(userID uint32, sessionID string) string { return s.prefix + strconv.FormatUint(uint64(userID), 10) + ":" + strings.TrimSpace(sessionID) } // Get 通过键获取缓存值 - 原始方法保持兼容性 func (s *cacheStore[T]) Get(ctx context.Context, key string) (T, error) { var zero T result, err := s.manager.Get(ctx, s.formatKey(key)) if err != nil { return zero, gerror.Wrapf(err, "获取缓存失败,键: %s", key) } if result.IsEmpty() { return zero, ErrCacheMiss } // 使用 ConvertWithRefer 进行类型转换 value := gconv.ConvertWithRefer(result.Val(), zero) // 类型断言检查转换结果 converted, ok := value.(T) if !ok { return zero, gerror.Wrapf( ErrTypeConvert, "键: %s,缓存值实际类型: %T,期望类型: %T", key, result.Val(), zero, ) } return converted, nil } // GetByCompoundKey 通过 uint32+string 组合键获取缓存值 func (s *cacheStore[T]) GetByCompoundKey(ctx context.Context, userID uint32, sessionID string) (T, error) { var zero T key := s.formatCompoundKey(userID, sessionID) result, err := s.manager.Get(ctx, key) if err != nil { return zero, gerror.Wrapf(err, "获取缓存失败,键: %s", key) } if result.IsEmpty() { return zero, ErrCacheMiss } // 使用 ConvertWithRefer 进行类型转换 value := gconv.ConvertWithRefer(result.Val(), zero) // 类型断言检查转换结果 converted, ok := value.(T) if !ok { return zero, gerror.Wrapf( ErrTypeConvert, "键: %s,缓存值实际类型: %T,期望类型: %T", key, result.Val(), zero, ) } return converted, nil } // Set 设置缓存值并带有效期 - 原始方法保持兼容性 func (s *cacheStore[T]) Set(ctx context.Context, key string, value T, duration time.Duration) error { err := s.manager.Set(ctx, s.formatKey(key), value, duration) if err != nil { return gerror.Wrapf(err, "设置缓存失败,键: %s,值: %v", key, value) } fmt.Printf("[INFO] 缓存操作 [%s] 键: %s 值: %v 有效期: %v\n", s.prefix, key, value, duration) return nil } // SetByCompoundKey 通过 uint32+string 组合键设置缓存值 func (s *cacheStore[T]) SetByCompoundKey(ctx context.Context, userID uint32, sessionID string, value T, duration time.Duration) error { key := s.formatCompoundKey(userID, sessionID) err := s.manager.Set(ctx, key, value, duration) if err != nil { return gerror.Wrapf(err, "设置缓存失败,键: %s,值: %v", key, value) } fmt.Printf("[INFO] 缓存操作 [%s] 键: %d:%s 值: %v 有效期: %v\n", s.prefix, userID, sessionID, value, duration) return nil } // Del 删除缓存 - 原始方法保持兼容性 func (s *cacheStore[T]) Del(ctx context.Context, key string) error { _, err := s.manager.Remove(ctx, s.formatKey(key)) if err != nil { return gerror.Wrapf(err, "删除缓存失败,键: %s", key) } //fmt.Printf("[INFO] 删除缓存 [%s] 键: %s 成功\n", s.prefix, key) return nil } // DelByCompoundKey 通过 uint32+string 组合键删除缓存 func (s *cacheStore[T]) DelByCompoundKey(ctx context.Context, userID uint32, sessionID string) error { key := s.formatCompoundKey(userID, sessionID) _, err := s.manager.Remove(ctx, key) if err != nil { return gerror.Wrapf(err, "删除缓存失败,键: %s", key) } fmt.Printf("[INFO] 删除缓存 [%s] 键: %d:%s 成功\n", s.prefix, userID, sessionID) return nil } // Contains 检查缓存是否存在 - 原始方法保持兼容性 func (s *cacheStore[T]) Contains(ctx context.Context, key string) (bool, error) { exists, err := s.manager.Contains(ctx, s.formatKey(key)) if err != nil { return false, gerror.Wrapf(err, "检查缓存是否存在失败,键: %s", key) } return exists, nil } // ContainsByCompoundKey 检查 uint32+string 组合键的缓存是否存在 func (s *cacheStore[T]) ContainsByCompoundKey(ctx context.Context, userID uint32, sessionID string) (bool, error) { key := s.formatCompoundKey(userID, sessionID) exists, err := s.manager.Contains(ctx, key) if err != nil { return false, gerror.Wrapf(err, "检查缓存是否存在失败,键: %s", key) } return exists, nil } // GetOrSet 获取缓存值,如果不存在则设置默认值 func (s *cacheStore[T]) GetOrSet(ctx context.Context, key string, defaultValue T, duration time.Duration) (T, error) { var zero T result, err := s.manager.GetOrSet(ctx, s.formatKey(key), defaultValue, duration) if err != nil { return zero, gerror.Wrapf(err, "获取或设置缓存失败,键: %s", key) } // 类型转换 value := gconv.ConvertWithRefer(result.Val(), zero) converted, ok := value.(T) if !ok { return zero, gerror.Wrapf( ErrTypeConvert, "键: %s,缓存值实际类型: %T,期望类型: %T", key, result.Val(), zero, ) } return converted, nil } // Scan 扫描匹配模式的键(先获取所有键,再内存筛选) func (s *cacheStore[T]) Scan(ctx context.Context, pattern string) ([]string, error) { // 1. 获取所有键 allKeys, err := s.manager.Keys(ctx) if err != nil { return nil, gerror.Wrapf(err, "获取所有键失败") } // 2. 筛选符合模式的键(使用字符串匹配,支持简单通配符*) var matchedKeys []string for _, key := range allKeys { // 简单模式匹配:支持*匹配任意字符(可根据需求扩展为glob匹配) if matchSimplePattern(key.(string), pattern) { matchedKeys = append(matchedKeys, key.(string)) } } return matchedKeys, nil } // 简单模式匹配(支持*通配符) func matchSimplePattern(str, pattern string) bool { // 替换pattern中的*为正则匹配符,转义其他特殊字符 regexPattern := strings.ReplaceAll(regexp.QuoteMeta(pattern), "\\*", ".*") regexPattern = "^" + regexPattern + "$" // 全匹配 matched, _ := regexp.MatchString(regexPattern, str) return matched } // MGet 批量获取多个键的值(循环调用Get实现) func (s *cacheStore[T]) MGet(ctx context.Context, keys []string) ([]interface{}, error) { values := make([]interface{}, len(keys)) for i, key := range keys { formattedKey := s.formatKey(key) // 格式化键(添加前缀) result, err := s.manager.Get(ctx, formattedKey) if err != nil { return nil, gerror.Wrapf(err, "批量获取缓存失败,键: %s", key) } if result.IsEmpty() { values[i] = nil // 未命中时存nil } else { values[i] = result.Val() } } return values, nil }