Browse Source

feat: Rewrite part of the interface using DynamicDefinitionManager

pull/1565/head
colin 2 weeks ago
parent
commit
6580a39578
  1. 4
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj
  2. 9
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreModule.cs
  3. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/AbpAICoreOptions.cs
  4. 21
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/DynamicWorkspaceStrategy.cs
  5. 3
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs
  6. 252
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinitionManager.cs
  7. 197
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs
  8. 7
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs
  9. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs
  10. 19
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/DynamicAItoolStrategy.cs
  11. 1
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN.Abp.Notifications.Core.csproj
  12. 10
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsCoreModule.cs
  13. 3
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsOptions.cs
  14. 19
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/DynamicNotificationStrategy.cs
  15. 346
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs
  16. 4
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN.Abp.Webhooks.Core.csproj
  17. 16
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksCoreModule.cs
  18. 5
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs
  19. 19
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/DynamicWebhookStrategy.cs
  20. 360
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs

4
aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN.Abp.AI.Core.csproj

@ -26,4 +26,8 @@
<PackageReference Include="Volo.Abp.Timing" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\dynamic-definition\LINGYUN.Abp.Dynamic.Definitions.Core\LINGYUN.Abp.Dynamic.Definitions.Core.csproj" />
</ItemGroup>
</Project>

9
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<AbpDynamicDefinitionsOptions>(options =>
{
options.MapStrategy<WorkspaceDefinition>(DynamicDefinitionStrategy.Merge);
});
}
private static void AutoAddDefinitionProviders(IServiceCollection services)

2
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<IWorkspaceDefinitionProvider> DefinitionProviders { get; }
public ITypeList<IChatClientProvider> ChatClientProviders { get; }
public ITypeList<IKernelProvider> KernelProviders { get; }
@ -21,7 +20,6 @@ public class AbpAICoreOptions
public AbpAICoreOptions()
{
DynamicWorkspaceStrategy = DynamicWorkspaceStrategy.Merge;
DefinitionProviders = new TypeList<IWorkspaceDefinitionProvider>();
ChatClientProviders = new TypeList<IChatClientProvider>();
KernelProviders = new TypeList<IKernelProvider>();

21
aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/DynamicWorkspaceStrategy.cs

@ -1,21 +0,0 @@
namespace LINGYUN.Abp.AI.Workspaces;
/// <summary>
/// 动态工作区策略
/// </summary>
public enum DynamicWorkspaceStrategy
{
/// <summary>
/// 忽略动态工作区(静态优先)
/// </summary>
Ignore,
/// <summary>
/// 动态覆盖静态
/// </summary>
Covering,
/// <summary>
/// 合并策略
/// </summary>
Merge
}

3
aspnet-core/modules/ai/LINGYUN.Abp.AI.Core/LINGYUN/Abp/AI/Workspaces/WorkspaceDefinition.cs

@ -147,8 +147,7 @@ public class WorkspaceDefinition : IHasSimpleStateCheckers<WorkspaceDefinition>
Name = name;
Provider = provider;
ModelName = modelName;
_displayName = displayName;
_displayName = displayName;
DisplayName = displayName;
Description = description;
SystemPrompt = systemPrompt;
Instructions = instructions;

252
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<WorkspaceDefinition>,
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<AbpAICoreOptions> options)
IOptions<AbpDynamicDefinitionsOptions> options,
IOptions<AbpAICoreOptions> aIOptions)
: base(options)
{
StaticStore = staticStore;
DynamicStore = dynamicStore;
Options = options.Value;
AIOptions = aIOptions.Value;
}
public virtual async Task<WorkspaceDefinition> GetAsync(string name)
public async virtual Task<IReadOnlyList<WorkspaceDefinition>> 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<WorkspaceDefinition?> GetOrNullAsync(string name)
public async virtual Task<WorkspaceDefinition> 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<IReadOnlyList<WorkspaceDefinition>> GetAllAsync()
public async virtual Task<WorkspaceDefinition?> 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));
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态工作区
/// </summary>
protected virtual Task<IReadOnlyList<WorkspaceDefinition>> GetWorkspacesWithIgnoreStrategy(
IReadOnlyList<WorkspaceDefinition> staticWorkspaces,
IReadOnlyList<WorkspaceDefinition> dynamicWorkspaces)
{
var staticWorkspaceNames = staticWorkspaces
.Select(p => p.Name)
.ToImmutableHashSet();
var staticDefinition = await StaticStore.GetOrNullAsync(name);
var dynamicDefinition = await DynamicStore.GetOrNullAsync(name);
return Task.FromResult<IReadOnlyList<WorkspaceDefinition>>(
staticWorkspaces
.Concat(dynamicWorkspaces.Where(d => !staticWorkspaceNames.Contains(d.Name)))
.ToImmutableList()
);
return await GetDefinitionAsync(staticDefinition, dynamicDefinition);
}
/// <summary>
/// 覆盖策略:动态完全覆盖静态工作区
/// </summary>
protected virtual Task<IReadOnlyList<WorkspaceDefinition>> GetWorkspacesWithCoveringStrategy(
IReadOnlyList<WorkspaceDefinition> staticWorkspaces,
IReadOnlyList<WorkspaceDefinition> 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<IReadOnlyList<WorkspaceDefinition>>(result);
return definition.Name;
}
/// <summary>
/// 合并策略:合并静态和动态工作区,创建新实例
/// </summary>
protected virtual Task<IReadOnlyList<WorkspaceDefinition>> GetWorkspacesWithMergeStrategy(
IReadOnlyList<WorkspaceDefinition> staticWorkspaces,
IReadOnlyList<WorkspaceDefinition> dynamicWorkspaces)
protected override Task AfterMergedDefinitionsAsync(Dictionary<string, WorkspaceDefinition> mergedDefinitions)
{
var mergedWorkspaces = new Dictionary<string, WorkspaceDefinition>();
// 先添加所有静态工作区
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<IReadOnlyList<WorkspaceDefinition>>(mergedWorkspaces.Values.ToImmutableList());
return Task.CompletedTask;
}
/// <summary>
/// 合并两个工作区定义,返回新的 WorkspaceDefinition 实例
/// </summary>
protected virtual WorkspaceDefinition MergeWorkspace(
WorkspaceDefinition staticWorkspace,
WorkspaceDefinition dynamicWorkspace)
protected override Task<WorkspaceDefinition> 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
}

