deepseek修改版

This commit is contained in:
杭州明婳科技
2026-07-03 00:00:11 +08:00
parent 0f13292a46
commit a74ea0c2f4
22 changed files with 378 additions and 195 deletions
+17 -7
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"sync"
"sync/atomic"
"time"
"github.com/EthanCodeCraft/xlgo-core/logger"
@@ -164,16 +165,25 @@ type CacheManager struct {
svc CacheService
}
// DefaultCache 默认缓存管理器,包级 facade 代理到它
var DefaultCache = NewCacheManager()
// defaultCachePtr 全局默认缓存管理器CK3 修复:atomic.Pointer 替代裸指针)
var defaultCachePtr atomic.Pointer[CacheManager]
func init() {
defaultCachePtr.Store(NewCacheManager())
}
// NewCacheManager 创建缓存管理器实例。
func NewCacheManager() *CacheManager { return &CacheManager{} }
// SetDefaultCacheManager 提升指定 CacheManager 为全局默认
// GetDefaultCache 返回全局默认缓存管理器(并发安全,CK3 修复)
func GetDefaultCache() *CacheManager {
return defaultCachePtr.Load()
}
// SetDefaultCacheManager 提升指定 CacheManager 为全局默认(atomic 置换,并发安全)。
func SetDefaultCacheManager(m *CacheManager) {
if m != nil {
DefaultCache = m
defaultCachePtr.Store(m)
}
}
@@ -201,14 +211,14 @@ func (m *CacheManager) Get() CacheService {
return m.svc
}
// --- 包级 facade(代理到 DefaultCache,兼容存量 ---
// --- 包级 facade(代理到 GetDefaultCache()CK3 修复 ---
// Init 初始化全局缓存实例
func Init() {
DefaultCache.Init()
GetDefaultCache().Init()
}
// GetCache 获取全局缓存实例
func GetCache() CacheService {
return DefaultCache.Get()
return GetDefaultCache().Get()
}
+27 -13
View File
@@ -9,9 +9,10 @@ import (
"github.com/EthanCodeCraft/xlgo-core/config"
)
// KeyBuilder 缓存键名构建器
// KeyBuilder 缓存键名构建器CK2 修复:mu 保护并发访问)。
// 使用场景: 多个小项目共用一台 Redis 服务器,每个项目设置不同前缀
type KeyBuilder struct {
mu sync.Mutex
prefix string // 项目/站点前缀,如 "site_a"
_separator string // 分隔符,默认 ":"
_cacheType string // 缓存类型标识,如 "cache"
@@ -57,10 +58,12 @@ func NewKeyBuilder(opts ...KeyBuilderOption) *KeyBuilder {
return kb
}
// Build 构建完整键名
// Build 构建完整键名CK2 修复:mu 读保护)。
// 格式: {cacheType}{separator}{prefix}{separator}{key}
// 示例: kb.Build("user:1") -> "cache_site_a_user:1"
func (kb *KeyBuilder) Build(key string) string {
kb.mu.Lock()
defer kb.mu.Unlock()
parts := []string{}
if kb._cacheType != "" {
parts = append(parts, kb._cacheType)
@@ -72,9 +75,11 @@ func (kb *KeyBuilder) Build(key string) string {
return strings.Join(parts, kb._separator)
}
// BuildTemp 构建临时缓存键名(带过期时间)
// BuildTemp 构建临时缓存键名(CK2 修复:mu 读保护)。
// 格式: "temp{separator}{prefix}{separator}{key}"
func (kb *KeyBuilder) BuildTemp(key string) string {
kb.mu.Lock()
defer kb.mu.Unlock()
parts := []string{"temp"}
if kb.prefix != "" {
parts = append(parts, kb.prefix)
@@ -83,9 +88,11 @@ func (kb *KeyBuilder) BuildTemp(key string) string {
return strings.Join(parts, kb._separator)
}
// BuildPerm 构建永久缓存键名(不带过期时间)
// BuildPerm 构建永久缓存键名(CK2 修复:mu 读保护)。
// 格式: "perm{separator}{prefix}{separator}{key}"
func (kb *KeyBuilder) BuildPerm(key string) string {
kb.mu.Lock()
defer kb.mu.Unlock()
parts := []string{"perm"}
if kb.prefix != "" {
parts = append(parts, kb.prefix)
@@ -94,9 +101,11 @@ func (kb *KeyBuilder) BuildPerm(key string) string {
return strings.Join(parts, kb._separator)
}
// BuildLock 构建分布式锁键名
// BuildLock 构建分布式锁键名CK2 修复:mu 读保护)。
// 格式: "lock{separator}{prefix}{separator}{key}"
func (kb *KeyBuilder) BuildLock(key string) string {
kb.mu.Lock()
defer kb.mu.Unlock()
parts := []string{"lock"}
if kb.prefix != "" {
parts = append(parts, kb.prefix)
@@ -105,9 +114,11 @@ func (kb *KeyBuilder) BuildLock(key string) string {
return strings.Join(parts, kb._separator)
}
// BuildCounter 构建计数器键名
// BuildCounter 构建计数器键名CK2 修复:mu 读保护)。
// 格式: "counter{separator}{prefix}{separator}{key}"
func (kb *KeyBuilder) BuildCounter(key string) string {
kb.mu.Lock()
defer kb.mu.Unlock()
parts := []string{"counter"}
if kb.prefix != "" {
parts = append(parts, kb.prefix)
@@ -116,9 +127,11 @@ func (kb *KeyBuilder) BuildCounter(key string) string {
return strings.Join(parts, kb._separator)
}
// BuildSession 构建会话键名
// BuildSession 构建会话键名CK2 修复:mu 读保护)。
// 格式: "session{separator}{prefix}{separator}{key}"
func (kb *KeyBuilder) BuildSession(key string) string {
kb.mu.Lock()
defer kb.mu.Unlock()
parts := []string{"session"}
if kb.prefix != "" {
parts = append(parts, kb.prefix)
@@ -127,22 +140,23 @@ func (kb *KeyBuilder) BuildSession(key string) string {
return strings.Join(parts, kb._separator)
}
// BuildPattern 构建匹配模式(用于 SCAN/Keys)
// BuildPattern 构建匹配模式(用于 SCAN/Keys)CK2 修复:mu 读保护)。
// 示例: kb.BuildPattern("user:*") -> "cache_site_a_user:*"
func (kb *KeyBuilder) BuildPattern(pattern string) string {
return kb.Build(pattern)
}
// GetPrefix 获取当前前缀
// GetPrefix 获取当前前缀CK2 修复:mu 读保护)。
func (kb *KeyBuilder) GetPrefix() string {
kb.mu.Lock()
defer kb.mu.Unlock()
return kb.prefix
}
// SetPrefix 动态设置前缀。
//
// 注意(M13):KeyBuilder 实例非并发安全——并发 Build 与 SetPrefix 会竞争 prefix。
// 全局构建器主要在启动期配置,运行期改前缀请自行加锁或重建实例。
// SetPrefix 动态设置前缀(CK2 修复:mu 写保护,并发安全)
func (kb *KeyBuilder) SetPrefix(prefix string) *KeyBuilder {
kb.mu.Lock()
defer kb.mu.Unlock()
kb.prefix = prefix
return kb
}
+42 -28
View File
@@ -82,14 +82,15 @@ end
// 参数: key 锁名称,ttl 锁定时长
// 返回: LockToken 用于后续解锁或续期
func NewLock(ctx context.Context, key string, ttl time.Duration) (*LockToken, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return nil, ErrRedisNotReady
}
token := utils.UUID()
ttlMs := int64(ttl / time.Millisecond)
result, err := database.RedisClient.Eval(ctx, lockScript, []string{key}, token, ttlMs).Result()
result, err := rdb.Eval(ctx, lockScript, []string{key}, token, ttlMs).Result()
if err != nil {
return nil, err
}
@@ -117,7 +118,8 @@ func Lock(ctx context.Context, key string, ttl time.Duration) (bool, error) {
// Unlock 安全释放锁
func Unlock(ctx context.Context, token *LockToken) error {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return ErrRedisNotReady
}
@@ -125,7 +127,7 @@ func Unlock(ctx context.Context, token *LockToken) error {
return ErrLockNotHeld
}
result, err := database.RedisClient.Eval(ctx, unlockScript, []string{token.Key}, token.Token).Result()
result, err := rdb.Eval(ctx, unlockScript, []string{token.Key}, token.Token).Result()
if err != nil {
return err
}
@@ -144,16 +146,18 @@ func Unlock(ctx context.Context, token *LockToken) error {
// UnlockByKey 按键名释放锁(不安全,仅用于旧代码兼容)
// 注意: 此函数不检查 Token,任何客户端都能释放锁
func UnlockByKey(ctx context.Context, key string) error {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return nil
}
return database.RedisClient.Del(ctx, key).Err()
return rdb.Del(ctx, key).Err()
}
// ExtendLock 续期锁
// 参数: token 锁令牌,ttl 新的过期时间
func ExtendLock(ctx context.Context, token *LockToken, ttl time.Duration) error {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return ErrRedisNotReady
}
@@ -163,7 +167,7 @@ func ExtendLock(ctx context.Context, token *LockToken, ttl time.Duration) error
ttlMs := int64(ttl / time.Millisecond)
result, err := database.RedisClient.Eval(ctx, extendScript, []string{token.Key}, token.Token, ttlMs).Result()
result, err := rdb.Eval(ctx, extendScript, []string{token.Key}, token.Token, ttlMs).Result()
if err != nil {
return err
}
@@ -281,83 +285,93 @@ func WithLockAutoExtend(ctx context.Context, key string, initialTTL time.Duratio
// IsLocked 检查锁是否被占用(不获取锁)
func IsLocked(ctx context.Context, key string) (bool, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return false, nil
}
return database.RedisClient.Exists(ctx, key).Val() > 0, nil
return rdb.Exists(ctx, key).Val() > 0, nil
}
// GetLockTTL 获取锁的剩余过期时间
func GetLockTTL(ctx context.Context, key string) (time.Duration, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return 0, nil
}
return database.RedisClient.TTL(ctx, key).Result()
return rdb.TTL(ctx, key).Result()
}
// ForceUnlock 强制释放锁(危险操作,仅用于管理场景)
// 注意: 此函数不检查 Token,强制删除锁
func ForceUnlock(ctx context.Context, key string) error {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return nil
}
return database.RedisClient.Del(ctx, key).Err()
return rdb.Del(ctx, key).Err()
}
// ===== 计数器操作 =====
// Incr 自增计数器
func Incr(ctx context.Context, key string) (int64, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return 0, nil
}
return database.RedisClient.Incr(ctx, key).Result()
return rdb.Incr(ctx, key).Result()
}
// IncrBy 指定增量自增
func IncrBy(ctx context.Context, key string, value int64) (int64, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return 0, nil
}
return database.RedisClient.IncrBy(ctx, key, value).Result()
return rdb.IncrBy(ctx, key, value).Result()
}
// Decr 自减计数器
func Decr(ctx context.Context, key string) (int64, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return 0, nil
}
return database.RedisClient.Decr(ctx, key).Result()
return rdb.Decr(ctx, key).Result()
}
// GetTTL 获取键的剩余过期时间
func GetTTL(ctx context.Context, key string) (time.Duration, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return 0, nil
}
return database.RedisClient.TTL(ctx, key).Result()
return rdb.TTL(ctx, key).Result()
}
// SetExpire 设置键的过期时间
func SetExpire(ctx context.Context, key string, ttl time.Duration) (bool, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return false, nil
}
return database.RedisClient.Expire(ctx, key, ttl).Result()
return rdb.Expire(ctx, key, ttl).Result()
}
// GetRaw 获取原始字符串值(不反序列化)
func GetRaw(ctx context.Context, key string) (string, error) {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return "", nil
}
return database.RedisClient.Get(ctx, key).Result()
return rdb.Get(ctx, key).Result()
}
// SetRaw 设置原始值(不序列化)
func SetRaw(ctx context.Context, key string, value string, ttl time.Duration) error {
if database.RedisClient == nil {
rdb := database.GetRedis()
if rdb == nil {
return nil
}
return database.RedisClient.Set(ctx, key, value, ttl).Err()
return rdb.Set(ctx, key, value, ttl).Err()
}
+4 -5
View File
@@ -14,16 +14,15 @@ import (
"github.com/redis/go-redis/v9"
)
// setupMiniRedis 启动一个 miniredis 实例并把它接到 database.RedisClient,返回清理函数。
// setupMiniRedis 启动一个 miniredis 实例并把它注入 database 内部,返回清理函数。
func setupMiniRedis(t *testing.T) *miniredis.Miniredis {
t.Helper()
mr := miniredis.RunT(t)
client := redis.NewClient(&redis.Options{Addr: mr.Addr()})
t.Cleanup(func() { _ = client.Close() })
// 保存旧值,测试结束恢复,避免污染其他测试。
old := database.RedisClient
database.RedisClient = client
t.Cleanup(func() { database.RedisClient = old })
// 通过 SetTestRedisClient 注入 miniredis 客户端到 database 包的内部 redisClient
database.SetTestRedisClient(client)
t.Cleanup(func() { database.SetTestRedisClient(nil) })
return mr
}