Pārlūkot izejas kodu

新的 Config 存储框架

龙腾猫跃 4 mēneši atpakaļ
vecāks
revīzija
5c44002d5d
1 mainītis faili ar 53 papildinājumiem un 56 dzēšanām
  1. 53 56
      Shared/General/Config.cs

+ 53 - 56
Shared/General/Config.cs

@@ -8,13 +8,13 @@ public interface IConfigProvider {
     /// 将数据写入缓存。
     /// <para/> 这不会写入数据,还需要调用 <see cref="Save"/> 以保存。
     /// </summary>
-    void SetToCache(string key, string? value, bool encrypted);
+    void SetToCache<T>(string key, T? value, bool encrypted);
     /// <summary>
     /// 将指定数据从缓存中移除。
     /// <para/> 这不会写入数据,还需要调用 <see cref="Save"/> 以保存。
     /// </summary>
     void RemoveFromCache(string key);
-    string? Read(string key, string? defaultValue, bool encrypted);
+    T? Read<T>(string key, T? defaultValue, bool encrypted);
     bool HasValue(string key);
     void DiscardCache();
     void Save();
@@ -22,16 +22,17 @@ public interface IConfigProvider {
 
 public class JsonConfigProvider : IConfigProvider {
     private readonly string filePath;
-    private readonly ConcurrentDictionary<string, string?> decryptedContentCache = new();
-
     public JsonConfigProvider(string filePath) {
         this.filePath = filePath;
-        ResetContent();
+        InitJson();
     }
 
-    private Lazy<JObject> content;
-    private void ResetContent() {
-        content = new(() => {
+    // 原始文件缓存
+    private Lazy<JObject> json;
+    // 反序列化后的结果缓存
+    private readonly ConcurrentDictionary<string, object?> cache = new();
+    private void InitJson() {
+        json = new(() => {
             try {
                 return FileUtils.Exists(filePath) ? ((JObject?) FileUtils.ReadAsJson(filePath) ?? []) : [];
             } catch (Exception ex) {
@@ -41,23 +42,34 @@ public class JsonConfigProvider : IConfigProvider {
         });
     }
 
-    public void SetToCache(string key, string? value, bool encrypted) {
-        if (encrypted) {
-            content.Value[key] = CryptographyUtils.AesEncrypt(value); // UNDONE: 改为识别码
-            decryptedContentCache[key] = value;
+    public void SetToCache<T>(string key, T? value, bool encrypted) {
+        cache[key] = value;
+        if (value is null) {
+            // 直接在 JSON 中表示为 null
+            json.Value[key] = JValue.CreateNull();
+        } else if (encrypted) {
+            // 需要加密,在 JSON 中保存密文字符串
+            json.Value[key] = CryptographyUtils.AesEncrypt(value is string str ? str : JsonConvert.SerializeObject(value)); // UNDONE: 加密改为识别码
         } else {
-            content.Value[key] = value;
+            // 用 JToken 保留原始结构
+            json.Value[key] = JToken.FromObject(value);
         }
     }
-    public string? Read(string key, string? defaultValue, bool encrypted) {
-        if (encrypted && decryptedContentCache.TryGetValue(key, out var cachedValue)) return cachedValue; // 读取缓存中已经解密的值
+    public T? Read<T>(string key, T? defaultValue, bool encrypted) {
+        if (cache.TryGetValue(key, out var cachedValue)) return (T?) cachedValue; // 读取缓存
+        if (!json.Value.TryGetValue(key, out var entry)) return defaultValue; // 未找到该键
         try {
-            if (!content.Value.TryGetValue(key, out var result)) return defaultValue;
-            if (encrypted) { // 解密
-                result = CryptographyUtils.AesDecrypt(result?.ToString());
-                decryptedContentCache.TryAdd(key, result?.ToString());
+            T? result;
+            if (entry.Type is JTokenType.Null or JTokenType.Undefined) {
+                result = default;
+            } else if (encrypted) {
+                string plainText = CryptographyUtils.AesDecrypt(entry.Value<string>())!;
+                result = typeof(T) == typeof(string) ? (T) (object) plainText : JsonConvert.DeserializeObject<T>(plainText);
+            } else {
+                result = entry.ToObject<T>();
             }
-            return result?.ToString();
+            cache[key] = result;
+            return result;
         } catch (CryptographicException ex) {
             Logger.Error(ex, $"解密配置失败,该配置将被重置({key})", LogBehavior.Alert);
             RemoveFromCache(key);
@@ -67,17 +79,17 @@ public class JsonConfigProvider : IConfigProvider {
     }
 
     public bool HasValue(string key)
-        => content.Value.ContainsKey(key);
+        => json.Value.ContainsKey(key);
     public void RemoveFromCache(string key) {
-        content.Value.Remove(key);
-        decryptedContentCache.TryRemove(key, out _);
+        json.Value.Remove(key);
+        cache.TryRemove(key, out _);
     }
     public void DiscardCache() {
-        ResetContent();
-        decryptedContentCache.Clear();
+        InitJson();
+        cache.Clear();
     }
     public void Save()
-        => FileUtils.Write(filePath, content.Value.ToString(Formatting.Indented));
+        => FileUtils.Write(filePath, json.Value.ToString(Formatting.Indented));
 }
 
 public static class ConfigUtils {
@@ -87,53 +99,38 @@ public static class ConfigUtils {
     /// 从 secret.json 中读取特定密钥,如果未找到对应密钥则抛出异常。
     /// </summary>
     public static string GetSecret(string key) 
-        => secret.Read(key, null, false)?.ToString() ?? throw new KeyNotFoundException($"Secret not found: {key}");
+        => secret.Read<string>(key, null, false) ?? throw new KeyNotFoundException($"Secret not found: {key}");
     private static readonly JsonConfigProvider secret = new(Path.Combine(Paths.AppData, "secret.json"));
 }
 
 public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaultProvider = null, bool encrypted = false) {
-    private IConfigProvider GetProvider(IConfigProvider? provider)
-        => provider ?? defaultProvider ?? ConfigUtils.AppData;
 
     /// <summary>
     /// 当设置项的值实际被改变时触发,参数为新值。
     /// </summary>
-    public event Action<T?>? Changed;
+    public event Action<T?, IConfigProvider>? Changed;
 
-    public T? Get(IConfigProvider? providerOverride = null) {
-        var provider = GetProvider(providerOverride);
-        if (!provider.HasValue(key)) return defaultValue;
-        string? value = provider.Read(key, defaultValue?.ToString(), encrypted);
-        try {
-            if (value is null) return default;
-            return typeof(T) == typeof(string) ? ((T) (object) value) : JsonConvert.DeserializeObject<T>(value);
-        } catch (Exception ex) {
-            Logger.Error(ex, $"读取配置项失败({key})", LogBehavior.Toast);
-            return defaultValue;
-        }
-    }
+    public IConfigProvider DefaultProvider => defaultProvider ?? ConfigUtils.AppData;
     public void Set(T? value, IConfigProvider? providerOverride = null) {
-        var provider = GetProvider(providerOverride);
+        var provider = providerOverride ?? DefaultProvider;
         T? current = Get(provider);
         if ((current is null && value is null) || (current is not null && value is not null && current.Equals(value))) return; // 值未改变,无需更新
-        provider.SetToCache(key, 
-            typeof(T) == typeof(string) ? (value as string) : JsonConvert.SerializeObject(value), encrypted);
+        provider.SetToCache(key, value, encrypted);
         provider.Save();
-        Changed?.Invoke(value);
-    }
-    public bool HasValue(IConfigProvider? providerOverride = null) {
-        var provider = GetProvider(providerOverride);
-        return provider.HasValue(key);
+        Changed?.Invoke(value, provider);
     }
     public void Reset(IConfigProvider? providerOverride = null) {
-        var provider = GetProvider(providerOverride);
+        var provider = providerOverride ?? DefaultProvider;
         if (!provider.HasValue(key)) return; // 值未设置,无需重置
         provider.RemoveFromCache(key);
         provider.Save();
-        Changed?.Invoke(defaultValue);
-    }
-    public void Save(IConfigProvider? providerOverride = null) {
-        var provider = GetProvider(providerOverride);
-        provider.Save();
+        Changed?.Invoke(defaultValue, provider);
     }
+    public T? Get(IConfigProvider? providerOverride = null)
+        => (providerOverride ?? DefaultProvider).Read(key, defaultValue, encrypted);
+    public bool HasValue(IConfigProvider? providerOverride = null)
+        => (providerOverride ?? DefaultProvider).HasValue(key);
+    public void Save(IConfigProvider? providerOverride = null) 
+        => (providerOverride ?? DefaultProvider).Save();
+    public static implicit operator T?(ConfigEntry<T> entry) => entry.Get();
 }