197
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<AIToolDefinition>,
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<AbpAIToolsOptions> aiToolOptions)
IOptions<AbpDynamicDefinitionsOptions> options,
IOptions<AbpAIToolsOptions> aIToolsOptions)
: base(options)
{
StaticStore = staticStore;
DynamicStore = dynamicStore;
AIToolOptions = aiToolOptions.Value;
AIToolsOptions = aIToolsOptions.Value;
}
public virtual async Task<AIToolDefinition> GetAsync(string name)
public async virtual Task<AIToolDefinition> 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<AIToolDefinition?> GetOrNullAsync(string name)
public async virtual Task<IReadOnlyList<AIToolDefinition>> 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<IReadOnlyList<AIToolDefinition>> GetAllAsync()
public async virtual Task<AIToolDefinition?> 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);
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态AI工具
/// </summary>
protected virtual Task<IReadOnlyList<AIToolDefinition>> GetAIToolsWithIgnoreStrategy(
IReadOnlyList<AIToolDefinition> staticAITools,
IReadOnlyList<AIToolDefinition> dynamicAITools)
{
var staticAIToolNames = staticAITools
.Select(p => p.Name)
.ToImmutableHashSet();
return Task.FromResult<IReadOnlyList<AIToolDefinition>>(
staticAITools
.Concat(dynamicAITools.Where(d => !staticAIToolNames.Contains(d.Name)))
.ToImmutableList()
);
return await GetDefinitionAsync(staticDefinition, dynamicDefinition);
}
/// <summary>
/// 覆盖策略:动态完全覆盖静态AI工具
/// </summary>
protected virtual Task<IReadOnlyList<AIToolDefinition>> GetAIToolsWithCoveringStrategy(
IReadOnlyList<AIToolDefinition> staticAITools,
IReadOnlyList<AIToolDefinition> 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<IReadOnlyList<AIToolDefinition>>(result);
return definition.Name;
}
/// <summary>
/// 合并策略:合并静态和动态AI工具,创建新实例
/// </summary>
protected virtual Task<IReadOnlyList<AIToolDefinition>> GetAIToolsWithMergeStrategy(
IReadOnlyList<AIToolDefinition> staticAITools,
IReadOnlyList<AIToolDefinition> dynamicAITools)
protected override Task AfterMergedDefinitionsAsync(Dictionary<string, AIToolDefinition> mergedDefinitions)
{
var mergedAITools = new Dictionary<string, AIToolDefinition>();
// 先添加所有静态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<IReadOnlyList<AIToolDefinition>>(mergedAITools.Values.ToImmutableList());
return Task.CompletedTask;
}
/// <summary>
/// 合并两个AI工具定义,返回新的 AIToolDefinition 实例
/// </summary>
protected virtual AIToolDefinition MergeAITool(
AIToolDefinition staticAITool,
AIToolDefinition dynamicAITool)
protected override Task<AIToolDefinition> 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
}
}

