Files
bl/common/data/share/share.go
昔念 026689f3ed ```
feat(cache): 添加复合键缓存操作支持

添加了基于 uint32+string 组合键的缓存操作方法,包括
GetByCompoundKey、SetByCompoundKey、DelByCompoundKey 和
ContainsByCompoundKey 方法,用于处理用户ID和会话ID的组合缓存场景

fix(vscode): 添加 cSpell 配置支持 struc 词汇

refactor(session): 移除过时的会话管理方法

移除了基于单一字符串键的会话管理方法,因为已迁移到使用
复合键的缓存操作方式
```
2026-01-19 18:51:56 +08:00

225 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}