wanshenmean
2026-03-06 aefdecd0aa3226b7d00d1dc764241b82658b3be8
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_RedisService/Cache/RedisCacheService.cs
@@ -70,6 +70,209 @@
            Db.KeyDelete(redisKeys);
        }
        #region 删除扩展方法
        public string? RemoveAndGet(string key)
        {
            var fullKey = BuildKey(key);
            var value = Db.StringGet(fullKey);
            if (!value.IsNullOrEmpty)
            {
                Db.KeyDelete(fullKey);
                return value.ToString();
            }
            return null;
        }
        public T? RemoveAndGet<T>(string key) where T : class
        {
            var fullKey = BuildKey(key);
            var value = Db.StringGet(fullKey);
            if (!value.IsNullOrEmpty)
            {
                Db.KeyDelete(fullKey);
                return _serializer.Deserialize<T>(value!);
            }
            return default;
        }
        public int RemoveByPrefix(string prefix)
        {
            var fullPrefix = BuildKey(prefix);
            var server = Db.Multiplexer.GetServer(Db.Multiplexer.GetEndPoints().First());
            var keys = server.Keys(pattern: $"{fullPrefix}*").ToArray();
            if (keys.Length == 0) return 0;
            return (int)Db.KeyDelete(keys);
        }
        public int RemoveByPattern(string pattern)
        {
            var fullPattern = BuildKey(pattern).Replace("*", ""); // 保留用户传入的通配符
            var server = Db.Multiplexer.GetServer(Db.Multiplexer.GetEndPoints().First());
            var keys = server.Keys(pattern: $"{_options.KeyPrefix}{pattern}").ToArray();
            if (keys.Length == 0) return 0;
            return (int)Db.KeyDelete(keys);
        }
        public int RemoveAll(IEnumerable<string> keys)
        {
            if (keys == null) return 0;
            var redisKeys = keys.Select(k => (RedisKey)BuildKey(k)).ToArray();
            return (int)Db.KeyDelete(redisKeys);
        }
        public int RemoveWhere(Func<string, bool> predicate)
        {
            if (predicate == null) return 0;
            var server = Db.Multiplexer.GetServer(Db.Multiplexer.GetEndPoints().First());
            var keys = server.Keys(pattern: $"{_options.KeyPrefix}*")
                .Where(k => predicate(k.ToString().Replace(_options.KeyPrefix, "")))
                .ToArray();
            if (keys.Length == 0) return 0;
            return (int)Db.KeyDelete(keys);
        }
        #endregion
        #region 添加和修改扩展方法
        public void AddAll(IDictionary<string, string> items, int expireSeconds = -1)
        {
            if (items == null) return;
            var batch = Db.CreateBatch();
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            foreach (var item in items)
            {
                batch.StringSetAsync(BuildKey(item.Key), item.Value, expiry);
            }
            batch.Execute();
        }
        public void AddAllObjects(IDictionary<string, object> items, int expireSeconds = -1)
        {
            if (items == null) return;
            var batch = Db.CreateBatch();
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            foreach (var item in items)
            {
                batch.StringSetAsync(BuildKey(item.Key), _serializer.Serialize(item.Value), expiry);
            }
            batch.Execute();
        }
        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
        {
            var fullKey = BuildKey(key);
            if (!Db.KeyExists(fullKey)) return false;
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, _serializer.Serialize(newValue), expiry, When.Exists);
        }
        public string? GetAndRefresh(string key, int expireSeconds)
        {
            var fullKey = BuildKey(key);
            var value = Db.StringGet(fullKey);
            if (!value.IsNullOrEmpty)
            {
                Db.KeyExpire(fullKey, TimeSpan.FromSeconds(expireSeconds));
                return value.ToString();
            }
            return null;
        }
        public T? GetAndRefresh<T>(string key, int expireSeconds) where T : class
        {
            var fullKey = BuildKey(key);
            var value = Db.StringGet(fullKey);
            if (!value.IsNullOrEmpty)
            {
                Db.KeyExpire(fullKey, TimeSpan.FromSeconds(expireSeconds));
                return _serializer.Deserialize<T>(value!);
            }
            return default;
        }
        public bool RefreshExpire(string key, int expireSeconds)
        {
            return Db.KeyExpire(BuildKey(key), TimeSpan.FromSeconds(expireSeconds));
        }
        public bool ExpireIn(string key, int seconds)
        {
            return Db.KeyExpire(BuildKey(key), TimeSpan.FromSeconds(seconds));
        }
        public bool ExpireAt(string key, DateTime expireTime)
        {
            return Db.KeyExpire(BuildKey(key), expireTime);
        }
        public long? GetExpire(string key)
        {
            var ttl = Db.KeyTimeToLive(BuildKey(key));
            return ttl.HasValue ? (long)ttl.Value.TotalSeconds : null;
        }
        public bool AddIfNotExists(string key, string value, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, value, expiry, When.NotExists);
        }
        public bool AddIfNotExists<T>(string key, T value, int expireSeconds = -1) where T : class
        {
            var fullKey = BuildKey(key);
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, _serializer.Serialize(value), expiry, When.NotExists);
        }
        public string? GetAndSet(string key, string newValue, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            var oldValue = Db.StringGetSet(fullKey, newValue);
            if (expireSeconds > 0)
            {
                Db.KeyExpire(fullKey, expiry);
            }
            return oldValue.IsNullOrEmpty ? null : oldValue.ToString();
        }
        public T? GetAndSet<T>(string key, T newValue, int expireSeconds = -1) where T : class
        {
            var fullKey = BuildKey(key);
            var serialized = _serializer.Serialize(newValue);
            var oldValue = Db.StringGetSet(fullKey, serialized);
            if (expireSeconds > 0)
            {
                Db.KeyExpire(fullKey, TimeSpan.FromSeconds(expireSeconds));
            }
            return oldValue.IsNullOrEmpty ? default : _serializer.Deserialize<T>(oldValue!);
        }
        public long Increment(string key, long value = 1)
        {
            return Db.StringIncrement(BuildKey(key), value);
        }
        public long Decrement(string key, long value = 1)
        {
            return Db.StringDecrement(BuildKey(key), value);
        }
        public long Append(string key, string value)
        {
            return Db.StringAppend(BuildKey(key), value);
        }
        #endregion
        public T? Get<T>(string key) where T : class
        {
            var value = Db.StringGet(BuildKey(key));
@@ -139,6 +342,57 @@
            return Db.StringSet(fullKey, newValue, expiry, When.Exists);
        }
        public bool TryUpdateIfChanged(string key, string newValue, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);
            var existing = Db.StringGet(fullKey);
            if (existing.IsNullOrEmpty) return false;
            if (existing.ToString() == newValue) return false; // 值相同,不更新
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, newValue, expiry, When.Exists);
        }
        public bool TryUpdateIfChanged<T>(string key, T newValue, int expireSeconds = -1) where T : class
        {
            var fullKey = BuildKey(key);
            var existing = Db.StringGet(fullKey);
            if (existing.IsNullOrEmpty) return false;
            var newJson = _serializer.Serialize(newValue);
            if (existing.ToString() == newJson) return false; // JSON字符串相同,不更新
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, newJson, expiry, When.Exists);
        }
        public bool TrySafeUpdate<T>(
            string key,
            T newValue,
            object? expectedVersion,
            Func<T, object?> versionExtractor,
            int expireSeconds = -1) where T : class
        {
            var fullKey = BuildKey(key);
            var existing = Db.StringGet(fullKey);
            if (existing.IsNullOrEmpty) return false;
            // 反序列化现有值
            var existingValue = _serializer.Deserialize<T>(existing.ToString()!);
            if (existingValue == null) return false;
            // 检查版本是否匹配
            var currentVersion = versionExtractor(existingValue);
            if (!Equals(currentVersion, expectedVersion))
            {
                return false; // 版本不匹配,拒绝更新
            }
            // 版本匹配,执行更新
            var newJson = _serializer.Serialize(newValue);
            var expiry = expireSeconds > 0 ? TimeSpan.FromSeconds(expireSeconds) : (TimeSpan?)null;
            return Db.StringSet(fullKey, newJson, expiry, When.Exists);
        }
        public string GetOrAdd(string key, string value, int expireSeconds = -1)
        {
            var fullKey = BuildKey(key);