7
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<AbpAIResource>()
.AddVirtualJson("/LINGYUN/Abp/AI/Tools/Localization/Resources");
});
Configure<AbpDynamicDefinitionsOptions>(options =>
{
options.MapStrategy<AIToolDefinition>(DynamicDefinitionStrategy.Merge);
});
}
private static void AutoAddDefinitionProviders(IServiceCollection services)

2
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<IAIToolDefinitionProvider> DefinitionProviders { get; }
public DynamicAItoolStrategy DynamicAItoolStrategy { get; set; }
public ITypeList<IAIToolProvider> AIToolProviders { get; }
public HashSet<string> DeletedAITools { get; }
public AbpAIToolsOptions()
{
DynamicAItoolStrategy = DynamicAItoolStrategy.Merge;
DefinitionProviders = new TypeList<IAIToolDefinitionProvider>();
AIToolProviders = new TypeList<IAIToolProvider>();
DeletedAITools = new HashSet<string>();

19
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/DynamicAItoolStrategy.cs

@ -1,19 +0,0 @@
namespace LINGYUN.Abp.AI.Tools;
/// <summary>
/// 动态工具策略
/// </summary>
public enum DynamicAItoolStrategy : byte
{
/// <summary>
/// 忽略
/// </summary>
Ignore = 0,
/// <summary>
/// 覆盖
/// </summary>
Covering =1,
/// <summary>
/// 合并
/// </summary>
Merge = 2
}

1
aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN.Abp.Notifications.Core.csproj

@ -22,6 +22,7 @@
<ItemGroup>
<ProjectReference Include="..\..\..\framework\common\LINGYUN.Abp.RealTime\LINGYUN.Abp.RealTime.csproj" />
<ProjectReference Include="..\..\..\framework\dynamic-definition\LINGYUN.Abp.Dynamic.Definitions.Core\LINGYUN.Abp.Dynamic.Definitions.Core.csproj" />
</ItemGroup>
</Project>

10
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<AbpDynamicDefinitionsOptions>(options =>
{
options.MapStrategy<NotificationDefinition>(DynamicDefinitionStrategy.Merge);
options.MapStrategy<NotificationGroupDefinition>(DynamicDefinitionStrategy.Merge);
});
}
private void AutoAddDefinitionProviders(IServiceCollection services)

3
aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsOptions.cs

