diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/AbpSettingManagementDbProperties.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/AbpSettingManagementDbProperties.cs new file mode 100644 index 0000000..82c3574 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/AbpSettingManagementDbProperties.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Data; + +namespace NPin.Framework.SettingManagement.Domain; + +/// +/// Abp设置管理器数据库参数 +/// +public class AbpSettingManagementDbProperties +{ + public static string DbTablePrefix { get; set; } = AbpCommonDbProperties.DbTablePrefix; + public static string? DbSchema { get; set; } = AbpCommonDbProperties.DbSchema; + public const string ConnectionStringName = "AbpSettingManagement"; +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Caching/SettingCacheItem.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Caching/SettingCacheItem.cs new file mode 100644 index 0000000..76320b0 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Caching/SettingCacheItem.cs @@ -0,0 +1,47 @@ +using JetBrains.Annotations; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Text.Formatting; + +namespace NPin.Framework.SettingManagement.Domain.Caching; + +/// +/// 设置缓存项 +/// 忽略多租户 +/// +[Serializable] +[IgnoreMultiTenancy] +public class SettingCacheItem +{ + /// + /// 缓存Key格式化参数 + /// pn: providerName + /// pk: providerKey + /// n: name + /// + private const string CacheKeyFormat = "pn:{0},pk:{1},n:{2}"; + + /// + /// 值 + /// + public string? Value { get; set; } + + public SettingCacheItem() + { + } + + public SettingCacheItem(string? value) + { + Value = value; + } + + public static string CalculateCacheKey(string name, string providerName, string providerKey) + { + return string.Format(CacheKeyFormat, providerName, providerKey, name); + } + + public static string? GetSettingNameFormCacheKeyOrNull(string cacheKey) + { + var result = FormattedStringValueExtracter.Extract(cacheKey, CacheKeyFormat, true); + return result.IsMatch ? result.Matches.Last().Value : null; + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Caching/SettingCacheItemInvalidator.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Caching/SettingCacheItemInvalidator.cs new file mode 100644 index 0000000..71c030b --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Caching/SettingCacheItemInvalidator.cs @@ -0,0 +1,38 @@ +using NPin.Framework.SettingManagement.Domain.Entities; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus; + +namespace NPin.Framework.SettingManagement.Domain.Caching; + +/// +/// 设置缓存项 过期 +/// 变动时 过期 +/// +public class SettingCacheItemInvalidator : ILocalEventHandler>, + ITransientDependency +{ + protected IDistributedCache Cache { get; } + + public SettingCacheItemInvalidator(IDistributedCache cache) + { + Cache = cache; + } + + public virtual async Task HandleEventAsync(EntityChangedEventData eventData) + { + var entity = eventData.Entity; + var cacheKey = CalculateCacheKey( + entity.Name, + entity.ProviderName, + entity.ProviderKey); + + await Cache.RemoveAsync(cacheKey, considerUow: true); + } + + protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) + { + return SettingCacheItem.CalculateCacheKey(name, providerName, providerKey); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Entities/SettingEntity.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Entities/SettingEntity.cs new file mode 100644 index 0000000..4fffffb --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Entities/SettingEntity.cs @@ -0,0 +1,46 @@ +using JetBrains.Annotations; +using Volo.Abp.Domain.Entities; + +namespace NPin.Framework.SettingManagement.Domain.Entities; + +public class SettingEntity: Entity, IAggregateRoot +{ + [NotNull] + public virtual string Name { get; protected set; } + + [NotNull] + public virtual string Value { get; internal set; } + + [CanBeNull] + public virtual string ProviderName { get; protected set; } + + [CanBeNull] + public virtual string ProviderKey { get; protected set; } + + public SettingEntity() + { + + } + + public SettingEntity( + Guid id, + [NotNull] string name, + [NotNull] string value, + [CanBeNull] string providerName = null, + [CanBeNull] string providerKey = null) + { + Check.NotNull(name, nameof(name)); + Check.NotNull(value, nameof(value)); + + Id = id; + Name = name; + Value = value; + ProviderName = providerName; + ProviderKey = providerKey; + } + + public override string ToString() + { + return $"{base.ToString()}, Name = {Name}, Value = {Value}, ProviderName = {ProviderName}, ProviderKey = {ProviderKey}"; + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/ISettingManager.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/ISettingManager.cs new file mode 100644 index 0000000..d527b3d --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/ISettingManager.cs @@ -0,0 +1,16 @@ +using JetBrains.Annotations; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +/// +/// 设置管理器 +/// +public interface ISettingManager +{ + Task GetOrNullAsync([NotNull] string name, [NotNull] string providerName, string? providerKey, bool fallback = true); + + Task> GetAllAsync([NotNull] string providerName, string? providerKey, bool fallback = true); + + Task SetAsync([NotNull] string name, string? value, [NotNull] string providerName, string? providerKey, bool forceToSet = false); +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/ISettingRepository.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/ISettingRepository.cs new file mode 100644 index 0000000..768c36a --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/ISettingRepository.cs @@ -0,0 +1,24 @@ +using NPin.Framework.SettingManagement.Domain.Entities; +using Volo.Abp.Domain.Repositories; + +namespace NPin.Framework.SettingManagement.Domain; + +public interface ISettingRepository: IBasicRepository +{ + Task FindAsync( + string name, + string providerName, + string providerKey, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + string providerName, + string providerKey, + CancellationToken cancellationToken = default); + + Task> GetListAsync( + string[] names, + string providerName, + string providerKey, + CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/NPin.Framework.SettingManagement.Domain.csproj b/module/setting-management/NPin.Framework.SettingManagement.Domain/NPin.Framework.SettingManagement.Domain.csproj new file mode 100644 index 0000000..1aaa0fc --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/NPin.Framework.SettingManagement.Domain.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/NPinFrameworkSettingManagementDomainModule.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/NPinFrameworkSettingManagementDomainModule.cs new file mode 100644 index 0000000..967f544 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/NPinFrameworkSettingManagementDomainModule.cs @@ -0,0 +1,28 @@ +using NPin.Framework.SettingManagement.Domain.Options; +using Volo.Abp.Caching; +using Volo.Abp.Domain; +using Volo.Abp.SettingManagement; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +[DependsOn( + typeof(AbpSettingsModule), + typeof(AbpDddDomainModule), + typeof(AbpSettingManagementDomainSharedModule), + typeof(AbpCachingModule) +)] +public class NPinFrameworkSettingManagementDomainModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.Providers.Add(); + options.Providers.Add(); + options.Providers.Add(); + options.Providers.Add(); + options.Providers.Add(); + }); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Options/SettingManagementOptions.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Options/SettingManagementOptions.cs new file mode 100644 index 0000000..5ce2180 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Options/SettingManagementOptions.cs @@ -0,0 +1,13 @@ +using Volo.Abp.Collections; + +namespace NPin.Framework.SettingManagement.Domain.Options; + +public class SettingManagementOptions +{ + public ITypeList Providers { get; } + + public SettingManagementOptions() + { + Providers = new TypeList(); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/ConfigurationSettingManagementProvider.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/ConfigurationSettingManagementProvider.cs new file mode 100644 index 0000000..dcade41 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/ConfigurationSettingManagementProvider.cs @@ -0,0 +1,32 @@ +using Microsoft.Extensions.Configuration; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +public class ConfigurationSettingManagementProvider: ISettingManagementProvider, ITransientDependency +{ + public string Name => ConfigurationSettingValueProvider.ProviderName; + + protected IConfiguration Configuration { get; } + + public ConfigurationSettingManagementProvider(IConfiguration configuration) + { + Configuration = configuration; + } + + public virtual Task GetOrNullAsync(SettingDefinition setting, string providerKey) + { + return Task.FromResult(Configuration[ConfigurationSettingValueProvider.ConfigurationNamePrefix + setting.Name]); + } + + public Task SetAsync(SettingDefinition setting, string value, string providerKey) + { + throw new AbpException($"Can not set a setting value to the application configuration."); + } + + public Task ClearAsync(SettingDefinition setting, string providerKey) + { + throw new AbpException($"Can not clear provider to the application configuration."); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/DefaultValueSettingManagementProvider.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/DefaultValueSettingManagementProvider.cs new file mode 100644 index 0000000..c857036 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/DefaultValueSettingManagementProvider.cs @@ -0,0 +1,26 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +public class DefaultValueSettingManagementProvider : ISettingManagementProvider, ISingletonDependency +{ + public string Name => DefaultValueSettingValueProvider.ProviderName; + + public virtual Task GetOrNullAsync(SettingDefinition setting, string providerKey) + { + return Task.FromResult(setting.DefaultValue); + } + + public Task SetAsync(SettingDefinition setting, string value, string providerKey) + { + throw new AbpException( + $"Can not set default value of a setting. It is only possible while defining the setting in a {typeof(ISettingDefinitionProvider)} implementation."); + } + + public Task ClearAsync(SettingDefinition setting, string providerKey) + { + throw new AbpException( + $"Can not clear default value of a setting. It is only possible while defining the setting in a {typeof(ISettingDefinitionProvider)} implementation."); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/ConfigurationValueSettingManagerExtensions.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/ConfigurationValueSettingManagerExtensions.cs new file mode 100644 index 0000000..3c5563d --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/ConfigurationValueSettingManagerExtensions.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain.Provider.Extensions; + +public static class ConfigurationValueSettingManagerExtensions +{ + public static Task GetOrNullConfigurationAsync(this ISettingManager settingManager, [NotNull] string name, + bool fallback = true) + { + return settingManager.GetOrNullAsync(name, ConfigurationSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllConfigurationAsync(this ISettingManager settingManager, + bool fallback = true) + { + return settingManager.GetAllAsync(ConfigurationSettingValueProvider.ProviderName, null, fallback); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/DefaultValueSettingManagerExtensions.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/DefaultValueSettingManagerExtensions.cs new file mode 100644 index 0000000..6c1e352 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/DefaultValueSettingManagerExtensions.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain.Provider.Extensions; + +public static class DefaultValueSettingManagerExtensions +{ + public static Task GetOrNullDefaultAsync(this ISettingManager settingManager, [NotNull] string name, + bool fallback = true) + { + return settingManager.GetOrNullAsync(name, DefaultValueSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllDefaultAsync(this ISettingManager settingManager, bool fallback = true) + { + return settingManager.GetAllAsync(DefaultValueSettingValueProvider.ProviderName, null, fallback); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/GlobalSettingManagerExtensions.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/GlobalSettingManagerExtensions.cs new file mode 100644 index 0000000..2c27d46 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/GlobalSettingManagerExtensions.cs @@ -0,0 +1,24 @@ +using JetBrains.Annotations; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain.Provider.Extensions; + +public static class GlobalSettingManagerExtensions +{ + public static Task GetOrNullGlobalAsync(this ISettingManager settingManager, [NotNull] string name, + bool fallback = true) + { + return settingManager.GetOrNullAsync(name, GlobalSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllGlobalAsync(this ISettingManager settingManager, bool fallback = true) + { + return settingManager.GetAllAsync(GlobalSettingValueProvider.ProviderName, null, fallback); + } + + public static Task SetGlobalAsync(this ISettingManager settingManager, [NotNull] string name, + string? value) + { + return settingManager.SetAsync(name, value, GlobalSettingValueProvider.ProviderName, null); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/TenantSettingManagerExtensions.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/TenantSettingManagerExtensions.cs new file mode 100644 index 0000000..3ac43e3 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/TenantSettingManagerExtensions.cs @@ -0,0 +1,53 @@ +using JetBrains.Annotations; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain.Provider.Extensions; + +public static class TenantSettingManagerExtensions +{ + public static Task GetOrNullForTenantAsync(this ISettingManager settingManager, [NotNull] string name, + Guid tenantId, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, TenantSettingValueProvider.ProviderName, tenantId.ToString(), + fallback); + } + + public static Task GetOrNullForCurrentTenantAsync(this ISettingManager settingManager, + [NotNull] string name, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, TenantSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllForTenantAsync(this ISettingManager settingManager, Guid tenantId, + bool fallback = true) + { + return settingManager.GetAllAsync(TenantSettingValueProvider.ProviderName, tenantId.ToString(), fallback); + } + + public static Task> GetAllForCurrentTenantAsync(this ISettingManager settingManager, + bool fallback = true) + { + return settingManager.GetAllAsync(TenantSettingValueProvider.ProviderName, null, fallback); + } + + public static Task SetForTenantAsync(this ISettingManager settingManager, Guid tenantId, [NotNull] string name, + string? value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, TenantSettingValueProvider.ProviderName, tenantId.ToString(), + forceToSet); + } + + public static Task SetForCurrentTenantAsync(this ISettingManager settingManager, [NotNull] string name, + string? value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, TenantSettingValueProvider.ProviderName, null, forceToSet); + } + + public static Task SetForTenantOrGlobalAsync(this ISettingManager settingManager, Guid? tenantId, + [NotNull] string name, string? value, bool forceToSet = false) + { + return tenantId.HasValue + ? settingManager.SetForTenantAsync(tenantId.Value, name, value, forceToSet) + : settingManager.SetGlobalAsync(name, value); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/UserSettingManagerExtensions.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/UserSettingManagerExtensions.cs new file mode 100644 index 0000000..2052c97 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/Extensions/UserSettingManagerExtensions.cs @@ -0,0 +1,44 @@ +using JetBrains.Annotations; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain.Provider.Extensions; + +public static class UserSettingManagerExtensions +{ + public static Task GetOrNullForUserAsync(this ISettingManager settingManager, [NotNull] string name, + Guid userId, bool fallback = true) + { + return settingManager.GetOrNullAsync(name, UserSettingValueProvider.ProviderName, userId.ToString(), fallback); + } + + public static Task GetOrNullForCurrentUserAsync(this ISettingManager settingManager, [NotNull] string name, + bool fallback = true) + { + return settingManager.GetOrNullAsync(name, UserSettingValueProvider.ProviderName, null, fallback); + } + + public static Task> GetAllForUserAsync(this ISettingManager settingManager, Guid userId, + bool fallback = true) + { + return settingManager.GetAllAsync(UserSettingValueProvider.ProviderName, userId.ToString(), fallback); + } + + public static Task> GetAllForCurrentUserAsync(this ISettingManager settingManager, + bool fallback = true) + { + return settingManager.GetAllAsync(UserSettingValueProvider.ProviderName, null, fallback); + } + + public static Task SetForUserAsync(this ISettingManager settingManager, Guid userId, [NotNull] string name, + string? value, bool forceToSet = false) + { + return settingManager.SetAsync(name, value, UserSettingValueProvider.ProviderName, userId.ToString(), + forceToSet); + } + + public static Task SetForCurrentUserAsync(this ISettingManager settingManager, [NotNull] string name, string? value, + bool forceToSet = false) + { + return settingManager.SetAsync(name, value, UserSettingValueProvider.ProviderName, null, forceToSet); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/GlobalSettingManagementProvider.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/GlobalSettingManagementProvider.cs new file mode 100644 index 0000000..87567bf --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/GlobalSettingManagementProvider.cs @@ -0,0 +1,18 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +public class GlobalSettingManagementProvider: SettingManagementProvider, ITransientDependency +{ + public override string Name => GlobalSettingValueProvider.ProviderName; + + public GlobalSettingManagementProvider(ISettingManagementStore settingManagementStore) : base(settingManagementStore) + { + } + + protected override string? NormalizeProviderKey(string providerKey) + { + return null; + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/ISettingManagementProvider.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/ISettingManagementProvider.cs new file mode 100644 index 0000000..414c916 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/ISettingManagementProvider.cs @@ -0,0 +1,34 @@ +using JetBrains.Annotations; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +public interface ISettingManagementProvider +{ + string Name { get; } + + /// + /// 获取配置项 + /// + /// + /// + /// + Task GetOrNullAsync([NotNull] SettingDefinition setting, [CanBeNull] string providerKey); + + /// + /// 设置配置数据 + /// + /// + /// + /// + /// + Task SetAsync([NotNull] SettingDefinition setting, [NotNull] string value, [CanBeNull] string providerKey); + + /// + /// 清除配置项 + /// + /// + /// + /// + Task ClearAsync([NotNull] SettingDefinition setting, [CanBeNull] string providerKey); +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/TenantSettingManagementProvider.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/TenantSettingManagementProvider.cs new file mode 100644 index 0000000..2f17537 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/TenantSettingManagementProvider.cs @@ -0,0 +1,28 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +public class TenantSettingManagementProvider : SettingManagementProvider, ITransientDependency +{ + public override string Name => TenantSettingValueProvider.ProviderName; + + protected ICurrentTenant CurrentTenant { get; } + + public TenantSettingManagementProvider(ISettingManagementStore settingManagementStore, ICurrentTenant currentTenant) + : base(settingManagementStore) + { + CurrentTenant = currentTenant; + } + + protected override string? NormalizeProviderKey(string? providerKey) + { + if (providerKey != null) + { + return providerKey; + } + + return CurrentTenant.Id?.ToString(); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/UserSettingManagementProvider.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/UserSettingManagementProvider.cs new file mode 100644 index 0000000..46509e4 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Provider/UserSettingManagementProvider.cs @@ -0,0 +1,28 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; +using Volo.Abp.Users; + +namespace NPin.Framework.SettingManagement.Domain; + +public class UserSettingManagementProvider : SettingManagementProvider, ITransientDependency +{ + public override string Name => UserSettingValueProvider.ProviderName; + + protected ICurrentUser CurrentUser { get; } + + public UserSettingManagementProvider(ISettingManagementStore settingManagementStore, ICurrentUser currentUser) : + base(settingManagementStore) + { + CurrentUser = currentUser; + } + + protected override string? NormalizeProviderKey(string? providerKey) + { + if (providerKey != null) + { + return providerKey; + } + + return CurrentUser?.Id.ToString(); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/SettingManagementProvider.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/SettingManagementProvider.cs new file mode 100644 index 0000000..3118321 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/SettingManagementProvider.cs @@ -0,0 +1,38 @@ +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +/// +/// 设置管理器提供者 +/// +public abstract class SettingManagementProvider : ISettingManagementProvider +{ + public abstract string Name { get; } + + protected ISettingManagementStore SettingManagementStore { get; } + + public SettingManagementProvider(ISettingManagementStore settingManagementStore) + { + SettingManagementStore = settingManagementStore; + } + + public virtual async Task GetOrNullAsync(SettingDefinition setting, string providerKey) + { + return await SettingManagementStore.GetOrNullAsync(setting.Name, Name, NormalizeProviderKey(providerKey)); + } + + public virtual async Task SetAsync(SettingDefinition setting, string value, string providerKey) + { + await SettingManagementStore.SetAsync(setting.Name, value, Name, NormalizeProviderKey(providerKey)); + } + + public virtual async Task ClearAsync(SettingDefinition setting, string providerKey) + { + await SettingManagementStore.DeleteAsync(setting.Name, Name, NormalizeProviderKey(providerKey)); + } + + protected virtual string? NormalizeProviderKey(string? providerKey) + { + return providerKey; + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/SettingManager.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/SettingManager.cs new file mode 100644 index 0000000..262f981 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/SettingManager.cs @@ -0,0 +1,200 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using NPin.Framework.SettingManagement.Domain.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +public class SettingManager : ISettingManager, ISingletonDependency +{ + private readonly Lazy> _lazyProviders; + protected ISettingDefinitionManager SettingDefinitionManager { get; } + protected ISettingEncryptionService SettingEncryptionService { get; } + protected List Providers => _lazyProviders.Value; + protected SettingManagementOptions Options { get; } + + public SettingManager( + IServiceProvider serviceProvider, + ISettingDefinitionManager settingDefinitionManager, + ISettingEncryptionService settingEncryptionService, + IOptions options) + { + SettingDefinitionManager = settingDefinitionManager; + SettingEncryptionService = settingEncryptionService; + Options = options.Value; + + // TODO use IServiceScopeFactory and create a scope ? + _lazyProviders = new Lazy>( + () => Options + .Providers + .Select(c => serviceProvider.GetRequiredService(c) as ISettingManagementProvider) + .ToList(), + true + ); + } + + public virtual Task GetOrNullAsync(string name, string providerName, string? providerKey, + bool fallback = true) + { + Check.NotNull(name, nameof(name)); + Check.NotNull(providerName, nameof(providerName)); + + return GetOrNullInternalAsync(name, providerName, providerKey, fallback); + } + + public virtual async Task> GetAllAsync(string providerName, string? providerKey, + bool fallback = true) + { + Check.NotNull(providerName, nameof(providerName)); + + var settingDefinitions = await SettingDefinitionManager.GetAllAsync(); + var providers = Enumerable.Reverse(Providers) + .SkipWhile(c => c.Name != providerName); + + if (!fallback) + { + providers = providers.TakeWhile(c => c.Name == providerName); + } + + var providerList = providers.Reverse().ToList(); + + if (!providerList.Any()) + { + return new List(); + } + + var settingValues = new Dictionary(); + + foreach (var setting in settingDefinitions) + { + string value = null; + + if (setting.IsInherited) + { + foreach (var provider in providerList) + { + var providerValue = await provider.GetOrNullAsync( + setting, + provider.Name == providerName ? providerKey : null + ); + if (providerValue != null) + { + value = providerValue; + } + } + } + else + { + value = await providerList[0].GetOrNullAsync( + setting, + providerKey + ); + } + + if (setting.IsEncrypted) + { + value = SettingEncryptionService.Decrypt(setting, value); + } + + if (value != null) + { + settingValues[setting.Name] = new SettingValue(setting.Name, value); + } + } + + return settingValues.Values.ToList(); + } + + public virtual async Task SetAsync(string name, string? value, string providerName, string? providerKey, + bool forceToSet = false) + { + Check.NotNull(name, nameof(name)); + Check.NotNull(providerName, nameof(providerName)); + + var setting = await SettingDefinitionManager.GetAsync(name); + + var providers = Enumerable + .Reverse(Providers) + .SkipWhile(p => p.Name != providerName) + .ToList(); + + if (!providers.Any()) + { + return; + } + + if (setting.IsEncrypted) + { + value = SettingEncryptionService.Encrypt(setting, value); + } + + if (providers.Count > 1 && !forceToSet && setting.IsInherited && value != null) + { + var fallbackValue = await GetOrNullInternalAsync(name, providers[1].Name, null); + if (fallbackValue == value) + { + //Clear the value if it's same as it's fallback value + value = null; + } + } + + providers = providers + .TakeWhile(p => p.Name == providerName) + .ToList(); //Getting list for case of there are more than one provider with same providerName + + if (value == null) + { + foreach (var provider in providers) + { + await provider.ClearAsync(setting, providerKey); + } + } + else + { + foreach (var provider in providers) + { + await provider.SetAsync(setting, value, providerKey); + } + } + } + + protected virtual async Task GetOrNullInternalAsync(string name, string? providerName, string? providerKey, + bool fallback = true) + { + var setting = await SettingDefinitionManager.GetAsync(name); + var providers = Enumerable + .Reverse(Providers); + + if (providerName != null) + { + providers = providers.SkipWhile(c => c.Name != providerName); + } + + if (!fallback || !setting.IsInherited) + { + providers = providers.TakeWhile(c => c.Name == providerName); + } + + string value = null; + foreach (var provider in providers) + { + value = await provider.GetOrNullAsync( + setting, + provider.Name == providerName ? providerKey : null + ); + + if (value != null) + { + break; + } + } + + if (setting.IsEncrypted) + { + value = SettingEncryptionService.Decrypt(setting, value); + } + + return value; + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/ISettingManagementStore.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/ISettingManagementStore.cs new file mode 100644 index 0000000..83c1c46 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/ISettingManagementStore.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +/// +/// 设置存储器 +/// +public interface ISettingManagementStore +{ + Task GetOrNullAsync(string name, string providerName, string providerKey); + + Task> GetListAsync(string providerName, string providerKey); + + Task> GetListAsync(string[] names, string providerName, string providerKey); + + Task SetAsync(string name, string value, string providerName, string providerKey); + + Task DeleteAsync(string name, string providerName, string providerKey); +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/SettingManagementStore.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/SettingManagementStore.cs new file mode 100644 index 0000000..2028c07 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/SettingManagementStore.cs @@ -0,0 +1,215 @@ +using NPin.Framework.SettingManagement.Domain.Caching; +using NPin.Framework.SettingManagement.Domain.Entities; +using Volo.Abp.Caching; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Guids; +using Volo.Abp.Settings; +using Volo.Abp.Uow; + +namespace NPin.Framework.SettingManagement.Domain; + +public class SettingManagementStore : ISettingManagementStore, ITransientDependency +{ + protected IDistributedCache Cache { get; } + protected ISettingDefinitionManager SettingDefinitionManager { get; } + protected ISettingRepository SettingRepository { get; } + protected IGuidGenerator GuidGenerator { get; } + + public SettingManagementStore(IDistributedCache cache, + ISettingDefinitionManager settingDefinitionManager, ISettingRepository settingRepository, + IGuidGenerator guidGenerator) + { + Cache = cache; + SettingDefinitionManager = settingDefinitionManager; + SettingRepository = settingRepository; + GuidGenerator = guidGenerator; + } + + [UnitOfWork] + public virtual async Task GetOrNullAsync(string name, string providerName, string providerKey) + { + return (await GetCacheItemAsync(name, providerName, providerKey)).Value; + } + + [UnitOfWork] + public virtual async Task> GetListAsync(string providerName, string providerKey) + { + var settings = await SettingRepository.GetListAsync(providerName, providerKey); + return settings.Select(s => new SettingValue(s.Name, s.Value)).ToList(); + } + + [UnitOfWork] + public virtual async Task> GetListAsync(string[] names, string providerName, string providerKey) + { + Check.NotNullOrEmpty(names, nameof(names)); + + var result = new List(); + + if (names.Length == 1) + { + var name = names.First(); + result.Add(new SettingValue(name, (await GetCacheItemAsync(name, providerName, providerKey)).Value)); + return result; + } + + var cacheItems = await GetCacheItemsAsync(names, providerName, providerKey); + result.AddRange(cacheItems.Select(item => + new SettingValue(GetSettingNameFormCacheKeyOrNull(item.Key), item.Value?.Value))); + + return result; + } + + [UnitOfWork] + public virtual async Task SetAsync(string name, string value, string providerName, string providerKey) + { + var setting = await SettingRepository.FindAsync(name, providerName, providerKey); + if (setting == null) + { + setting = new SettingEntity(GuidGenerator.Create(), name, value, providerName, providerKey); + await SettingRepository.InsertAsync(setting); + } + else + { + setting.Value = value; + await SettingRepository.UpdateAsync(setting); + } + + await Cache.SetAsync(CalculateCacheKey(name, providerName, providerKey), new SettingCacheItem(setting.Value), + considerUow: true); + } + + [UnitOfWork] + public virtual async Task DeleteAsync(string name, string providerName, string providerKey) + { + var setting = await SettingRepository.FindAsync(name, providerName, providerKey); + if (setting != null) + { + await SettingRepository.DeleteAsync(setting); + await Cache.RemoveAsync(CalculateCacheKey(name, providerName, providerKey), considerUow: true); + } + } + + protected virtual async Task GetCacheItemAsync(string name, string providerName, + string providerKey) + { + var cacheKey = CalculateCacheKey(name, providerName, providerKey); + var cacheItem = await Cache.GetAsync(cacheKey, considerUow: true); + + if (cacheItem != null) + { + return cacheItem; + } + + cacheItem = new SettingCacheItem(null); + + await SetCacheItemsAsync(providerName, providerKey, name, cacheItem); + + return cacheItem; + } + + protected virtual async Task>> GetCacheItemsAsync(string[] names, + string providerName, string providerKey) + { + var cacheKeys = names.Select(x => CalculateCacheKey(x, providerName, providerKey)).ToList(); + + var cacheItems = (await Cache.GetManyAsync(cacheKeys, considerUow: true)).ToList(); + + if (cacheItems.All(x => x.Value != null)) + { + return cacheItems; + } + + var notCacheKeys = cacheItems.Where(x => x.Value == null).Select(x => x.Key).ToList(); + + var newCacheItems = await SetCacheItemsAsync(providerName, providerKey, notCacheKeys); + + var result = new List>(); + foreach (var key in cacheKeys) + { + var item = newCacheItems.FirstOrDefault(x => x.Key == key); + if (item.Value == null) + { + item = cacheItems.FirstOrDefault(x => x.Key == key); + } + + result.Add(new KeyValuePair(key, item.Value)); + } + + return result; + } + + private async Task SetCacheItemsAsync( + string providerName, + string providerKey, + string currentName, + SettingCacheItem currentCacheItem) + { + var settingDefinitions = await SettingDefinitionManager.GetAllAsync(); + var settingsDictionary = (await SettingRepository.GetListAsync(providerName, providerKey)) + .ToDictionary(s => s.Name, s => s.Value); + + var cacheItems = new List>(); + + foreach (var settingDefinition in settingDefinitions) + { + var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name); + + cacheItems.Add( + new KeyValuePair( + CalculateCacheKey(settingDefinition.Name, providerName, providerKey), + new SettingCacheItem(settingValue) + ) + ); + + if (settingDefinition.Name == currentName) + { + currentCacheItem.Value = settingValue; + } + } + + await Cache.SetManyAsync(cacheItems, considerUow: true); + } + + private async Task>> SetCacheItemsAsync( + string providerName, + string providerKey, + List notCacheKeys) + { + var settingDefinitions = (await SettingDefinitionManager.GetAllAsync()).Where(x => + notCacheKeys.Any(k => GetSettingNameFormCacheKeyOrNull(k) == x.Name)); + + var settingsDictionary = + (await SettingRepository.GetListAsync(notCacheKeys.Select(GetSettingNameFormCacheKeyOrNull).ToArray(), + providerName, providerKey)) + .ToDictionary(s => s.Name, s => s.Value); + + var cacheItems = new List>(); + + foreach (var settingDefinition in settingDefinitions) + { + var settingValue = settingsDictionary.GetOrDefault(settingDefinition.Name); + cacheItems.Add( + new KeyValuePair( + CalculateCacheKey(settingDefinition.Name, providerName, providerKey), + new SettingCacheItem(settingValue) + ) + ); + } + + await Cache.SetManyAsync(cacheItems, considerUow: true); + + return cacheItems; + } + + + protected virtual string CalculateCacheKey(string name, string providerName, string providerKey) + { + return SettingCacheItem.CalculateCacheKey(name, providerName, providerKey); + } + + protected virtual string? GetSettingNameFormCacheKeyOrNull(string key) + { + //TODO: throw ex when name is null? + return SettingCacheItem.GetSettingNameFormCacheKeyOrNull(key); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/SettingStore.cs b/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/SettingStore.cs new file mode 100644 index 0000000..1dfd97f --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.Domain/Store/SettingStore.cs @@ -0,0 +1,27 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.Settings; + +namespace NPin.Framework.SettingManagement.Domain; + +/// +/// 设置存储 +/// +public class SettingStore : ISettingStore, ITransientDependency +{ + protected ISettingManagementStore ManagementStore { get; } + + public SettingStore(ISettingManagementStore managementStore) + { + ManagementStore = managementStore; + } + + public virtual Task GetOrNullAsync(string name, string? providerName, string? providerKey) + { + return ManagementStore.GetOrNullAsync(name, providerName, providerKey); + } + + public virtual Task> GetAllAsync(string[] names, string? providerName, string? providerKey) + { + return ManagementStore.GetListAsync(names, providerName, providerKey); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/NPin.Framework.SettingManagement.SqlSugarCore.csproj b/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/NPin.Framework.SettingManagement.SqlSugarCore.csproj new file mode 100644 index 0000000..8806d33 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/NPin.Framework.SettingManagement.SqlSugarCore.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + + + + + + + + diff --git a/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/NPinFrameworkSettingManagementSqlSugarCoreModule.cs b/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/NPinFrameworkSettingManagementSqlSugarCoreModule.cs new file mode 100644 index 0000000..8c7b693 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/NPinFrameworkSettingManagementSqlSugarCoreModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; +using NPin.Framework.SettingManagement.Domain; +using NPin.Framework.SqlSugarCore; + +namespace NPin.Framework.SettingManagement.SqlSugarCore; + +[DependsOn( + typeof(NPinFrameworkSettingManagementDomainModule), + typeof(NPinFrameworkSqlSugarCoreModule) +)] +public class NPinFrameworkSettingManagementSqlSugarCoreModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var services = context.Services; + services.AddTransient(); + } +} \ No newline at end of file diff --git a/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/SqlSugarCoreSettingRepository.cs b/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/SqlSugarCoreSettingRepository.cs new file mode 100644 index 0000000..0542ab8 --- /dev/null +++ b/module/setting-management/NPin.Framework.SettingManagement.SqlSugarCore/SqlSugarCoreSettingRepository.cs @@ -0,0 +1,39 @@ +using NPin.Framework.SettingManagement.Domain; +using NPin.Framework.SettingManagement.Domain.Entities; +using NPin.Framework.SqlSugarCore.Abstractions; +using NPin.Framework.SqlSugarCore.Repositories; + +namespace NPin.Framework.SettingManagement.SqlSugarCore; + +public class SqlSugarCoreSettingRepository : SqlSugarRepository, ISettingRepository +{ + public SqlSugarCoreSettingRepository(ISugarDbContextProvider sugarDbContextProvider) : base( + sugarDbContextProvider) + { + } + + public virtual async Task FindAsync(string name, string providerName, string providerKey, + CancellationToken cancellationToken = default) + { + return await DbQueryable + .Where(s => s.Name == name && s.ProviderName == providerName && s.ProviderKey == providerKey) + .OrderBy(x => x.Id) + .FirstAsync(cancellationToken); + } + + public virtual async Task> GetListAsync(string providerName, string providerKey, + CancellationToken cancellationToken = default) + { + return await DbQueryable + .Where(s => s.ProviderName == providerName && s.ProviderKey == providerKey) + .ToListAsync(cancellationToken); + } + + public virtual async Task> GetListAsync(string[] names, string providerName, string providerKey, + CancellationToken cancellationToken = default) + { + return await DbQueryable + .Where(s => names.Contains(s.Name) && s.ProviderName == providerName && s.ProviderKey == providerKey) + .ToListAsync(cancellationToken); + } +} \ No newline at end of file