wanshenmean
2026-03-04 17e5dbd7bd0364e27a33f1a7dab91cf33d5dcabc
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Cache/HybridCacheService.cs
@@ -53,9 +53,20 @@
        public bool Add(string key, string value, int expireSeconds = -1, bool isSliding = false)
        {
            var fullKey = BuildKey(key);
            // 只有启用L1缓存时才写入内存缓存
            if (_options.EnableL1Cache)
            {
            SetMemoryCache(fullKey, value, expireSeconds, isSliding);
            }
            if (!RedisAvailable)
            {
                if (!_options.EnableL1Cache)
                {
                    _logger.LogWarning("Redis不可用且L1缓存已禁用, key={Key}", key);
                    return false;
                }
                _logger.LogWarning("Redis不可用,仅使用内存缓存, key={Key}", key);
                return true;
            }
@@ -69,7 +80,7 @@
            catch (Exception ex)
            {
                _logger.LogWarning(ex, "Redis Add失败, key={Key}", key);
                return _options.FallbackToMemory;
                return _options.FallbackToMemory && _options.EnableL1Cache;
            }
        }
@@ -91,8 +102,14 @@
        public bool Remove(string key)
        {
            var fullKey = BuildKey(key);
            // 只有启用L1缓存时才从内存缓存中移除
            if (_options.EnableL1Cache)
            {
            _memoryCache.Remove(fullKey);
            if (!RedisAvailable) return true;
            }
            if (!RedisAvailable) return _options.EnableL1Cache;
            try
            {
                return _connectionManager.GetDatabase().KeyDelete(fullKey);
@@ -109,9 +126,434 @@
            foreach (var key in keys) Remove(key);
        }
        #region 删除扩展方法
        public string? RemoveAndGet(string key)
        {
            var value = Get(key);
            if (value != null) Remove(key);
            return value;
        }
        public T? RemoveAndGet<T>(string key) where T : class
        {
            var value = Get<T>(key);
            if (value != null) Remove(key);
            return value;
        }
        public int RemoveByPrefix(string prefix)
        {
            if (string.IsNullOrEmpty(prefix)) return 0;
            var fullPrefix = BuildKey(prefix);
            int count = 0;
            // 删除内存缓存中的匹配项
            // MemoryCache无法枚举,跳过
            if (RedisAvailable)
            {
                try
                {
                    var server = _connectionManager.GetServer();
                    var keys = server.Keys(pattern: $"{fullPrefix}*").ToArray();
                    if (keys.Length > 0)
                    {
                        count = (int)_connectionManager.GetDatabase().KeyDelete(keys);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis RemoveByPrefix失败, prefix={Prefix}", prefix);
                }
            }
            return count;
        }
        public int RemoveByPattern(string pattern)
        {
            if (string.IsNullOrEmpty(pattern)) return 0;
            int count = 0;
            if (RedisAvailable)
            {
                try
                {
                    var server = _connectionManager.GetServer();
                    var keys = server.Keys(pattern: $"{_options.KeyPrefix}{pattern}").ToArray();
                    if (keys.Length > 0)
                    {
                        count = (int)_connectionManager.GetDatabase().KeyDelete(keys);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis RemoveByPattern失败, pattern={Pattern}", pattern);
                }
            }
            return count;
        }
        public int RemoveAll(IEnumerable<string> keys)
        {
            if (keys == null) return 0;
            int count = 0;
            foreach (var key in keys)
            {
                if (Remove(key)) count++;
            }
            return count;
        }
        public int RemoveWhere(Func<string, bool> predicate)
        {
            if (predicate == null) return 0;
            int count = 0;
            if (RedisAvailable)
            {
                try
                {
                    var server = _connectionManager.GetServer();
                    var keys = server.Keys(pattern: $"{_options.KeyPrefix}*").ToArray();
                    var keysToDelete = keys.Where(k =>
                    {
                        var originalKey = k.ToString().Replace(_options.KeyPrefix, "");
                        return predicate(originalKey);
                    }).ToArray();
                    if (keysToDelete.Length > 0)
                    {
                        count = (int)_connectionManager.GetDatabase().KeyDelete(keysToDelete);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis RemoveWhere失败");
                }
            }
            return count;
        }
        #endregion
        #region 添加和修改扩展方法
        public void AddAll(IDictionary<string, string> items, int expireSeconds = -1)
        {
            if (items == null) return;
            foreach (var item in items)
            {
                Add(item.Key, item.Value, expireSeconds);
            }
        }
        public void AddAllObjects(IDictionary<string, object> items, int expireSeconds = -1)
        {
            if (items == null) return;
            foreach (var item in items)
            {
                AddObject(item.Key, item.Value, expireSeconds);
            }
        }
        public bool Replace(string key, string newValue, int expireSeconds = -1)
        {
            return TryUpdate(key, newValue, expireSeconds);
        }
        public bool Replace<T>(string key, T newValue, int expireSeconds = -1) where T : class
        {
            if (!Exists(key)) return false;
            AddObject(key, newValue, expireSeconds);
            return true;
        }
        public string? GetAndRefresh(string key, int expireSeconds)
        {
            var fullKey = BuildKey(key);
            string? value = null;
            // 从Redis获取值
            if (RedisAvailable)
            {
                try
                {
                    var redisValue = _connectionManager.GetDatabase().StringGet(fullKey);
                    if (!redisValue.IsNullOrEmpty)
                    {
                        value = redisValue.ToString();
                        // 刷新Redis过期时间
                        _connectionManager.GetDatabase().KeyExpire(fullKey, TimeSpan.FromSeconds(expireSeconds));
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis GetAndRefresh失败, key={Key}", key);
                }
            }
            // 如果Redis不可用,尝试从内存缓存获取
            if (value == null && _options.EnableL1Cache)
            {
                if (_memoryCache.TryGetValue(fullKey, out string? cached))
                    value = cached;
            }
            // 更新内存缓存(如果有值)
            if (value != null && _options.EnableL1Cache)
            {
                SetMemoryCache(fullKey, value, expireSeconds, false);
            }
            return value;
        }
        public T? GetAndRefresh<T>(string key, int expireSeconds) where T : class
        {
            var fullKey = BuildKey(key);
            T? value = default;
            // 从Redis获取值
            if (RedisAvailable)
            {
                try
                {
                    var redisValue = _connectionManager.GetDatabase().StringGet(fullKey);
                    if (!redisValue.IsNullOrEmpty)
                    {
                        var json = redisValue.ToString();
                        value = _serializer.Deserialize<T>(json);
                        // 刷新Redis过期时间
                        _connectionManager.GetDatabase().KeyExpire(fullKey, TimeSpan.FromSeconds(expireSeconds));
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis GetAndRefresh<T>失败, key={Key}", key);
                }
            }
            // 如果Redis不可用,尝试从内存缓存获取
            if (value == null && _options.EnableL1Cache)
            {
                if (_memoryCache.TryGetValue(fullKey, out string? cached) && cached != null)
                    value = _serializer.Deserialize<T>(cached);
            }
            // 更新内存缓存(如果有值)
            if (value != null && _options.EnableL1Cache)
            {
                SetMemoryCache(fullKey, _serializer.Serialize(value), expireSeconds, false);
            }
            return value;
        }
        public bool RefreshExpire(string key, int expireSeconds)
        {
            var fullKey = BuildKey(key);
            bool result = false;
            // 刷新Redis过期时间
            if (RedisAvailable)
            {
                try
                {
                    result = _connectionManager.GetDatabase().KeyExpire(fullKey, TimeSpan.FromSeconds(expireSeconds));
                }
                catch { }
            }
            // 更新内存缓存过期时间(需要重新设置值来刷新过期时间)
            if (_options.EnableL1Cache && _memoryCache.TryGetValue(fullKey, out string? cached) && cached != null)
            {
                SetMemoryCache(fullKey, cached, expireSeconds, false);
                return true;
            }
            return result;
        }
        public bool ExpireIn(string key, int seconds)
        {
            return RefreshExpire(key, seconds);
        }
        public bool ExpireAt(string key, DateTime expireTime)
        {
            var seconds = (long)(expireTime - DateTime.Now).TotalSeconds;
            if (seconds <= 0) return Remove(key);
            return RefreshExpire(key, (int)seconds);
        }
        public long? GetExpire(string key)
        {
            if (RedisAvailable)
            {
                try
                {
                    var ttl = _connectionManager.GetDatabase().KeyTimeToLive(BuildKey(key));
                    return ttl.HasValue ? (long)ttl.Value.TotalSeconds : null;
                }
                catch { }
            }
            return null; // MemoryCache不支持TTL查询
        }
        public bool AddIfNotExists(string key, string value, int expireSeconds = -1)
        {
            return TryAdd(key, value, expireSeconds);
        }
        public bool AddIfNotExists<T>(string key, T value, int expireSeconds = -1) where T : class
        {
            return TryAdd(key, value, expireSeconds);
        }
        public string? GetAndSet(string key, string newValue, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);
            string? oldValue = null;
            // 从Redis获取旧值
            if (RedisAvailable)
            {
                try
                {
                    var value = _connectionManager.GetDatabase().StringGet(fullKey);
                    oldValue = value.IsNullOrEmpty ? null : value.ToString();
                }
                catch { }
            }
            // 如果Redis不可用,从内存缓存获取
            if (oldValue == null && _options.EnableL1Cache)
            {
                _memoryCache.TryGetValue(fullKey, out oldValue);
            }
            // 写入Redis
            if (RedisAvailable)
            {
                try
                {
                    var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
                    _connectionManager.GetDatabase().StringSet(fullKey, newValue, expiry);
                }
                catch { }
            }
            // 更新内存缓存
            if (_options.EnableL1Cache)
            {
                SetMemoryCache(fullKey, newValue, expireSeconds, false);
            }
            return oldValue;
        }
        public T? GetAndSet<T>(string key, T newValue, int expireSeconds = -1) where T : class
        {
            var fullKey = BuildKey(key);
            T? oldValue = default;
            string? oldJson = null;
            // 从Redis获取旧值
            if (RedisAvailable)
            {
                try
                {
                    var value = _connectionManager.GetDatabase().StringGet(fullKey);
                    if (!value.IsNullOrEmpty)
                    {
                        oldJson = value.ToString();
                        oldValue = _serializer.Deserialize<T>(oldJson);
                    }
                }
                catch { }
            }
            var newJson = _serializer.Serialize(newValue);
            // 写入Redis
            if (RedisAvailable)
            {
                try
                {
                    var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
                    _connectionManager.GetDatabase().StringSet(fullKey, newJson, expiry);
                }
                catch { }
            }
            // 更新内存缓存
            if (_options.EnableL1Cache)
            {
                SetMemoryCache(fullKey, newJson, expireSeconds, false);
            }
            return oldValue;
        }
        public long Increment(string key, long value = 1)
        {
            if (RedisAvailable)
            {
                try
                {
                    return _connectionManager.GetDatabase().StringIncrement(BuildKey(key), value);
                }
                catch { }
            }
            // Fallback to memory
            var current = long.TryParse(Get(key), out var v) ? v : 0;
            var newValue = current + value;
            Add(key, newValue.ToString());
            return newValue;
        }
        public long Decrement(string key, long value = 1)
        {
            return Increment(key, -value);
        }
        public long Append(string key, string value)
        {
            var current = Get(key) ?? "";
            var newValue = current + value;
            Add(key, newValue);
            return newValue.Length;
        }
        #endregion
        public T? Get<T>(string key) where T : class
        {
            var fullKey = BuildKey(key);
            // 如果禁用了L1缓存,直接查Redis
            if (!_options.EnableL1Cache)
            {
                if (!RedisAvailable) return default;
                try
                {
                    var value = _connectionManager.GetDatabase().StringGet(fullKey);
                    if (value.IsNullOrEmpty) return default;
                    return _serializer.Deserialize<T>(value!);
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis Get<T>失败, key={Key}", key);
                    return default;
                }
            }
            // 正常的L1+L2逻辑
            if (_memoryCache.TryGetValue(fullKey, out string? cached) && cached != null)
                return _serializer.Deserialize<T>(cached);
@@ -134,6 +576,25 @@
        public object? Get(Type type, string key)
        {
            var fullKey = BuildKey(key);
            // 如果禁用了L1缓存,直接查Redis
            if (!_options.EnableL1Cache)
            {
                if (!RedisAvailable) return null;
                try
                {
                    var value = _connectionManager.GetDatabase().StringGet(fullKey);
                    if (value.IsNullOrEmpty) return null;
                    return _serializer.Deserialize(value!, type);
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis Get(Type)失败, key={Key}", key);
                    return null;
                }
            }
            // 正常的L1+L2逻辑
            if (_memoryCache.TryGetValue(fullKey, out string? cached) && cached != null)
                return _serializer.Deserialize(cached, type);
@@ -156,6 +617,24 @@
        public string? Get(string key)
        {
            var fullKey = BuildKey(key);
            // 如果禁用了L1缓存,直接查Redis
            if (!_options.EnableL1Cache)
            {
                if (!RedisAvailable) return null;
                try
                {
                    var value = _connectionManager.GetDatabase().StringGet(fullKey);
                    return value.IsNullOrEmpty ? null : value.ToString();
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis Get失败, key={Key}", key);
                    return null;
                }
            }
            // 正常的L1+L2逻辑
            if (_memoryCache.TryGetValue(fullKey, out string? cached))
                return cached;
@@ -229,6 +708,73 @@
            return true;
        }
        public bool TryUpdateIfChanged(string key, string newValue, int expireSeconds = -1)
        {
            var existing = Get(key);
            if (existing == null) return false;
            if (existing == newValue) return false; // 值相同,不更新
            Add(key, newValue, expireSeconds);
            return true;
        }
        public bool TryUpdateIfChanged<T>(string key, T newValue, int expireSeconds = -1) where T : class
        {
            var fullKey = BuildKey(key);
            // 总是从Redis获取当前实际值进行比较,确保数据一致性
            string? existingJson = null;
            if (RedisAvailable)
            {
                try
                {
                    var value = _connectionManager.GetDatabase().StringGet(fullKey);
                    if (!value.IsNullOrEmpty)
                        existingJson = value.ToString();
                }
                catch { }
            }
            if (existingJson == null)
            {
                // Redis不可用,检查内存缓存
                if (_options.EnableL1Cache && _memoryCache.TryGetValue(fullKey, out string? cached) && cached != null)
                    existingJson = cached;
                else
                    return false;
            }
            var newJson = _serializer.Serialize(newValue);
            if (existingJson == newJson) return false; // JSON字符串相同,不更新
            // 先写入Redis,成功后再更新内存缓存
            if (RedisAvailable)
            {
                try
                {
                    var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
                    if (!_connectionManager.GetDatabase().StringSet(fullKey, newJson, expiry))
                    {
                        // Redis写入失败
                        _logger.LogWarning("Redis TryUpdateIfChanged写入失败, key={Key}", key);
                        return _options.FallbackToMemory && _options.EnableL1Cache;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Redis TryUpdateIfChanged写入失败, key={Key}", key);
                    return _options.FallbackToMemory && _options.EnableL1Cache;
                }
            }
            // Redis写入成功(或Redis不可用时),更新内存缓存
            if (_options.EnableL1Cache)
            {
                SetMemoryCache(fullKey, newJson, expireSeconds, false);
            }
            return true;
        }
        public string GetOrAdd(string key, string value, int expireSeconds = -1)
        {
            var existing = Get(key);