@ -10,15 +10,12 @@ public class AbpNotificationsOptions
/// </summary>
public ITypeList<INotificationDefinitionProvider> DefinitionProviders { get; }
public DynamicNotificationStrategy DynamicNotificationStrategy { get; set; }
public HashSet<string> DeletedNotifications { get; }
public HashSet<string> DeletedNotificationGroups { get; }
public AbpNotificationsOptions()
{
DynamicNotificationStrategy = DynamicNotificationStrategy.Merge;
DefinitionProviders = new TypeList<INotificationDefinitionProvider>();
DeletedNotifications = new HashSet<string>();

19
aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/DynamicNotificationStrategy.cs

@ -1,19 +0,0 @@
namespace LINGYUN.Abp.Notifications;
/// <summary>
/// 动态通知策略
/// </summary>
public enum DynamicNotificationStrategy : byte
{
/// <summary>
/// 忽略
/// </summary>
Ignore = 0,
/// <summary>
/// 覆盖
/// </summary>
Covering =1,
/// <summary>
/// 合并
/// </summary>
Merge = 2
}

346
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<NotificationGroupDefinition, NotificationDefinition>,
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<AbpNotificationsOptions> notificationsOptions)
IOptions<AbpDynamicDefinitionsOptions> options) : base(options)
{
_staticStore = staticStore;
_dynamicStore = dynamicStore;
_notificationsOptions = notificationsOptions.Value;
StaticStore = staticStore;
DynamicStore = dynamicStore;
}
public async virtual Task<NotificationDefinition> GetAsync(string name)
public async virtual Task<NotificationDefinition> GetAsync([NotNull] string name)
{
var notification = await GetOrNullAsync(name);
if (notification == null)
{
throw new AbpException("Undefined notification: " + name);
}
return notification;
}
public async virtual Task<NotificationDefinition?> 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<IReadOnlyList<NotificationDefinition>> 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<NotificationGroupDefinition?> 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<IReadOnlyList<NotificationGroupDefinition>> 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 通知定义策略
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态通知
/// </summary>
protected virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsWithIgnoreStrategy(
IReadOnlyList<NotificationDefinition> staticNotifications,
IReadOnlyList<NotificationDefinition> dynamicNotifications)
public async virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsAsync()
{
var staticNotificationNames = staticNotifications
.Select(p => p.Name)
.ToImmutableHashSet();
return Task.FromResult<IReadOnlyList<NotificationDefinition>>(
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);
}
/// <summary>
/// 覆盖策略:动态完全覆盖静态通知
/// </summary>
protected virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsWithCoveringStrategy(
IReadOnlyList<NotificationDefinition> staticNotifications,
IReadOnlyList<NotificationDefinition> dynamicNotifications)
public async virtual Task<NotificationDefinition?> 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<IReadOnlyList<NotificationDefinition>>(result);
return await GetDefinitionAsync(staticDefinition, dynamicDefinition);
}
/// <summary>
/// 合并策略:合并静态和动态通知,创建新实例
/// </summary>
protected virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsWithMergeStrategy(
IReadOnlyList<NotificationDefinition> staticNotifications,
IReadOnlyList<NotificationDefinition> dynamicNotifications)
protected override string GetDefinitionKey(NotificationDefinition definition)
{
var mergedNotifications = new Dictionary<string, NotificationDefinition>();
// 先添加所有静态通知
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<IReadOnlyList<NotificationDefinition>>(mergedNotifications.Values.ToImmutableList());
return definition.Name;
}
/// <summary>
/// 合并两个通知定义,返回新的 NotificationDefinition 实例
/// </summary>
protected virtual NotificationDefinition MergeNotification(
NotificationDefinition staticNotification,
NotificationDefinition dynamicNotification)
protected override Task<NotificationDefinition> 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 分组定义策略
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态分组
/// </summary>
protected virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsWithIgnoreStrategy(
IReadOnlyList<NotificationGroupDefinition> staticGroups,
IReadOnlyList<NotificationGroupDefinition> dynamicGroups)
protected override string GetGroupDefinitionKey(NotificationGroupDefinition groupDefinition)
{
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
return Task.FromResult<IReadOnlyList<NotificationGroupDefinition>>(
staticGroups
.Concat(dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name)))
.ToImmutableList()
);
}
/// <summary>
/// 覆盖策略:动态完全覆盖静态分组
/// </summary>
protected virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsWithCoveringStrategy(
IReadOnlyList<NotificationGroupDefinition> staticGroups,
IReadOnlyList<NotificationGroupDefinition> dynamicGroups)
{
var dynamicGroupNames = dynamicGroups
.Select(p => p.Name)
.ToImmutableHashSet();
var result = dynamicGroups
.Concat(staticGroups.Where(s => !dynamicGroupNames.Contains(s.Name)))
.ToImmutableList();
return Task.FromResult<IReadOnlyList<NotificationGroupDefinition>>(result);
return groupDefinition.Name;
}
/// <summary>
/// 合并策略:合并静态和动态分组
/// </summary>
protected virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsWithMergeStrategy(
IReadOnlyList<NotificationGroupDefinition> staticGroups,
IReadOnlyList<NotificationGroupDefinition> dynamicGroups)
protected override Task MergeGroupDefinitionAsync(NotificationGroupDefinition targetGroupDefinition, NotificationGroupDefinition sourceGroupDefinition)
{
var mergedGroups = new Dictionary<string, NotificationGroupDefinition>();
// 先添加所有静态分组
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<IReadOnlyList<NotificationGroupDefinition>>(
mergedGroups.Values.ToImmutableList()
);
}
/// <summary>
/// 合并分组的通知列表
/// </summary>
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
}
return Task.CompletedTask;
}
}

