|
|
@@ -5,19 +5,24 @@ namespace MeloongCore;
|
|
|
|
|
|
public interface IConfigProvider {
|
|
|
/// <summary>
|
|
|
- /// 将原始数据存入缓存。
|
|
|
+ /// 将数据写入缓存。
|
|
|
+ /// <para/> 这不会写入数据,还需要调用 <see cref="Save"/> 以保存。
|
|
|
/// </summary>
|
|
|
- void Set(string key, string value, bool encrypted = false);
|
|
|
+ void SetToCache(string key, string? value, bool encrypted = false);
|
|
|
+ /// <summary>
|
|
|
+ /// 将指定数据从缓存中移除。
|
|
|
+ /// <para/> 这不会写入数据,还需要调用 <see cref="Save"/> 以保存。
|
|
|
+ /// </summary>
|
|
|
+ void RemoveFromCache(string key);
|
|
|
string? Read(string key, string? defaultValue = null, bool encrypted = false);
|
|
|
bool HasValue(string key);
|
|
|
- void Remove(string key);
|
|
|
- void Save();
|
|
|
void ClearCache();
|
|
|
+ void Save();
|
|
|
}
|
|
|
|
|
|
public class JsonConfigProvider : IConfigProvider {
|
|
|
private readonly string filePath;
|
|
|
- public readonly ConcurrentDictionary<string, string?> DecryptedContent = new();
|
|
|
+ private readonly ConcurrentDictionary<string, string?> decryptedContentCache = new();
|
|
|
|
|
|
public JsonConfigProvider(string filePath) {
|
|
|
this.filePath = filePath;
|
|
|
@@ -33,32 +38,32 @@ public class JsonConfigProvider : IConfigProvider {
|
|
|
try {
|
|
|
return FileUtils.Exists(filePath) ? ((JObject?) FileUtils.ReadAsJson(filePath) ?? []) : [];
|
|
|
} catch (Exception ex) {
|
|
|
- Logger.Error(ex, $"读取配置文件失败:{filePath}", LogBehavior.Toast);
|
|
|
+ Logger.Error(ex, $"读取配置文件失败({filePath})", LogBehavior.Toast);
|
|
|
return [];
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- public void Set(string key, string? value, bool encrypted = false) {
|
|
|
+ public void SetToCache(string key, string? value, bool encrypted = false) {
|
|
|
if (encrypted) {
|
|
|
Content.Value[key] = CryptographyUtils.AesEncrypt(value); // UNDONE: 改为识别码
|
|
|
- DecryptedContent[key] = value;
|
|
|
+ decryptedContentCache[key] = value;
|
|
|
} else {
|
|
|
Content.Value[key] = value;
|
|
|
}
|
|
|
}
|
|
|
public string? Read(string key, string? defaultValue = null, bool encrypted = false) {
|
|
|
if (!Content.Value.ContainsKey(key)) return defaultValue;
|
|
|
- if (encrypted && DecryptedContent.TryGetValue(key, out var cachedValue)) return cachedValue; // 读取缓存中已经解密的值
|
|
|
+ if (encrypted && decryptedContentCache.TryGetValue(key, out var cachedValue)) return cachedValue; // 读取缓存中已经解密的值
|
|
|
// 读取值
|
|
|
- string value = Content.Value[key]!.ToString();
|
|
|
+ string? value = Content.Value[key]?.ToString();
|
|
|
if (!encrypted) return value;
|
|
|
// 解密读取的数据
|
|
|
try {
|
|
|
- return DecryptedContent.GetOrAdd(key, _ => CryptographyUtils.AesDecrypt(value));
|
|
|
+ return decryptedContentCache.GetOrAdd(key, _ => CryptographyUtils.AesDecrypt(value));
|
|
|
} catch (CryptographicException ex) {
|
|
|
- Logger.Error(ex, $"解密设置失败,该设置将被重置:{key}", LogBehavior.Toast);
|
|
|
- Remove(key);
|
|
|
+ Logger.Error(ex, $"解密配置失败({key})", LogBehavior.Toast);
|
|
|
+ RemoveFromCache(key);
|
|
|
Save();
|
|
|
return defaultValue;
|
|
|
}
|
|
|
@@ -66,16 +71,16 @@ public class JsonConfigProvider : IConfigProvider {
|
|
|
|
|
|
public bool HasValue(string key)
|
|
|
=> Content.Value.ContainsKey(key);
|
|
|
- public void Remove(string key) {
|
|
|
+ public void RemoveFromCache(string key) {
|
|
|
Content.Value.Remove(key);
|
|
|
- DecryptedContent.TryRemove(key, out _);
|
|
|
+ decryptedContentCache.TryRemove(key, out _);
|
|
|
}
|
|
|
- public void Save()
|
|
|
- => FileUtils.Write(filePath, Content.Value.ToString(Formatting.Indented));
|
|
|
public void ClearCache() {
|
|
|
ResetContent();
|
|
|
- DecryptedContent.Clear();
|
|
|
+ decryptedContentCache.Clear();
|
|
|
}
|
|
|
+ public void Save()
|
|
|
+ => FileUtils.Write(filePath, Content.Value.ToString(Formatting.Indented));
|
|
|
}
|
|
|
|
|
|
public static class Configs {
|
|
|
@@ -88,7 +93,7 @@ public static class Configs {
|
|
|
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) {
|
|
|
+public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaultProvider = null, bool encrypted = false) where T : IEquatable<T> {
|
|
|
private IConfigProvider GetProvider(IConfigProvider? provider)
|
|
|
=> provider ?? defaultProvider ?? Configs.AppData;
|
|
|
|
|
|
@@ -97,29 +102,42 @@ public class ConfigEntry<T>(string key, T? defaultValue, IConfigProvider? defaul
|
|
|
/// </summary>
|
|
|
public event Action<T?>? OnChanged;
|
|
|
|
|
|
-
|
|
|
- /*public T? Value {
|
|
|
- get {
|
|
|
- return GetProvider(null).Read(key, JToken.FromObject(defaultValue))?.ToObject<T>();
|
|
|
- }
|
|
|
- set {
|
|
|
- var provider = GetProvider(null);
|
|
|
- var oldValue = Value;
|
|
|
- if (!Equals(oldValue, value)) {
|
|
|
- provider.Write(key, JToken.FromObject(value));
|
|
|
- OnChanged?.Invoke(value);
|
|
|
- }
|
|
|
+ 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;
|
|
|
+ var type = typeof(T);
|
|
|
+ if (type == typeof(string)) return (T) (object) value;
|
|
|
+ if (typeof(JToken).IsAssignableFrom(type)) return (T) (object) value.DeserializeJson()!;
|
|
|
+ return (T) Convert.ChangeType(value, type);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ Logger.Error(ex, $"读取配置项失败({key})", LogBehavior.Toast);
|
|
|
+ return defaultValue;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- public T? GetValue(IConfigProvider? provider = null) {
|
|
|
- return GetProvider(provider).Read(key, JToken.FromObject(defaultValue))?.ToObject<T>();
|
|
|
- }*/
|
|
|
-
|
|
|
- public bool HasValue(IConfigProvider? provider = null)
|
|
|
- => GetProvider(provider).HasValue(key);
|
|
|
- public void Remove(IConfigProvider? provider = null)
|
|
|
- => GetProvider(provider).Remove(key);
|
|
|
- public void Save(IConfigProvider? provider = null)
|
|
|
- => GetProvider(provider).Save();
|
|
|
-}
|
|
|
+ public void Set(T? value, IConfigProvider? providerOverride = null) {
|
|
|
+ var provider = GetProvider(providerOverride);
|
|
|
+ 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, value?.ToString(), encrypted);
|
|
|
+ provider.Save();
|
|
|
+ OnChanged?.Invoke(value);
|
|
|
+ }
|
|
|
+ public bool HasValue(IConfigProvider? providerOverride = null) {
|
|
|
+ var provider = GetProvider(providerOverride);
|
|
|
+ return provider.HasValue(key);
|
|
|
+ }
|
|
|
+ public void Reset(IConfigProvider? providerOverride = null) {
|
|
|
+ var provider = GetProvider(providerOverride);
|
|
|
+ if (!provider.HasValue(key)) return; // 值未设置,无需重置
|
|
|
+ provider.RemoveFromCache(key);
|
|
|
+ provider.Save();
|
|
|
+ OnChanged?.Invoke(defaultValue);
|
|
|
+ }
|
|
|
+ public void Save(IConfigProvider? providerOverride = null) {
|
|
|
+ var provider = GetProvider(providerOverride);
|
|
|
+ provider.Save();
|
|
|
+ }
|
|
|
+}
|