From 6580a3957844935d439c8000915c3ac7fb136ecf Mon Sep 17 00:00:00 2001 From: colin Date: Wed, 26 Aug 2026 09:23:13 +0800 Subject: [PATCH] feat: Rewrite part of the interface using DynamicDefinitionManager --- .../LINGYUN.Abp.AI.Core.csproj | 4 + .../LINGYUN/Abp/AI/AbpAICoreModule.cs | 9 +- .../LINGYUN/Abp/AI/AbpAICoreOptions.cs | 2 - .../AI/Workspaces/DynamicWorkspaceStrategy.cs | 21 - .../Abp/AI/Workspaces/WorkspaceDefinition.cs | 3 +- .../Workspaces/WorkspaceDefinitionManager.cs | 252 ++++-------- .../Abp/AI/Tools/AIToolDefinitionManager.cs | 197 +++------- .../LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs | 7 +- .../LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs | 2 - .../Abp/AI/Tools/DynamicAItoolStrategy.cs | 19 - .../LINGYUN.Abp.Notifications.Core.csproj | 1 + .../AbpNotificationsCoreModule.cs | 10 +- .../Notifications/AbpNotificationsOptions.cs | 3 - .../DynamicNotificationStrategy.cs | 19 - .../NotificationDefinitionManager.cs | 346 ++++------------- .../LINGYUN.Abp.Webhooks.Core.csproj | 4 + .../Abp/Webhooks/AbpWebhooksCoreModule.cs | 16 +- .../Abp/Webhooks/AbpWebhooksOptions.cs | 5 - .../Abp/Webhooks/DynamicWebhookStrategy.cs | 19 - .../Abp/Webhooks/WebhookDefinitionManager.cs | 360 ++++-------------- 20 files changed, 297 insertions(+), 1002 deletions(-) delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/DynamicWorkspaceStrategy.cs delete mode 100644 aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/DynamicAItoolStrategy.cs delete mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/DynamicNotificationStrategy.cs delete mode 100644 aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/DynamicWebhookStrategy.cs diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj index b6e35c892..ba6d8dc63 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj @@ -26,4 +26,8 @@ + + + + diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs index 9bd80e7b7..f4569ce83 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs @@ -1,6 +1,7 @@ using LINGYUN.Abp.AI.Internal; using LINGYUN.Abp.AI.Localization; using LINGYUN.Abp.AI.Workspaces; +using LINGYUN.Abp.Dynamic.Definitions; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; @@ -18,7 +19,8 @@ namespace LINGYUN.Abp.AI; typeof(AbpAIModule), typeof(AbpGuidsModule), typeof(AbpTimingModule), - typeof(AbpLocalizationModule))] + typeof(AbpLocalizationModule), + typeof(AbpDynamicDefinitionsCoreModule))] public class AbpAICoreModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) @@ -53,6 +55,11 @@ public class AbpAICoreModule : AbpModule { options.MapCodeNamespace(AbpAIErrorCodes.Namespace, typeof(AbpAIResource)); }); + + Configure(options => + { + options.MapStrategy(DynamicDefinitionStrategy.Merge); + }); } private static void AutoAddDefinitionProviders(IServiceCollection services) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs index 3a4a78b8d..3b25830bd 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs @@ -9,7 +9,6 @@ using Volo.Abp.Collections; namespace LINGYUN.Abp.AI; public class AbpAICoreOptions { - public DynamicWorkspaceStrategy DynamicWorkspaceStrategy { get; set; } public ITypeList DefinitionProviders { get; } public ITypeList ChatClientProviders { get; } public ITypeList KernelProviders { get; } @@ -21,7 +20,6 @@ public class AbpAICoreOptions public AbpAICoreOptions() { - DynamicWorkspaceStrategy = DynamicWorkspaceStrategy.Merge; DefinitionProviders = new TypeList(); ChatClientProviders = new TypeList(); KernelProviders = new TypeList(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/DynamicWorkspaceStrategy.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/DynamicWorkspaceStrategy.cs deleted file mode 100644 index 6aa754874..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/DynamicWorkspaceStrategy.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace LINGYUN.Abp.AI.Workspaces; -/// -/// 动态工作区策略 -/// -public enum DynamicWorkspaceStrategy -{ - /// - /// 忽略动态工作区(静态优先) - /// - Ignore, - - /// - /// 动态覆盖静态 - /// - Covering, - - /// - /// 合并策略 - /// - Merge -} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs index 9b6d9c431..a59c636df 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs @@ -147,8 +147,7 @@ public class WorkspaceDefinition : IHasSimpleStateCheckers Name = name; Provider = provider; ModelName = modelName; - _displayName = displayName; - _displayName = displayName; + DisplayName = displayName; Description = description; SystemPrompt = systemPrompt; Instructions = instructions; diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionManager.cs index 0a4ef9102..2d48aa041 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionManager.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionManager.cs @@ -1,231 +1,117 @@ -using Microsoft.Extensions.Options; +using JetBrains.Annotations; +using LINGYUN.Abp.Dynamic.Definitions; +using Microsoft.Extensions.Options; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.AI.Workspaces; -public class WorkspaceDefinitionManager : IWorkspaceDefinitionManager, ISingletonDependency + +public class WorkspaceDefinitionManager : + DynamicDefinitionManager, + IWorkspaceDefinitionManager, + ITransientDependency { - protected readonly AbpAICoreOptions Options; - protected readonly IStaticWorkspaceDefinitionStore StaticStore; - protected readonly IDynamicWorkspaceDefinitionStore DynamicStore; + protected IStaticWorkspaceDefinitionStore StaticStore { get; } + protected IDynamicWorkspaceDefinitionStore DynamicStore { get; } + protected AbpAICoreOptions AIOptions { get; } public WorkspaceDefinitionManager( IStaticWorkspaceDefinitionStore staticStore, IDynamicWorkspaceDefinitionStore dynamicStore, - IOptions options) + IOptions options, + IOptions aIOptions) + : base(options) { StaticStore = staticStore; DynamicStore = dynamicStore; - Options = options.Value; + AIOptions = aIOptions.Value; } - public virtual async Task GetAsync(string name) + public async virtual Task> GetAllAsync() { - var workspace = await GetOrNullAsync(name); - if (workspace == null) - { - throw new AbpException("Undefined Workspace: " + name); - } + var staticDefinitions = await StaticStore.GetAllAsync(); + var dynamicDefinitions = await DynamicStore.GetAllAsync(); - return workspace; + return await GetDefinitionsAsync(staticDefinitions, dynamicDefinitions); } - public virtual async Task GetOrNullAsync(string name) + public async virtual Task GetAsync([NotNull] string name) { - Check.NotNull(name, nameof(name)); - - var staticDefinition = await StaticStore.GetOrNullAsync(name); - var dynamicDefinition = await DynamicStore.GetOrNullAsync(name); - - if (staticDefinition != null && dynamicDefinition != null) - { - return Options.DynamicWorkspaceStrategy switch - { - DynamicWorkspaceStrategy.Ignore => staticDefinition, - DynamicWorkspaceStrategy.Covering => dynamicDefinition, - DynamicWorkspaceStrategy.Merge => MergeWorkspace(staticDefinition, dynamicDefinition), - _ => MergeWorkspace(staticDefinition, dynamicDefinition) - }; - } - - return staticDefinition ?? dynamicDefinition; + return await GetOrNullAsync(name) ?? throw new AbpException("Undefined Workspace: " + name); } - public virtual async Task> GetAllAsync() + public async virtual Task GetOrNullAsync([NotNull] string name) { - var staticWorkspaces = await StaticStore.GetAllAsync(); - var dynamicWorkspaces = await DynamicStore.GetAllAsync(); - - // 根据策略处理工作区定义 - return Options.DynamicWorkspaceStrategy switch - { - DynamicWorkspaceStrategy.Ignore => await GetWorkspacesWithIgnoreStrategy(staticWorkspaces, dynamicWorkspaces), - DynamicWorkspaceStrategy.Covering => await GetWorkspacesWithCoveringStrategy(staticWorkspaces, dynamicWorkspaces), - DynamicWorkspaceStrategy.Merge => await GetWorkspacesWithMergeStrategy(staticWorkspaces, dynamicWorkspaces), - _ => await GetWorkspacesWithMergeStrategy(staticWorkspaces, dynamicWorkspaces) // 默认使用合并策略 - }; - } - - #region 工作区定义策略 + Check.NotNull(name, nameof(name)); - /// - /// 忽略策略:静态优先,过滤掉同名的动态工作区 - /// - protected virtual Task> GetWorkspacesWithIgnoreStrategy( - IReadOnlyList staticWorkspaces, - IReadOnlyList dynamicWorkspaces) - { - var staticWorkspaceNames = staticWorkspaces - .Select(p => p.Name) - .ToImmutableHashSet(); + var staticDefinition = await StaticStore.GetOrNullAsync(name); + var dynamicDefinition = await DynamicStore.GetOrNullAsync(name); - return Task.FromResult>( - staticWorkspaces - .Concat(dynamicWorkspaces.Where(d => !staticWorkspaceNames.Contains(d.Name))) - .ToImmutableList() - ); + return await GetDefinitionAsync(staticDefinition, dynamicDefinition); } - /// - /// 覆盖策略:动态完全覆盖静态工作区 - /// - protected virtual Task> GetWorkspacesWithCoveringStrategy( - IReadOnlyList staticWorkspaces, - IReadOnlyList dynamicWorkspaces) + protected override string GetDefinitionKey(WorkspaceDefinition definition) { - var dynamicWorkspaceNames = dynamicWorkspaces - .Select(p => p.Name) - .ToImmutableHashSet(); - - // 动态工作区完全覆盖静态工作区 - var result = dynamicWorkspaces - .Concat(staticWorkspaces.Where(s => !dynamicWorkspaceNames.Contains(s.Name))) - .ToImmutableList(); - - return Task.FromResult>(result); + return definition.Name; } - /// - /// 合并策略:合并静态和动态工作区,创建新实例 - /// - protected virtual Task> GetWorkspacesWithMergeStrategy( - IReadOnlyList staticWorkspaces, - IReadOnlyList dynamicWorkspaces) + protected override Task AfterMergedDefinitionsAsync(Dictionary mergedDefinitions) { - var mergedWorkspaces = new Dictionary(); - - // 先添加所有静态工作区 - foreach (var staticWorkspace in staticWorkspaces) - { - mergedWorkspaces[staticWorkspace.Name] = staticWorkspace; - } - - // 合并动态工作区 - foreach (var dynamicWorkspace in dynamicWorkspaces) + foreach (var deletedWorkspaceName in AIOptions.DeletedWorkspaces) { - if (mergedWorkspaces.TryGetValue(dynamicWorkspace.Name, out var existingWorkspace)) - { - // 工作区已存在,创建新的合并工作区 - var mergedWorkspace = MergeWorkspace(existingWorkspace, dynamicWorkspace); - mergedWorkspaces[dynamicWorkspace.Name] = mergedWorkspace; - } - else - { - // 添加新的动态工作区 - mergedWorkspaces[dynamicWorkspace.Name] = dynamicWorkspace; - } + mergedDefinitions.Remove(deletedWorkspaceName); } - // 处理被删除的工作区 - foreach (var deletedWorkspaceName in Options.DeletedWorkspaces) - { - if (mergedWorkspaces.ContainsKey(deletedWorkspaceName)) - { - mergedWorkspaces.Remove(deletedWorkspaceName); - } - } - - return Task.FromResult>(mergedWorkspaces.Values.ToImmutableList()); + return Task.CompletedTask; } - /// - /// 合并两个工作区定义,返回新的 WorkspaceDefinition 实例 - /// - protected virtual WorkspaceDefinition MergeWorkspace( - WorkspaceDefinition staticWorkspace, - WorkspaceDefinition dynamicWorkspace) + protected override Task MergeDefinitionAsync(WorkspaceDefinition targetDefinition, WorkspaceDefinition sourceDefinition) { - // 决定使用哪个提供者(优先使用动态的) - var provider = !string.IsNullOrEmpty(dynamicWorkspace.Provider) - ? dynamicWorkspace.Provider - : staticWorkspace.Provider; - - // 决定使用哪个模型名称(优先使用动态的) - var modelName = !string.IsNullOrEmpty(dynamicWorkspace.ModelName) - ? dynamicWorkspace.ModelName - : staticWorkspace.ModelName; - - // 决定使用哪个显示名称(优先使用动态的) - var displayName = dynamicWorkspace.DisplayName ?? staticWorkspace.DisplayName; - - // 创建新的工作区实例(Name是只读的) + var provider = !string.IsNullOrEmpty(sourceDefinition.Provider) + ? sourceDefinition.Provider + : targetDefinition.Provider; + var modelName = !string.IsNullOrEmpty(sourceDefinition.ModelName) + ? sourceDefinition.ModelName + : targetDefinition.ModelName; + var displayName = sourceDefinition.DisplayName ?? targetDefinition.DisplayName; var mergedWorkspace = new WorkspaceDefinition( - staticWorkspace.Name, // 保持名称不变 + targetDefinition.Name, provider, modelName, displayName - ); - - // 设置描述(优先使用动态的) - mergedWorkspace.Description = dynamicWorkspace.Description ?? staticWorkspace.Description; + ) + { + Description = sourceDefinition.Description ?? targetDefinition.Description + }; - // 设置API密钥(优先使用动态的) - if (!string.IsNullOrEmpty(dynamicWorkspace.ApiKey)) + if (!string.IsNullOrEmpty(sourceDefinition.ApiKey)) { - mergedWorkspace.WithApiKey(dynamicWorkspace.ApiKey!); + mergedWorkspace.WithApiKey(sourceDefinition.ApiKey!); } - else if (!string.IsNullOrEmpty(staticWorkspace.ApiKey)) + else if (!string.IsNullOrEmpty(targetDefinition.ApiKey)) { - mergedWorkspace.WithApiKey(staticWorkspace.ApiKey!); + mergedWorkspace.WithApiKey(targetDefinition.ApiKey!); } - // 设置API基础URL(优先使用动态的) - if (!string.IsNullOrEmpty(dynamicWorkspace.ApiBaseUrl)) + if (!string.IsNullOrEmpty(sourceDefinition.ApiBaseUrl)) { - mergedWorkspace.WithApiBaseUrl(dynamicWorkspace.ApiBaseUrl!); + mergedWorkspace.WithApiBaseUrl(sourceDefinition.ApiBaseUrl!); } - else if (!string.IsNullOrEmpty(staticWorkspace.ApiBaseUrl)) + else if (!string.IsNullOrEmpty(targetDefinition.ApiBaseUrl)) { - mergedWorkspace.WithApiBaseUrl(staticWorkspace.ApiBaseUrl!); + mergedWorkspace.WithApiBaseUrl(targetDefinition.ApiBaseUrl!); } + mergedWorkspace.SystemPrompt = sourceDefinition.SystemPrompt ?? targetDefinition.SystemPrompt; + mergedWorkspace.Instructions = sourceDefinition.Instructions ?? targetDefinition.Instructions; + mergedWorkspace.Temperature = sourceDefinition.Temperature ?? targetDefinition.Temperature; + mergedWorkspace.MaxOutputTokens = sourceDefinition.MaxOutputTokens ?? targetDefinition.MaxOutputTokens; + mergedWorkspace.FrequencyPenalty = sourceDefinition.FrequencyPenalty ?? targetDefinition.FrequencyPenalty; + mergedWorkspace.PresencePenalty = sourceDefinition.PresencePenalty ?? targetDefinition.PresencePenalty; + mergedWorkspace.IsEnabled = targetDefinition.IsEnabled || sourceDefinition.IsEnabled; - // 设置系统提示词(优先使用动态的) - mergedWorkspace.SystemPrompt = dynamicWorkspace.SystemPrompt ?? staticWorkspace.SystemPrompt; - - // 设置附加系统提示词(优先使用动态的) - mergedWorkspace.Instructions = dynamicWorkspace.Instructions ?? staticWorkspace.Instructions; - - // 设置温度值(优先使用动态的) - mergedWorkspace.Temperature = dynamicWorkspace.Temperature ?? staticWorkspace.Temperature; - - // 设置最大输出token数(优先使用动态的) - mergedWorkspace.MaxOutputTokens = dynamicWorkspace.MaxOutputTokens ?? staticWorkspace.MaxOutputTokens; - - // 设置频率惩罚(优先使用动态的) - mergedWorkspace.FrequencyPenalty = dynamicWorkspace.FrequencyPenalty ?? staticWorkspace.FrequencyPenalty; - - // 设置存在惩罚(优先使用动态的) - mergedWorkspace.PresencePenalty = dynamicWorkspace.PresencePenalty ?? staticWorkspace.PresencePenalty; - - // 设置是否启用(只要有一方启用,结果就是启用) - mergedWorkspace.IsEnabled = staticWorkspace.IsEnabled || dynamicWorkspace.IsEnabled; - - // 合并状态检查器 - foreach (var checker in staticWorkspace.StateCheckers) + foreach (var checker in targetDefinition.StateCheckers) { if (!mergedWorkspace.StateCheckers.Contains(checker)) { @@ -233,7 +119,7 @@ public class WorkspaceDefinitionManager : IWorkspaceDefinitionManager, ISingleto } } - foreach (var checker in dynamicWorkspace.StateCheckers) + foreach (var checker in sourceDefinition.StateCheckers) { if (!mergedWorkspace.StateCheckers.Contains(checker)) { @@ -241,8 +127,7 @@ public class WorkspaceDefinitionManager : IWorkspaceDefinitionManager, ISingleto } } - // 合并工具列表(去重) - foreach (var tool in staticWorkspace.Tools) + foreach (var tool in targetDefinition.Tools) { if (!mergedWorkspace.Tools.Contains(tool)) { @@ -250,7 +135,7 @@ public class WorkspaceDefinitionManager : IWorkspaceDefinitionManager, ISingleto } } - foreach (var tool in dynamicWorkspace.Tools) + foreach (var tool in sourceDefinition.Tools) { if (!mergedWorkspace.Tools.Contains(tool)) { @@ -258,19 +143,16 @@ public class WorkspaceDefinitionManager : IWorkspaceDefinitionManager, ISingleto } } - // 合并属性(动态覆盖静态) - foreach (var property in staticWorkspace.Properties) + foreach (var property in targetDefinition.Properties) { mergedWorkspace.Properties[property.Key] = property.Value; } - foreach (var property in dynamicWorkspace.Properties) + foreach (var property in sourceDefinition.Properties) { mergedWorkspace.Properties[property.Key] = property.Value; } - return mergedWorkspace; + return Task.FromResult(mergedWorkspace); } - - #endregion } diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs index 630842b08..70179c0b7 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs @@ -1,188 +1,90 @@ -using Microsoft.Extensions.Options; +using JetBrains.Annotations; +using LINGYUN.Abp.Dynamic.Definitions; +using Microsoft.Extensions.Options; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.AI.Tools; -public class AIToolDefinitionManager : IAIToolDefinitionManager, ISingletonDependency +public class AIToolDefinitionManager : + DynamicDefinitionManager, + IAIToolDefinitionManager, + ITransientDependency { - protected readonly AbpAIToolsOptions AIToolOptions; - protected readonly IStaticAIToolDefinitionStore StaticStore; - protected readonly IDynamicAIToolDefinitionStore DynamicStore; + protected IStaticAIToolDefinitionStore StaticStore { get; } + protected IDynamicAIToolDefinitionStore DynamicStore { get; } + protected AbpAIToolsOptions AIToolsOptions { get; } public AIToolDefinitionManager( IStaticAIToolDefinitionStore staticStore, IDynamicAIToolDefinitionStore dynamicStore, - IOptions aiToolOptions) + IOptions options, + IOptions aIToolsOptions) + : base(options) { StaticStore = staticStore; DynamicStore = dynamicStore; - AIToolOptions = aiToolOptions.Value; + AIToolsOptions = aIToolsOptions.Value; } - public virtual async Task GetAsync(string name) + public async virtual Task GetAsync([NotNull] string name) { - var workspace = await GetOrNullAsync(name); - if (workspace == null) - { - throw new AbpException("Undefined AITool: " + name); - } - - return workspace; + return await GetOrNullAsync(name) ?? throw new AbpException("Undefined AITool: " + name); } - public virtual async Task GetOrNullAsync(string name) + public async virtual Task> GetAllAsync() { - Check.NotNull(name, nameof(name)); - - var staticDefinition = await StaticStore.GetOrNullAsync(name); - var dynamicDefinition = await DynamicStore.GetOrNullAsync(name); - - if (staticDefinition != null && dynamicDefinition != null) - { - return AIToolOptions.DynamicAItoolStrategy switch - { - DynamicAItoolStrategy.Ignore => staticDefinition, - DynamicAItoolStrategy.Covering => dynamicDefinition, - DynamicAItoolStrategy.Merge => MergeAITool(staticDefinition, dynamicDefinition), - _ => MergeAITool(staticDefinition, dynamicDefinition) - }; - } + var staticDefinitions = await StaticStore.GetAllAsync(); + var dynamicDefinitions = await DynamicStore.GetAllAsync(); - return staticDefinition ?? dynamicDefinition; + return await GetDefinitionsAsync(staticDefinitions, dynamicDefinitions); } - public virtual async Task> GetAllAsync() + public async virtual Task GetOrNullAsync([NotNull] string name) { - var staticAITools = await StaticStore.GetAllAsync(); - var dynamicAITools = await DynamicStore.GetAllAsync(); - - // 根据策略处理AI工具定义 - return AIToolOptions.DynamicAItoolStrategy switch - { - DynamicAItoolStrategy.Ignore => await GetAIToolsWithIgnoreStrategy(staticAITools, dynamicAITools), - DynamicAItoolStrategy.Covering => await GetAIToolsWithCoveringStrategy(staticAITools, dynamicAITools), - DynamicAItoolStrategy.Merge => await GetAIToolsWithMergeStrategy(staticAITools, dynamicAITools), - _ => await GetAIToolsWithMergeStrategy(staticAITools, dynamicAITools) // 默认使用合并策略 - }; - } + Check.NotNull(name, nameof(name)); - #region AI工具定义策略 + var staticDefinition = await StaticStore.GetOrNullAsync(name); + var dynamicDefinition = await DynamicStore.GetOrNullAsync(name); - /// - /// 忽略策略:静态优先,过滤掉同名的动态AI工具 - /// - protected virtual Task> GetAIToolsWithIgnoreStrategy( - IReadOnlyList staticAITools, - IReadOnlyList dynamicAITools) - { - var staticAIToolNames = staticAITools - .Select(p => p.Name) - .ToImmutableHashSet(); - - return Task.FromResult>( - staticAITools - .Concat(dynamicAITools.Where(d => !staticAIToolNames.Contains(d.Name))) - .ToImmutableList() - ); + return await GetDefinitionAsync(staticDefinition, dynamicDefinition); } - /// - /// 覆盖策略:动态完全覆盖静态AI工具 - /// - protected virtual Task> GetAIToolsWithCoveringStrategy( - IReadOnlyList staticAITools, - IReadOnlyList dynamicAITools) + protected override string GetDefinitionKey(AIToolDefinition definition) { - var dynamicAIToolNames = dynamicAITools - .Select(p => p.Name) - .ToImmutableHashSet(); - - // 动态AI工具完全覆盖静态AI工具 - var result = dynamicAITools - .Concat(staticAITools.Where(s => !dynamicAIToolNames.Contains(s.Name))) - .ToImmutableList(); - - return Task.FromResult>(result); + return definition.Name; } - /// - /// 合并策略:合并静态和动态AI工具,创建新实例 - /// - protected virtual Task> GetAIToolsWithMergeStrategy( - IReadOnlyList staticAITools, - IReadOnlyList dynamicAITools) + protected override Task AfterMergedDefinitionsAsync(Dictionary mergedDefinitions) { - var mergedAITools = new Dictionary(); - - // 先添加所有静态AI工具 - foreach (var staticAITool in staticAITools) - { - mergedAITools[staticAITool.Name] = staticAITool; - } - - // 合并动态AI工具 - foreach (var dynamicAITool in dynamicAITools) - { - if (mergedAITools.TryGetValue(dynamicAITool.Name, out var existingAITool)) - { - // AI工具已存在,创建新的合并AI工具 - var mergedAITool = MergeAITool(existingAITool, dynamicAITool); - mergedAITools[dynamicAITool.Name] = mergedAITool; - } - else - { - // 添加新的动态AI工具 - mergedAITools[dynamicAITool.Name] = dynamicAITool; - } - } - - // 处理被删除的AI工具 - foreach (var deletedToolName in AIToolOptions.DeletedAITools) + foreach (var deletedToolName in AIToolsOptions.DeletedAITools) { - if (mergedAITools.ContainsKey(deletedToolName)) - { - mergedAITools.Remove(deletedToolName); - } + mergedDefinitions.Remove(deletedToolName); } - return Task.FromResult>(mergedAITools.Values.ToImmutableList()); + return Task.CompletedTask; } - /// - /// 合并两个AI工具定义,返回新的 AIToolDefinition 实例 - /// - protected virtual AIToolDefinition MergeAITool( - AIToolDefinition staticAITool, - AIToolDefinition dynamicAITool) + protected override Task MergeDefinitionAsync(AIToolDefinition targetDefinition, AIToolDefinition sourceDefinition) { - // 决定使用哪个提供者(优先使用动态的) - var provider = !string.IsNullOrEmpty(dynamicAITool.Provider) - ? dynamicAITool.Provider - : staticAITool.Provider; - - // 决定使用哪个描述(优先使用动态的) - var description = dynamicAITool.Description ?? staticAITool.Description; + var provider = !string.IsNullOrEmpty(sourceDefinition.Provider) + ? sourceDefinition.Provider + : targetDefinition.Provider; + var description = sourceDefinition.Description ?? targetDefinition.Description; - // 创建新的AI工具实例(Name是只读的) var mergedAITool = new AIToolDefinition( - staticAITool.Name, // 保持名称不变 + targetDefinition.Name, provider, description - ); - - // 设置是否启用(只要有一方启用,结果就是启用) - mergedAITool.IsEnabled = staticAITool.IsEnabled || dynamicAITool.IsEnabled; - - // 设置是否为全局工具(只要有一方是全局,结果就是全局) - mergedAITool.IsGlobal = staticAITool.IsGlobal || dynamicAITool.IsGlobal; + ) + { + IsEnabled = targetDefinition.IsEnabled || sourceDefinition.IsEnabled, + IsGlobal = targetDefinition.IsGlobal || sourceDefinition.IsGlobal + }; - // 合并状态检查器 - foreach (var checker in staticAITool.StateCheckers) + foreach (var checker in targetDefinition.StateCheckers) { if (!mergedAITool.StateCheckers.Contains(checker)) { @@ -190,7 +92,7 @@ public class AIToolDefinitionManager : IAIToolDefinitionManager, ISingletonDepen } } - foreach (var checker in dynamicAITool.StateCheckers) + foreach (var checker in sourceDefinition.StateCheckers) { if (!mergedAITool.StateCheckers.Contains(checker)) { @@ -198,19 +100,16 @@ public class AIToolDefinitionManager : IAIToolDefinitionManager, ISingletonDepen } } - // 合并属性(动态覆盖静态) - foreach (var property in staticAITool.Properties) + foreach (var property in targetDefinition.Properties) { mergedAITool.Properties[property.Key] = property.Value; } - foreach (var property in dynamicAITool.Properties) + foreach (var property in sourceDefinition.Properties) { mergedAITool.Properties[property.Key] = property.Value; } - return mergedAITool; + return Task.FromResult(mergedAITool); } - - #endregion -} \ No newline at end of file +} diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs index 2da4d9526..7f702e323 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs @@ -1,7 +1,7 @@ using LINGYUN.Abp.AI.Localization; +using LINGYUN.Abp.Dynamic.Definitions; using Microsoft.Extensions.AI; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using Volo.Abp.Localization; @@ -58,6 +58,11 @@ public class AbpAIToolsModule : AbpModule .Get() .AddVirtualJson("/LINGYUN/Abp/AI/Tools/Localization/Resources"); }); + + Configure(options => + { + options.MapStrategy(DynamicDefinitionStrategy.Merge); + }); } private static void AutoAddDefinitionProviders(IServiceCollection services) diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs index e1bd082c1..fa552fa29 100644 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs +++ b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs @@ -5,12 +5,10 @@ namespace LINGYUN.Abp.AI.Tools; public class AbpAIToolsOptions { public ITypeList DefinitionProviders { get; } - public DynamicAItoolStrategy DynamicAItoolStrategy { get; set; } public ITypeList AIToolProviders { get; } public HashSet DeletedAITools { get; } public AbpAIToolsOptions() { - DynamicAItoolStrategy = DynamicAItoolStrategy.Merge; DefinitionProviders = new TypeList(); AIToolProviders = new TypeList(); DeletedAITools = new HashSet(); diff --git a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/DynamicAItoolStrategy.cs b/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/DynamicAItoolStrategy.cs deleted file mode 100644 index 3435eb580..000000000 --- a/aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/DynamicAItoolStrategy.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace LINGYUN.Abp.AI.Tools; -/// -/// 动态工具策略 -/// -public enum DynamicAItoolStrategy : byte -{ - /// - /// 忽略 - /// - Ignore = 0, - /// - /// 覆盖 - /// - Covering =1, - /// - /// 合并 - /// - Merge = 2 -} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN.Abp.Notifications.Core.csproj b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN.Abp.Notifications.Core.csproj index a9cf1bbb2..147ebdd44 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN.Abp.Notifications.Core.csproj +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN.Abp.Notifications.Core.csproj @@ -22,6 +22,7 @@ + diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsCoreModule.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsCoreModule.cs index 18ad47fe2..dc3d5484b 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsCoreModule.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsCoreModule.cs @@ -1,4 +1,5 @@ -using LINGYUN.Abp.Notifications.Localization; +using LINGYUN.Abp.Dynamic.Definitions; +using LINGYUN.Abp.Notifications.Localization; using LINGYUN.Abp.RealTime; using Microsoft.Extensions.DependencyInjection; using System; @@ -12,6 +13,7 @@ using Volo.Abp.TextTemplating; namespace LINGYUN.Abp.Notifications; [DependsOn( + typeof(AbpDynamicDefinitionsCoreModule), typeof(AbpTextTemplatingCoreModule), typeof(AbpRealTimeModule), typeof(AbpLocalizationModule), @@ -36,6 +38,12 @@ public class AbpNotificationsCoreModule : AbpModule { preActions.Configure(options); }); + + Configure(options => + { + options.MapStrategy(DynamicDefinitionStrategy.Merge); + options.MapStrategy(DynamicDefinitionStrategy.Merge); + }); } private void AutoAddDefinitionProviders(IServiceCollection services) diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsOptions.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsOptions.cs index d158f25c5..a88701015 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsOptions.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsOptions.cs @@ -10,15 +10,12 @@ public class AbpNotificationsOptions /// public ITypeList DefinitionProviders { get; } - public DynamicNotificationStrategy DynamicNotificationStrategy { get; set; } - public HashSet DeletedNotifications { get; } public HashSet DeletedNotificationGroups { get; } public AbpNotificationsOptions() { - DynamicNotificationStrategy = DynamicNotificationStrategy.Merge; DefinitionProviders = new TypeList(); DeletedNotifications = new HashSet(); diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/DynamicNotificationStrategy.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/DynamicNotificationStrategy.cs deleted file mode 100644 index f473fed87..000000000 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/DynamicNotificationStrategy.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace LINGYUN.Abp.Notifications; -/// -/// 动态通知策略 -/// -public enum DynamicNotificationStrategy : byte -{ - /// - /// 忽略 - /// - Ignore = 0, - /// - /// 覆盖 - /// - Covering =1, - /// - /// 合并 - /// - Merge = 2 -} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs index 0fa26ae5e..7d6b4a24d 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs @@ -1,224 +1,93 @@ -using Microsoft.Extensions.Options; +using JetBrains.Annotations; +using LINGYUN.Abp.Dynamic.Definitions; +using Microsoft.Extensions.Options; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.Notifications; -public class NotificationDefinitionManager : INotificationDefinitionManager, ITransientDependency +public class NotificationDefinitionManager : + DynamicDefinitionManager, + INotificationDefinitionManager, + ITransientDependency { - private readonly AbpNotificationsOptions _notificationsOptions; - private readonly IStaticNotificationDefinitionStore _staticStore; - private readonly IDynamicNotificationDefinitionStore _dynamicStore; + protected IStaticNotificationDefinitionStore StaticStore { get; } + protected IDynamicNotificationDefinitionStore DynamicStore { get; } public NotificationDefinitionManager( IStaticNotificationDefinitionStore staticStore, IDynamicNotificationDefinitionStore dynamicStore, - IOptions notificationsOptions) + IOptions options) : base(options) { - _staticStore = staticStore; - _dynamicStore = dynamicStore; - _notificationsOptions = notificationsOptions.Value; + StaticStore = staticStore; + DynamicStore = dynamicStore; } - public async virtual Task GetAsync(string name) + public async virtual Task GetAsync([NotNull] string name) { - var notification = await GetOrNullAsync(name); - if (notification == null) - { - throw new AbpException("Undefined notification: " + name); - } - - return notification; - } - - public async virtual Task GetOrNullAsync(string name) - { - Check.NotNull(name, nameof(name)); - - var staticDefinition = await _staticStore.GetOrNullAsync(name); - var dynamicDefinition = await _dynamicStore.GetOrNullAsync(name); - - if (staticDefinition != null && dynamicDefinition != null) - { - return _notificationsOptions.DynamicNotificationStrategy switch - { - DynamicNotificationStrategy.Ignore => staticDefinition, - DynamicNotificationStrategy.Covering => dynamicDefinition, - DynamicNotificationStrategy.Merge => MergeNotification(staticDefinition, dynamicDefinition), - _ => staticDefinition - }; - } - - return staticDefinition ?? dynamicDefinition; - } - - public async virtual Task> GetNotificationsAsync() - { - var staticNotifications = await _staticStore.GetNotificationsAsync(); - var dynamicNotifications = await _dynamicStore.GetNotificationsAsync(); - - // 根据策略处理通知定义 - return _notificationsOptions.DynamicNotificationStrategy switch - { - DynamicNotificationStrategy.Ignore => await GetNotificationsWithIgnoreStrategy(staticNotifications, dynamicNotifications), - DynamicNotificationStrategy.Covering => await GetNotificationsWithCoveringStrategy(staticNotifications, dynamicNotifications), - DynamicNotificationStrategy.Merge => await GetNotificationsWithMergeStrategy(staticNotifications, dynamicNotifications), - _ => await GetNotificationsWithIgnoreStrategy(staticNotifications, dynamicNotifications) - }; + return await GetOrNullAsync(name) ?? throw new AbpException("Undefined notification: " + name); } public async virtual Task GetGroupOrNullAsync(string name) { Check.NotNull(name, nameof(name)); - var staticDefinition = await _staticStore.GetGroupOrNullAsync(name); - var dynamicDefinition = await _dynamicStore.GetGroupOrNullAsync(name); - - if (staticDefinition != null && dynamicDefinition != null) - { - switch (_notificationsOptions.DynamicNotificationStrategy) - { - case DynamicNotificationStrategy.Ignore: - return staticDefinition; - case DynamicNotificationStrategy.Covering: - return dynamicDefinition; - case DynamicNotificationStrategy.Merge: - MergeGroupNotifications(staticDefinition, dynamicDefinition); - return staticDefinition; - default: - return staticDefinition; - } - } + var staticGroupDefinition = await StaticStore.GetGroupOrNullAsync(name); + var dynamicGroupDefinition = await DynamicStore.GetGroupOrNullAsync(name); - return staticDefinition ?? dynamicDefinition; + return await GetGroupDefinitionAsync(staticGroupDefinition, dynamicGroupDefinition); } public async virtual Task> GetGroupsAsync() { - var staticGroups = await _staticStore.GetGroupsAsync(); - var dynamicGroups = await _dynamicStore.GetGroupsAsync(); + var staticGroupDefinitions = await StaticStore.GetGroupsAsync(); + var dynamicGroupDefinitions = await DynamicStore.GetGroupsAsync(); - // 根据策略处理分组定义 - return _notificationsOptions.DynamicNotificationStrategy switch - { - DynamicNotificationStrategy.Ignore => await GetGroupsWithIgnoreStrategy(staticGroups, dynamicGroups), - DynamicNotificationStrategy.Covering => await GetGroupsWithCoveringStrategy(staticGroups, dynamicGroups), - DynamicNotificationStrategy.Merge => await GetGroupsWithMergeStrategy(staticGroups, dynamicGroups), - _ => await GetGroupsWithIgnoreStrategy(staticGroups, dynamicGroups) - }; + return await GetGroupDefinitionsAsync(staticGroupDefinitions, dynamicGroupDefinitions); } - #region 通知定义策略 - - /// - /// 忽略策略:静态优先,过滤掉同名的动态通知 - /// - protected virtual Task> GetNotificationsWithIgnoreStrategy( - IReadOnlyList staticNotifications, - IReadOnlyList dynamicNotifications) + public async virtual Task> GetNotificationsAsync() { - var staticNotificationNames = staticNotifications - .Select(p => p.Name) - .ToImmutableHashSet(); - - return Task.FromResult>( - staticNotifications - .Concat(dynamicNotifications.Where(d => !staticNotificationNames.Contains(d.Name))) - .ToImmutableList() - ); + var staticDefinitions = await StaticStore.GetNotificationsAsync(); + var dynamicDefinitions = await DynamicStore.GetNotificationsAsync(); + + return await GetDefinitionsAsync(staticDefinitions, dynamicDefinitions); } - /// - /// 覆盖策略:动态完全覆盖静态通知 - /// - protected virtual Task> GetNotificationsWithCoveringStrategy( - IReadOnlyList staticNotifications, - IReadOnlyList dynamicNotifications) + public async virtual Task GetOrNullAsync(string name) { - var dynamicNotificationNames = dynamicNotifications - .Select(p => p.Name) - .ToImmutableHashSet(); + Check.NotNull(name, nameof(name)); - // 动态通知完全覆盖静态通知 - var result = dynamicNotifications - .Concat(staticNotifications.Where(s => !dynamicNotificationNames.Contains(s.Name))) - .ToImmutableList(); + var staticDefinition = await StaticStore.GetOrNullAsync(name); + var dynamicDefinition = await DynamicStore.GetOrNullAsync(name); - return Task.FromResult>(result); + return await GetDefinitionAsync(staticDefinition, dynamicDefinition); } - /// - /// 合并策略:合并静态和动态通知,创建新实例 - /// - protected virtual Task> GetNotificationsWithMergeStrategy( - IReadOnlyList staticNotifications, - IReadOnlyList dynamicNotifications) + protected override string GetDefinitionKey(NotificationDefinition definition) { - var mergedNotifications = new Dictionary(); - - // 先添加所有静态通知 - foreach (var staticNotification in staticNotifications) - { - mergedNotifications[staticNotification.Name] = staticNotification; - } - - // 合并动态通知 - foreach (var dynamicNotification in dynamicNotifications) - { - if (mergedNotifications.TryGetValue(dynamicNotification.Name, out var existingNotification)) - { - // 通知已存在,创建新的合并通知 - var mergedNotification = MergeNotification(existingNotification, dynamicNotification); - mergedNotifications[dynamicNotification.Name] = mergedNotification; - } - else - { - // 添加新的动态通知 - mergedNotifications[dynamicNotification.Name] = dynamicNotification; - } - } - - return Task.FromResult>(mergedNotifications.Values.ToImmutableList()); + return definition.Name; } - /// - /// 合并两个通知定义,返回新的 NotificationDefinition 实例 - /// - protected virtual NotificationDefinition MergeNotification( - NotificationDefinition staticNotification, - NotificationDefinition dynamicNotification) + protected override Task MergeDefinitionAsync(NotificationDefinition targetDefinition, NotificationDefinition sourceDefinition) { - // 决定使用哪个显示名称(优先使用动态的) - var displayName = dynamicNotification.DisplayName ?? staticNotification.DisplayName; - - // 决定使用哪个描述(优先使用动态的) - var description = dynamicNotification.Description ?? staticNotification.Description; - - // 决定通知类型(优先使用动态的) - var notificationType = dynamicNotification.NotificationType != NotificationType.Application - ? dynamicNotification.NotificationType - : staticNotification.NotificationType; - - // 决定存活类型(优先使用动态的) - var lifetime = dynamicNotification.NotificationLifetime != NotificationLifetime.Persistent - ? dynamicNotification.NotificationLifetime - : staticNotification.NotificationLifetime; - - // 决定内容类型(优先使用动态的) - var contentType = dynamicNotification.ContentType != NotificationContentType.Text - ? dynamicNotification.ContentType - : staticNotification.ContentType; + var displayName = sourceDefinition.DisplayName ?? targetDefinition.DisplayName; + var description = sourceDefinition.Description ?? targetDefinition.Description; + var notificationType = sourceDefinition.NotificationType != NotificationType.Application + ? sourceDefinition.NotificationType + : targetDefinition.NotificationType; + var lifetime = sourceDefinition.NotificationLifetime != NotificationLifetime.Persistent + ? sourceDefinition.NotificationLifetime + : targetDefinition.NotificationLifetime; + var contentType = sourceDefinition.ContentType != NotificationContentType.Text + ? sourceDefinition.ContentType + : targetDefinition.ContentType; + var allowSubscriptionToClients = sourceDefinition.AllowSubscriptionToClients || targetDefinition.AllowSubscriptionToClients; - // 决定是否允许客户端订阅(优先使用动态的) - var allowSubscriptionToClients = dynamicNotification.AllowSubscriptionToClients || staticNotification.AllowSubscriptionToClients; - - // 创建新的通知实例 var mergedNotification = new NotificationDefinition( - staticNotification.Name, // 保持名称不变 + targetDefinition.Name, displayName, description, notificationType, @@ -227,20 +96,17 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr allowSubscriptionToClients ); - // 复制静态通知的属性 - foreach (var property in staticNotification.Properties) + foreach (var property in targetDefinition.Properties) { mergedNotification.Properties[property.Key] = property.Value; } - // 复制动态通知的属性(覆盖同名的静态属性) - foreach (var property in dynamicNotification.Properties) + foreach (var property in sourceDefinition.Properties) { mergedNotification.Properties[property.Key] = property.Value; } - // 合并提供者 - foreach (var provider in staticNotification.Providers) + foreach (var provider in targetDefinition.Providers) { if (!mergedNotification.Providers.Contains(provider)) { @@ -248,7 +114,7 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr } } - foreach (var provider in dynamicNotification.Providers) + foreach (var provider in sourceDefinition.Providers) { if (!mergedNotification.Providers.Contains(provider)) { @@ -256,107 +122,32 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr } } - // 合并模板(优先使用动态的) - if (dynamicNotification.Template != null) + if (sourceDefinition.Template != null) { - mergedNotification.WithTemplate(dynamicNotification.Template); + mergedNotification.WithTemplate(sourceDefinition.Template); } - else if (staticNotification.Template != null) + else if (targetDefinition.Template != null) { - mergedNotification.WithTemplate(staticNotification.Template); + mergedNotification.WithTemplate(targetDefinition.Template); } - return mergedNotification; + return Task.FromResult(mergedNotification); } - #endregion - - #region 分组定义策略 - - /// - /// 忽略策略:静态优先,过滤掉同名的动态分组 - /// - protected virtual Task> GetGroupsWithIgnoreStrategy( - IReadOnlyList staticGroups, - IReadOnlyList dynamicGroups) + protected override string GetGroupDefinitionKey(NotificationGroupDefinition groupDefinition) { - var staticGroupNames = staticGroups - .Select(p => p.Name) - .ToImmutableHashSet(); - - return Task.FromResult>( - staticGroups - .Concat(dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name))) - .ToImmutableList() - ); - } - - /// - /// 覆盖策略:动态完全覆盖静态分组 - /// - protected virtual Task> GetGroupsWithCoveringStrategy( - IReadOnlyList staticGroups, - IReadOnlyList dynamicGroups) - { - var dynamicGroupNames = dynamicGroups - .Select(p => p.Name) - .ToImmutableHashSet(); - - var result = dynamicGroups - .Concat(staticGroups.Where(s => !dynamicGroupNames.Contains(s.Name))) - .ToImmutableList(); - - return Task.FromResult>(result); + return groupDefinition.Name; } - /// - /// 合并策略:合并静态和动态分组 - /// - protected virtual Task> GetGroupsWithMergeStrategy( - IReadOnlyList staticGroups, - IReadOnlyList dynamicGroups) + protected override Task MergeGroupDefinitionAsync(NotificationGroupDefinition targetGroupDefinition, NotificationGroupDefinition sourceGroupDefinition) { - var mergedGroups = new Dictionary(); - - // 先添加所有静态分组 - foreach (var staticGroup in staticGroups) - { - mergedGroups[staticGroup.Name] = staticGroup; - } - - // 合并动态分组 - foreach (var dynamicGroup in dynamicGroups) - { - if (mergedGroups.TryGetValue(dynamicGroup.Name, out var existingGroup)) - { - // 分组已存在,合并通知 - MergeGroupNotifications(existingGroup, dynamicGroup); - } - else - { - // 添加新的动态分组 - mergedGroups[dynamicGroup.Name] = dynamicGroup; - } - } - - return Task.FromResult>( - mergedGroups.Values.ToImmutableList() - ); - } - - /// - /// 合并分组的通知列表 - /// - private static void MergeGroupNotifications(NotificationGroupDefinition target, NotificationGroupDefinition source) - { - foreach (var sourceNotification in source.Notifications) + foreach (var sourceNotification in sourceGroupDefinition.Notifications) { - var existingNotification = target.GetNotificationOrNull(sourceNotification.Name); + var existingNotification = targetGroupDefinition.GetNotificationOrNull(sourceNotification.Name); if (existingNotification == null) { - // 通知不存在,直接添加 - var newNotification = target.AddNotification( + var newNotification = targetGroupDefinition.AddNotification( sourceNotification.Name, sourceNotification.DisplayName, sourceNotification.Description, @@ -366,7 +157,6 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr sourceNotification.AllowSubscriptionToClients ); - // 复制提供者 foreach (var provider in sourceNotification.Providers) { if (!newNotification.Providers.Contains(provider)) @@ -375,13 +165,11 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr } } - // 复制属性 foreach (var property in sourceNotification.Properties) { newNotification.Properties[property.Key] = property.Value; } - // 复制模板 if (sourceNotification.Template != null) { newNotification.WithTemplate(sourceNotification.Template); @@ -389,13 +177,11 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr } else { - // 通知已存在,合并属性 foreach (var property in sourceNotification.Properties) { existingNotification.Properties[property.Key] = property.Value; } - // 合并提供者 foreach (var provider in sourceNotification.Providers) { if (!existingNotification.Providers.Contains(provider)) @@ -404,30 +190,26 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr } } - // 更新显示名称(如果源提供了) if (sourceNotification.DisplayName != null) { existingNotification.DisplayName = sourceNotification.DisplayName; } - // 更新描述(如果源提供了) if (sourceNotification.Description != null) { existingNotification.Description = sourceNotification.Description; } - // 更新模板(优先使用动态的) if (sourceNotification.Template != null) { existingNotification.WithTemplate(sourceNotification.Template); } - // 更新允许客户端订阅 existingNotification.AllowSubscriptionToClients = existingNotification.AllowSubscriptionToClients || sourceNotification.AllowSubscriptionToClients; } } - } - #endregion -} \ No newline at end of file + return Task.CompletedTask; + } +} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN.Abp.Webhooks.Core.csproj b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN.Abp.Webhooks.Core.csproj index e84bed9d0..35748da90 100644 --- a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN.Abp.Webhooks.Core.csproj +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN.Abp.Webhooks.Core.csproj @@ -17,4 +17,8 @@ + + + + diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksCoreModule.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksCoreModule.cs index 75854f5a8..925829424 100644 --- a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksCoreModule.cs +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksCoreModule.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using LINGYUN.Abp.Dynamic.Definitions; +using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using Volo.Abp.Features; @@ -6,7 +7,9 @@ using Volo.Abp.Modularity; namespace LINGYUN.Abp.Webhooks; -[DependsOn(typeof(AbpFeaturesModule))] +[DependsOn( + typeof(AbpDynamicDefinitionsCoreModule), + typeof(AbpFeaturesModule))] public class AbpWebhooksCoreModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) @@ -14,6 +17,15 @@ public class AbpWebhooksCoreModule : AbpModule AutoAddDefinitionProviders(context.Services); } + public override void ConfigureServices(ServiceConfigurationContext context) + { + Configure(options => + { + options.MapStrategy(DynamicDefinitionStrategy.Merge); + options.MapStrategy(DynamicDefinitionStrategy.Merge); + }); + } + private static void AutoAddDefinitionProviders(IServiceCollection services) { var definitionProviders = new List(); diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs index 15435e52c..421e24331 100644 --- a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs @@ -12,10 +12,6 @@ public class AbpWebhooksOptions /// public TimeSpan TimeoutDuration { get; set; } /// - /// 动态Webhook策略 - /// - public DynamicWebhookStrategy DynamicWebhookStrategy { get; set; } - /// /// 默认最大发送次数 /// public int MaxSendAttemptCount { get; set; } @@ -43,7 +39,6 @@ public class AbpWebhooksOptions public string DefaultAgentIdentifier { get; set; } public AbpWebhooksOptions() { - DynamicWebhookStrategy = DynamicWebhookStrategy.Merge; TimeoutDuration = TimeSpan.FromSeconds(60); MaxSendAttemptCount = 5; MaxConsecutiveFailCountBeforeDeactivateSubscription = MaxSendAttemptCount * 3; diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/DynamicWebhookStrategy.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/DynamicWebhookStrategy.cs deleted file mode 100644 index 4e505be32..000000000 --- a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/DynamicWebhookStrategy.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace LINGYUN.Abp.Webhooks; -/// -/// 动态Webhook策略 -/// -public enum DynamicWebhookStrategy : byte -{ - /// - /// 忽略 - /// - Ignore = 0, - /// - /// 覆盖 - /// - Covering =1, - /// - /// 合并 - /// - Merge = 2 -} diff --git a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs index ac6c4ab3f..14facda43 100644 --- a/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs +++ b/aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs @@ -1,8 +1,8 @@ -using Microsoft.Extensions.DependencyInjection; +using LINGYUN.Abp.Dynamic.Definitions; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Volo.Abp; @@ -12,127 +12,76 @@ using Volo.Abp.MultiTenancy; namespace LINGYUN.Abp.Webhooks; -internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonDependency +public class WebhookDefinitionManager : + DynamicDefinitionManager, + IWebhookDefinitionManager, + ITransientDependency { - private readonly AbpWebhooksOptions _webhooksOptions; - private readonly IServiceProvider _serviceProvider; - private readonly IStaticWebhookDefinitionStore _staticStore; - private readonly IDynamicWebhookDefinitionStore _dynamicStore; + protected IServiceProvider ServiceProvider { get; } + protected IStaticWebhookDefinitionStore StaticStore { get; } + protected IDynamicWebhookDefinitionStore DynamicStore { get; } public WebhookDefinitionManager( IServiceProvider serviceProvider, - IStaticWebhookDefinitionStore staticStore, - IDynamicWebhookDefinitionStore dynamicStore, - IOptions webhooksOptions) + IStaticWebhookDefinitionStore staticStore, + IDynamicWebhookDefinitionStore dynamicStore, + IOptions options) + : base(options) { - _serviceProvider = serviceProvider; - _staticStore = staticStore; - _dynamicStore = dynamicStore; - _webhooksOptions = webhooksOptions.Value; - } - - public async virtual Task GetOrNullAsync(string name) - { - Check.NotNull(name, nameof(name)); - - var staticDefinition = await _staticStore.GetOrNullAsync(name); - var dynamicDefinition = await _dynamicStore.GetOrNullAsync(name); - - if (staticDefinition != null && dynamicDefinition != null) - { - return _webhooksOptions.DynamicWebhookStrategy switch - { - DynamicWebhookStrategy.Ignore => staticDefinition, - DynamicWebhookStrategy.Covering => dynamicDefinition, - DynamicWebhookStrategy.Merge => MergeWebhook(staticDefinition, dynamicDefinition), - _ => staticDefinition - }; - } - - return staticDefinition ?? dynamicDefinition; + ServiceProvider = serviceProvider; + StaticStore = staticStore; + DynamicStore = dynamicStore; } public async virtual Task GetAsync(string name) { - var webhook = await GetOrNullAsync(name); - if (webhook == null) - { - throw new AbpException("Undefined webhook: " + name); - } - - return webhook; + return await GetOrNullAsync(name) ?? throw new AbpException("Undefined webhook: " + name); } - public async virtual Task> GetWebhooksAsync() + public async virtual Task GetGroupAsync(string name) { - var staticWebhooks = await _staticStore.GetWebhooksAsync(); - var dynamicWebhooks = await _dynamicStore.GetWebhooksAsync(); - - // 根据策略处理Webhook定义 - return _webhooksOptions.DynamicWebhookStrategy switch - { - DynamicWebhookStrategy.Ignore => await GetWebhooksWithIgnoreStrategy(staticWebhooks, dynamicWebhooks), - DynamicWebhookStrategy.Covering => await GetWebhooksWithCoveringStrategy(staticWebhooks, dynamicWebhooks), - DynamicWebhookStrategy.Merge => await GetWebhooksWithMergeStrategy(staticWebhooks, dynamicWebhooks), - _ => await GetWebhooksWithIgnoreStrategy(staticWebhooks, dynamicWebhooks) - }; + return await GetGroupOrNullAsync(name) ?? throw new AbpException("Undefined webhook group: " + name); } public async virtual Task GetGroupOrNullAsync(string name) { Check.NotNull(name, nameof(name)); - var staticDefinition = await _staticStore.GetGroupOrNullAsync(name); - var dynamicDefinition = await _dynamicStore.GetGroupOrNullAsync(name); + var staticGroupDefinition = await StaticStore.GetGroupOrNullAsync(name); + var dynamicGroupDefinition = await DynamicStore.GetGroupOrNullAsync(name); - if (staticDefinition != null && dynamicDefinition != null) - { - switch (_webhooksOptions.DynamicWebhookStrategy) - { - case DynamicWebhookStrategy.Ignore: - return staticDefinition; - case DynamicWebhookStrategy.Covering: - return dynamicDefinition; - case DynamicWebhookStrategy.Merge: - MergeGroupWebhooks(staticDefinition, dynamicDefinition); - return staticDefinition; - default: - return staticDefinition; - } - } + return await GetGroupDefinitionAsync(staticGroupDefinition, dynamicGroupDefinition); + } + + public async virtual Task> GetGroupsAsync() + { + var staticGroupDefinitions = await StaticStore.GetGroupsAsync(); + var dynamicGroupDefinitions = await DynamicStore.GetGroupsAsync(); - return staticDefinition ?? dynamicDefinition; + return await GetGroupDefinitionsAsync(staticGroupDefinitions, dynamicGroupDefinitions); } - public async virtual Task GetGroupAsync(string name) + public async virtual Task GetOrNullAsync(string name) { - var webhookGroup = await GetGroupOrNullAsync(name); - if (webhookGroup == null) - { - throw new AbpException("Undefined webhook group: " + name); - } + Check.NotNull(name, nameof(name)); - return webhookGroup; + var staticDefinition = await StaticStore.GetOrNullAsync(name); + var dynamicDefinition = await DynamicStore.GetOrNullAsync(name); + + return await GetDefinitionAsync(staticDefinition, dynamicDefinition); } - public async virtual Task> GetGroupsAsync() + public async virtual Task> GetWebhooksAsync() { - var staticGroups = await _staticStore.GetGroupsAsync(); - var dynamicGroups = await _dynamicStore.GetGroupsAsync(); + var staticDefinitions = await StaticStore.GetWebhooksAsync(); + var dynamicDefinitions = await DynamicStore.GetWebhooksAsync(); - // 根据策略处理分组定义 - return _webhooksOptions.DynamicWebhookStrategy switch - { - DynamicWebhookStrategy.Ignore => await GetGroupsWithIgnoreStrategy(staticGroups, dynamicGroups), - DynamicWebhookStrategy.Covering => await GetGroupsWithCoveringStrategy(staticGroups, dynamicGroups), - DynamicWebhookStrategy.Merge => await GetGroupsWithMergeStrategy(staticGroups, dynamicGroups), - _ => await GetGroupsWithIgnoreStrategy(staticGroups, dynamicGroups) - }; + return await GetDefinitionsAsync(staticDefinitions, dynamicDefinitions); } - public async Task IsAvailableAsync(Guid? tenantId, string name) + public async virtual Task IsAvailableAsync(Guid? tenantId, string name) { - if (tenantId == null) // host allowed to subscribe all webhooks + if (tenantId == null) { return true; } @@ -149,8 +98,8 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD return true; } - var currentTenant = _serviceProvider.GetRequiredService(); - var featureChecker = _serviceProvider.GetRequiredService(); + var currentTenant = ServiceProvider.GetRequiredService(); + var featureChecker = ServiceProvider.GetRequiredService(); using (currentTenant.Change(tenantId)) { if (!await featureChecker.IsEnabledAsync(true, webhookDefinition.RequiredFeatures.ToArray())) @@ -162,111 +111,34 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD return true; } - #region Webhook定义策略 - - /// - /// 忽略策略:静态优先,过滤掉同名的动态Webhook - /// - protected virtual Task> GetWebhooksWithIgnoreStrategy( - IReadOnlyList staticWebhooks, - IReadOnlyList dynamicWebhooks) + protected override string GetDefinitionKey(WebhookDefinition definition) { - var staticWebhookNames = staticWebhooks - .Select(p => p.Name) - .ToImmutableHashSet(); - - return Task.FromResult>( - staticWebhooks - .Concat(dynamicWebhooks.Where(d => !staticWebhookNames.Contains(d.Name))) - .ToImmutableList() - ); + return definition.Name; } - /// - /// 覆盖策略:动态完全覆盖静态Webhook - /// - protected virtual Task> GetWebhooksWithCoveringStrategy( - IReadOnlyList staticWebhooks, - IReadOnlyList dynamicWebhooks) + protected override string GetGroupDefinitionKey(WebhookGroupDefinition groupDefinition) { - var dynamicWebhookNames = dynamicWebhooks - .Select(p => p.Name) - .ToImmutableHashSet(); - - // 动态Webhook完全覆盖静态Webhook - var result = dynamicWebhooks - .Concat(staticWebhooks.Where(s => !dynamicWebhookNames.Contains(s.Name))) - .ToImmutableList(); - - return Task.FromResult>(result); + return groupDefinition.Name; } - /// - /// 合并策略:合并静态和动态Webhook,创建新实例 - /// - protected virtual Task> GetWebhooksWithMergeStrategy( - IReadOnlyList staticWebhooks, - IReadOnlyList dynamicWebhooks) + protected override Task MergeDefinitionAsync(WebhookDefinition targetDefinition, WebhookDefinition sourceDefinition) { - var mergedWebhooks = new Dictionary(); + var displayName = sourceDefinition.DisplayName ?? targetDefinition.DisplayName; + var description = sourceDefinition.Description ?? targetDefinition.Description; - // 先添加所有静态Webhook - foreach (var staticWebhook in staticWebhooks) - { - mergedWebhooks[staticWebhook.Name] = staticWebhook; - } - - // 合并动态Webhook - foreach (var dynamicWebhook in dynamicWebhooks) - { - if (mergedWebhooks.TryGetValue(dynamicWebhook.Name, out var existingWebhook)) - { - // Webhook已存在,创建新的合并Webhook - var mergedWebhook = MergeWebhook(existingWebhook, dynamicWebhook); - mergedWebhooks[dynamicWebhook.Name] = mergedWebhook; - } - else - { - // 添加新的动态Webhook - mergedWebhooks[dynamicWebhook.Name] = dynamicWebhook; - } - } - - return Task.FromResult>(mergedWebhooks.Values.ToImmutableList()); - } - - /// - /// 合并两个Webhook定义,返回新的 WebhookDefinition 实例 - /// - protected virtual WebhookDefinition MergeWebhook( - WebhookDefinition staticWebhook, - WebhookDefinition dynamicWebhook) - { - // 决定使用哪个显示名称(优先使用动态的) - var displayName = dynamicWebhook.DisplayName ?? staticWebhook.DisplayName; - - // 决定使用哪个描述(优先使用动态的) - var description = dynamicWebhook.Description ?? staticWebhook.Description; - - // 创建新的Webhook实例(WebhookDefinition的Name是只读的) var mergedWebhook = new WebhookDefinition( - staticWebhook.Name, // 保持名称不变 + targetDefinition.Name, displayName, description - ); - - // 设置分组名称(优先使用动态的) - if (!string.IsNullOrEmpty(dynamicWebhook.GroupName)) - { - mergedWebhook.GroupName = dynamicWebhook.GroupName; - } - else if (!string.IsNullOrEmpty(staticWebhook.GroupName)) + ) { - mergedWebhook.GroupName = staticWebhook.GroupName; - } + GroupName = + !string.IsNullOrWhiteSpace(sourceDefinition.GroupName) + ? sourceDefinition.GroupName + : targetDefinition.GroupName + }; - // 合并必需的功能特性 - foreach (var feature in staticWebhook.RequiredFeatures) + foreach (var feature in targetDefinition.RequiredFeatures) { if (!mergedWebhook.RequiredFeatures.Contains(feature)) { @@ -274,125 +146,42 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD } } - foreach (var feature in dynamicWebhook.RequiredFeatures) + foreach (var feature in sourceDefinition.RequiredFeatures) { if (!mergedWebhook.RequiredFeatures.Contains(feature)) { mergedWebhook.RequiredFeatures.Add(feature); } } - - // 合并属性(动态覆盖静态) - foreach (var property in staticWebhook.Properties) + + foreach (var property in targetDefinition.Properties) { mergedWebhook.Properties[property.Key] = property.Value; } - foreach (var property in dynamicWebhook.Properties) + foreach (var property in sourceDefinition.Properties) { mergedWebhook.Properties[property.Key] = property.Value; } - return mergedWebhook; - } - - #endregion - - #region 分组定义策略 - - /// - /// 忽略策略:静态优先,过滤掉同名的动态分组 - /// - protected virtual Task> GetGroupsWithIgnoreStrategy( - IReadOnlyList staticGroups, - IReadOnlyList dynamicGroups) - { - var staticGroupNames = staticGroups - .Select(p => p.Name) - .ToImmutableHashSet(); - - return Task.FromResult>( - staticGroups - .Concat(dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name))) - .ToImmutableList() - ); - } - - /// - /// 覆盖策略:动态完全覆盖静态分组 - /// - protected virtual Task> GetGroupsWithCoveringStrategy( - IReadOnlyList staticGroups, - IReadOnlyList dynamicGroups) - { - var dynamicGroupNames = dynamicGroups - .Select(p => p.Name) - .ToImmutableHashSet(); - - var result = dynamicGroups - .Concat(staticGroups.Where(s => !dynamicGroupNames.Contains(s.Name))) - .ToImmutableList(); - - return Task.FromResult>(result); - } - - /// - /// 合并策略:合并静态和动态分组 - /// - protected virtual Task> GetGroupsWithMergeStrategy( - IReadOnlyList staticGroups, - IReadOnlyList dynamicGroups) - { - var mergedGroups = new Dictionary(); - - // 先添加所有静态分组 - foreach (var staticGroup in staticGroups) - { - mergedGroups[staticGroup.Name] = staticGroup; - } - - // 合并动态分组 - foreach (var dynamicGroup in dynamicGroups) - { - if (mergedGroups.TryGetValue(dynamicGroup.Name, out var existingGroup)) - { - // 分组已存在,合并Webhook - MergeGroupWebhooks(existingGroup, dynamicGroup); - } - else - { - // 添加新的动态分组 - mergedGroups[dynamicGroup.Name] = dynamicGroup; - } - } - - return Task.FromResult>( - mergedGroups.Values.ToImmutableList() - ); + return Task.FromResult(mergedWebhook); } - /// - /// 合并分组的Webhook列表 - /// - private void MergeGroupWebhooks(WebhookGroupDefinition target, WebhookGroupDefinition source) + protected override Task MergeGroupDefinitionAsync(WebhookGroupDefinition targetGroupDefinition, WebhookGroupDefinition sourceGroupDefinition) { - foreach (var sourceWebhook in source.Webhooks) + foreach (var sourceWebhook in sourceGroupDefinition.Webhooks) { - var existingWebhook = target.GetWebhookOrNull(sourceWebhook.Name); + var existingWebhook = targetGroupDefinition.GetWebhookOrNull(sourceWebhook.Name); if (existingWebhook == null) { - // Webhook不存在,直接添加 - var newWebhook = target.AddWebhook( + var newWebhook = targetGroupDefinition.AddWebhook( sourceWebhook.Name, sourceWebhook.DisplayName, sourceWebhook.Description ); + newWebhook.GroupName = targetGroupDefinition.Name; - // 设置分组名称 - newWebhook.GroupName = target.Name; - - // 复制必需的功能特性 foreach (var feature in sourceWebhook.RequiredFeatures) { if (!newWebhook.RequiredFeatures.Contains(feature)) @@ -401,7 +190,6 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD } } - // 复制属性 foreach (var property in sourceWebhook.Properties) { newWebhook.Properties[property.Key] = property.Value; @@ -409,13 +197,11 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD } else { - // Webhook已存在,合并属性 foreach (var property in sourceWebhook.Properties) { existingWebhook.Properties[property.Key] = property.Value; } - // 合并必需的功能特性 foreach (var feature in sourceWebhook.RequiredFeatures) { if (!existingWebhook.RequiredFeatures.Contains(feature)) @@ -424,23 +210,19 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD } } - // 更新显示名称(如果源提供了) if (sourceWebhook.DisplayName != null) { existingWebhook.DisplayName = sourceWebhook.DisplayName; } - // 更新描述(如果源提供了) if (sourceWebhook.Description != null) { existingWebhook.Description = sourceWebhook.Description; } - // 更新分组名称(确保保持一致) - existingWebhook.GroupName = target.Name; + existingWebhook.GroupName = targetGroupDefinition.Name; } } + return Task.CompletedTask; } - - #endregion -} \ No newline at end of file +}