4
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN.Abp.Webhooks.Core.csproj

@ -17,4 +17,8 @@
<PackageReference Include="Volo.Abp.Features" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\framework\dynamic-definition\LINGYUN.Abp.Dynamic.Definitions.Core\LINGYUN.Abp.Dynamic.Definitions.Core.csproj" />
</ItemGroup>
</Project>

16
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<AbpDynamicDefinitionsOptions>(options =>
{
options.MapStrategy<WebhookDefinition>(DynamicDefinitionStrategy.Merge);
options.MapStrategy<WebhookGroupDefinition>(DynamicDefinitionStrategy.Merge);
});
}
private static void AutoAddDefinitionProviders(IServiceCollection services)
{
var definitionProviders = new List<Type>();

5
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs

@ -12,10 +12,6 @@ public class AbpWebhooksOptions
/// </summary>
public TimeSpan TimeoutDuration { get; set; }
/// <summary>
/// 动态Webhook策略
/// </summary>
public DynamicWebhookStrategy DynamicWebhookStrategy { get; set; }
/// <summary>
/// 默认最大发送次数
/// </summary>
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;

19
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/DynamicWebhookStrategy.cs

@ -1,19 +0,0 @@
namespace LINGYUN.Abp.Webhooks;
/// <summary>
/// 动态Webhook策略
/// </summary>
public enum DynamicWebhookStrategy : byte
{
/// <summary>
/// 忽略
/// </summary>
Ignore = 0,
/// <summary>
/// 覆盖
/// </summary>
Covering =1,
/// <summary>
/// 合并
/// </summary>
Merge = 2
}

360
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<WebhookGroupDefinition, WebhookDefinition>,
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<AbpWebhooksOptions> webhooksOptions)
IStaticWebhookDefinitionStore staticStore,
IDynamicWebhookDefinitionStore dynamicStore,
IOptions<AbpDynamicDefinitionsOptions> options)
: base(options)
{
_serviceProvider = serviceProvider;
_staticStore = staticStore;
_dynamicStore = dynamicStore;
_webhooksOptions = webhooksOptions.Value;
}
public async virtual Task<WebhookDefinition?> 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<WebhookDefinition> 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<IReadOnlyList<WebhookDefinition>> GetWebhooksAsync()
public async virtual Task<WebhookGroupDefinition> 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<WebhookGroupDefinition?> 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<IReadOnlyList<WebhookGroupDefinition>> GetGroupsAsync()
{
var staticGroupDefinitions = await StaticStore.GetGroupsAsync();
var dynamicGroupDefinitions = await DynamicStore.GetGroupsAsync();
return staticDefinition ?? dynamicDefinition;
return await GetGroupDefinitionsAsync(staticGroupDefinitions, dynamicGroupDefinitions);
}
public async virtual Task<WebhookGroupDefinition> GetGroupAsync(string name)
public async virtual Task<WebhookDefinition?> 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<IReadOnlyList<WebhookGroupDefinition>> GetGroupsAsync()
public async virtual Task<IReadOnlyList<WebhookDefinition>> 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<bool> IsAvailableAsync(Guid? tenantId, string name)
public async virtual Task<bool> 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<ICurrentTenant>();
var featureChecker = _serviceProvider.GetRequiredService<IFeatureChecker>();
var currentTenant = ServiceProvider.GetRequiredService<ICurrentTenant>();
var featureChecker = ServiceProvider.GetRequiredService<IFeatureChecker>();
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定义策略
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态Webhook
/// </summary>
protected virtual Task<IReadOnlyList<WebhookDefinition>> GetWebhooksWithIgnoreStrategy(
IReadOnlyList<WebhookDefinition> staticWebhooks,
IReadOnlyList<WebhookDefinition> dynamicWebhooks)
protected override string GetDefinitionKey(WebhookDefinition definition)
{
var staticWebhookNames = staticWebhooks
.Select(p => p.Name)
.ToImmutableHashSet();
return Task.FromResult<IReadOnlyList<WebhookDefinition>>(
staticWebhooks
.Concat(dynamicWebhooks.Where(d => !staticWebhookNames.Contains(d.Name)))
.ToImmutableList()
);
return definition.Name;
}
/// <summary>
/// 覆盖策略:动态完全覆盖静态Webhook
/// </summary>
protected virtual Task<IReadOnlyList<WebhookDefinition>> GetWebhooksWithCoveringStrategy(
IReadOnlyList<WebhookDefinition> staticWebhooks,
IReadOnlyList<WebhookDefinition> 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<IReadOnlyList<WebhookDefinition>>(result);
return groupDefinition.Name;
}
/// <summary>
/// 合并策略:合并静态和动态Webhook,创建新实例
/// </summary>
protected virtual Task<IReadOnlyList<WebhookDefinition>> GetWebhooksWithMergeStrategy(
IReadOnlyList<WebhookDefinition> staticWebhooks,
IReadOnlyList<WebhookDefinition> dynamicWebhooks)
protected override Task<WebhookDefinition> MergeDefinitionAsync(WebhookDefinition targetDefinition, WebhookDefinition sourceDefinition)
{
var mergedWebhooks = new Dictionary<string, WebhookDefinition>();
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<IReadOnlyList<WebhookDefinition>>(mergedWebhooks.Values.ToImmutableList());
}
/// <summary>
/// 合并两个Webhook定义,返回新的 WebhookDefinition 实例
/// </summary>
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 分组定义策略
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态分组
/// </summary>
protected virtual Task<IReadOnlyList<WebhookGroupDefinition>> GetGroupsWithIgnoreStrategy(
IReadOnlyList<WebhookGroupDefinition> staticGroups,
IReadOnlyList<WebhookGroupDefinition> dynamicGroups)
{
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
return Task.FromResult<IReadOnlyList<WebhookGroupDefinition>>(
staticGroups
.Concat(dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name)))
.ToImmutableList()
);
}
/// <summary>
/// 覆盖策略:动态完全覆盖静态分组
/// </summary>
protected virtual Task<IReadOnlyList<WebhookGroupDefinition>> GetGroupsWithCoveringStrategy(
IReadOnlyList<WebhookGroupDefinition> staticGroups,
IReadOnlyList<WebhookGroupDefinition> dynamicGroups)
{
var dynamicGroupNames = dynamicGroups
.Select(p => p.Name)
.ToImmutableHashSet();
var result = dynamicGroups
.Concat(staticGroups.Where(s => !dynamicGroupNames.Contains(s.Name)))
.ToImmutableList();
return Task.FromResult<IReadOnlyList<WebhookGroupDefinition>>(result);
}
/// <summary>
/// 合并策略:合并静态和动态分组
/// </summary>
protected virtual Task<IReadOnlyList<WebhookGroupDefinition>> GetGroupsWithMergeStrategy(
IReadOnlyList<WebhookGroupDefinition> staticGroups,
IReadOnlyList<WebhookGroupDefinition> dynamicGroups)
{
var mergedGroups = new Dictionary<string, WebhookGroupDefinition>();
// 先添加所有静态分组
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<IReadOnlyList<WebhookGroupDefinition>>(
mergedGroups.Values.ToImmutableList()
);
return Task.FromResult(mergedWebhook);
}
/// <summary>
/// 合并分组的Webhook列表
/// </summary>
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
}
}

Loading…
Cancel
Save