Browse Source

feat: Optimize dynamic configuration

- Optimize Dynamic AITool
- Optimize Dynamic Notification
- Optimize Dynamic Webhook
pull/1516/head
colin 2 months ago
parent
commit
0e18f2a3d2
  1. 163
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs
  2. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs
  3. 19
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/DynamicAItoolStrategy.cs
  4. 3
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/AbpNotificationsOptions.cs
  5. 19
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/DynamicNotificationStrategy.cs
  6. 351
      aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs
  7. 5
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/AbpWebhooksOptions.cs
  8. 19
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/DynamicWebhookStrategy.cs
  9. 320
      aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs

163
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinitionManager.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
@ -6,17 +7,21 @@ using Volo.Abp;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.AI.Tools;
public class AIToolDefinitionManager : IAIToolDefinitionManager, ISingletonDependency
{
protected readonly AbpAIToolsOptions AIToolOptions;
protected readonly IStaticAIToolDefinitionStore StaticStore;
protected readonly IDynamicAIToolDefinitionStore DynamicStore;
public AIToolDefinitionManager(
IStaticAIToolDefinitionStore staticStore,
IDynamicAIToolDefinitionStore dynamicStore)
IDynamicAIToolDefinitionStore dynamicStore,
IOptions<AbpAIToolsOptions> aiToolOptions)
{
StaticStore = staticStore;
DynamicStore = dynamicStore;
AIToolOptions = aiToolOptions.Value;
}
public virtual async Task<AIToolDefinition> GetAsync(string name)
@ -34,19 +39,165 @@ public class AIToolDefinitionManager : IAIToolDefinitionManager, ISingletonDepen
{
Check.NotNull(name, nameof(name));
return await StaticStore.GetOrNullAsync(name) ?? await DynamicStore.GetOrNullAsync(name);
return await StaticStore.GetOrNullAsync(name) ??
await DynamicStore.GetOrNullAsync(name);
}
public virtual async Task<IReadOnlyList<AIToolDefinition>> GetAllAsync()
{
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) // 默认使用合并策略
};
}
#region AI工具定义策略
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态AI工具
/// </summary>
protected virtual Task<IReadOnlyList<AIToolDefinition>> GetAIToolsWithIgnoreStrategy(
IReadOnlyList<AIToolDefinition> staticAITools,
IReadOnlyList<AIToolDefinition> dynamicAITools)
{
var staticAIToolNames = staticAITools
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicAITools = await DynamicStore.GetAllAsync();
return Task.FromResult<IReadOnlyList<AIToolDefinition>>(
staticAITools
.Concat(dynamicAITools.Where(d => !staticAIToolNames.Contains(d.Name)))
.ToImmutableList()
);
}
return staticAITools.Concat(dynamicAITools.Where(d => !staticAIToolNames.Contains(d.Name)))
/// <summary>
/// 覆盖策略:动态完全覆盖静态AI工具
/// </summary>
protected virtual Task<IReadOnlyList<AIToolDefinition>> GetAIToolsWithCoveringStrategy(
IReadOnlyList<AIToolDefinition> staticAITools,
IReadOnlyList<AIToolDefinition> dynamicAITools)
{
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);
}
}
/// <summary>
/// 合并策略:合并静态和动态AI工具,创建新实例
/// </summary>
protected virtual Task<IReadOnlyList<AIToolDefinition>> GetAIToolsWithMergeStrategy(
IReadOnlyList<AIToolDefinition> staticAITools,
IReadOnlyList<AIToolDefinition> dynamicAITools)
{
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)
{
if (mergedAITools.ContainsKey(deletedToolName))
{
mergedAITools.Remove(deletedToolName);
}
}
return Task.FromResult<IReadOnlyList<AIToolDefinition>>(mergedAITools.Values.ToImmutableList());
}
/// <summary>
/// 合并两个AI工具定义,返回新的 AIToolDefinition 实例
/// </summary>
protected virtual AIToolDefinition MergeAITool(
AIToolDefinition staticAITool,
AIToolDefinition dynamicAITool)
{
// 决定使用哪个提供者(优先使用动态的)
var provider = !string.IsNullOrEmpty(dynamicAITool.Provider)
? dynamicAITool.Provider
: staticAITool.Provider;
// 决定使用哪个描述(优先使用动态的)
var description = dynamicAITool.Description ?? staticAITool.Description;
// 创建新的AI工具实例(Name是只读的)
var mergedAITool = new AIToolDefinition(
staticAITool.Name, // 保持名称不变
provider,
description
);
// 设置是否启用(只要有一方启用,结果就是启用)
mergedAITool.IsEnabled = staticAITool.IsEnabled || dynamicAITool.IsEnabled;
// 设置是否为全局工具(只要有一方是全局,结果就是全局)
mergedAITool.IsGlobal = staticAITool.IsGlobal || dynamicAITool.IsGlobal;
// 合并状态检查器
foreach (var checker in staticAITool.StateCheckers)
{
if (!mergedAITool.StateCheckers.Contains(checker))
{
mergedAITool.StateCheckers.Add(checker);
}
}
foreach (var checker in dynamicAITool.StateCheckers)
{
if (!mergedAITool.StateCheckers.Contains(checker))
{
mergedAITool.StateCheckers.Add(checker);
}
}
// 合并属性(动态覆盖静态)
foreach (var property in staticAITool.Properties)
{
mergedAITool.Properties[property.Key] = property.Value;
}
foreach (var property in dynamicAITool.Properties)
{
mergedAITool.Properties[property.Key] = property.Value;
}
return mergedAITool;
}
#endregion
}

2
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsOptions.cs

@ -5,10 +5,12 @@ 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

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

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

@ -10,12 +10,15 @@ 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

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

351
aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Core/LINGYUN/Abp/Notifications/NotificationDefinitionManager.cs

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
@ -9,15 +10,18 @@ namespace LINGYUN.Abp.Notifications;
public class NotificationDefinitionManager : INotificationDefinitionManager, ITransientDependency
{
private readonly AbpNotificationsOptions _notificationsOptions;
private readonly IStaticNotificationDefinitionStore _staticStore;
private readonly IDynamicNotificationDefinitionStore _dynamicStore;
public NotificationDefinitionManager(
IStaticNotificationDefinitionStore staticStore,
IDynamicNotificationDefinitionStore dynamicStore)
IDynamicNotificationDefinitionStore dynamicStore,
IOptions<AbpNotificationsOptions> notificationsOptions)
{
_staticStore = staticStore;
_dynamicStore = dynamicStore;
_notificationsOptions = notificationsOptions.Value;
}
public async virtual Task<NotificationDefinition> GetAsync(string name)
@ -42,15 +46,16 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr
public async virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsAsync()
{
var staticNotifications = await _staticStore.GetNotificationsAsync();
var staticNotificationNames = staticNotifications
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicNotifications = await _dynamicStore.GetNotificationsAsync();
return staticNotifications
.Concat(dynamicNotifications.Where(d => !staticNotificationNames.Contains(d.Name)))
.ToImmutableList();
// 根据策略处理通知定义
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)
};
}
public async virtual Task<NotificationGroupDefinition> GetGroupOrNullAsync(string name)
@ -64,14 +69,334 @@ public class NotificationDefinitionManager : INotificationDefinitionManager, ITr
public async virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsAsync()
{
var staticGroups = await _staticStore.GetGroupsAsync();
var dynamicGroups = 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)
};
}
#region 通知定义策略
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态通知
/// </summary>
protected virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsWithIgnoreStrategy(
IReadOnlyList<NotificationDefinition> staticNotifications,
IReadOnlyList<NotificationDefinition> dynamicNotifications)
{
var staticNotificationNames = staticNotifications
.Select(p => p.Name)
.ToImmutableHashSet();
return Task.FromResult<IReadOnlyList<NotificationDefinition>>(
staticNotifications
.Concat(dynamicNotifications.Where(d => !staticNotificationNames.Contains(d.Name)))
.ToImmutableList()
);
}
/// <summary>
/// 覆盖策略:动态完全覆盖静态通知
/// </summary>
protected virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsWithCoveringStrategy(
IReadOnlyList<NotificationDefinition> staticNotifications,
IReadOnlyList<NotificationDefinition> dynamicNotifications)
{
var dynamicNotificationNames = dynamicNotifications
.Select(p => p.Name)
.ToImmutableHashSet();
// 动态通知完全覆盖静态通知
var result = dynamicNotifications
.Concat(staticNotifications.Where(s => !dynamicNotificationNames.Contains(s.Name)))
.ToImmutableList();
return Task.FromResult<IReadOnlyList<NotificationDefinition>>(result);
}
/// <summary>
/// 合并策略:合并静态和动态通知,创建新实例
/// </summary>
protected virtual Task<IReadOnlyList<NotificationDefinition>> GetNotificationsWithMergeStrategy(
IReadOnlyList<NotificationDefinition> staticNotifications,
IReadOnlyList<NotificationDefinition> dynamicNotifications)
{
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());
}
/// <summary>
/// 合并两个通知定义,返回新的 NotificationDefinition 实例
/// </summary>
protected virtual NotificationDefinition MergeNotification(
NotificationDefinition staticNotification,
NotificationDefinition dynamicNotification)
{
// 决定使用哪个显示名称(优先使用动态的)
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 allowSubscriptionToClients = dynamicNotification.AllowSubscriptionToClients || staticNotification.AllowSubscriptionToClients;
// 创建新的通知实例
var mergedNotification = new NotificationDefinition(
staticNotification.Name, // 保持名称不变
displayName,
description,
notificationType,
lifetime,
contentType,
allowSubscriptionToClients
);
// 复制静态通知的属性
foreach (var property in staticNotification.Properties)
{
mergedNotification.Properties[property.Key] = property.Value;
}
// 复制动态通知的属性(覆盖同名的静态属性)
foreach (var property in dynamicNotification.Properties)
{
mergedNotification.Properties[property.Key] = property.Value;
}
// 合并提供者
foreach (var provider in staticNotification.Providers)
{
if (!mergedNotification.Providers.Contains(provider))
{
mergedNotification.Providers.Add(provider);
}
}
foreach (var provider in dynamicNotification.Providers)
{
if (!mergedNotification.Providers.Contains(provider))
{
mergedNotification.Providers.Add(provider);
}
}
// 合并模板(优先使用动态的)
if (dynamicNotification.Template != null)
{
mergedNotification.WithTemplate(dynamicNotification.Template);
}
else if (staticNotification.Template != null)
{
mergedNotification.WithTemplate(staticNotification.Template);
}
return mergedNotification;
}
#endregion
#region 分组定义策略
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态分组
/// </summary>
protected virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsWithIgnoreStrategy(
IReadOnlyList<NotificationGroupDefinition> staticGroups,
IReadOnlyList<NotificationGroupDefinition> dynamicGroups)
{
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicGroups = await _dynamicStore.GetGroupsAsync();
return Task.FromResult<IReadOnlyList<NotificationGroupDefinition>>(
staticGroups
.Concat(dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name)))
.ToImmutableList()
);
}
return staticGroups
.Concat(dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name)))
/// <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);
}
}
/// <summary>
/// 合并策略:合并静态和动态分组
/// </summary>
protected virtual Task<IReadOnlyList<NotificationGroupDefinition>> GetGroupsWithMergeStrategy(
IReadOnlyList<NotificationGroupDefinition> staticGroups,
IReadOnlyList<NotificationGroupDefinition> dynamicGroups)
{
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)
{
var existingNotification = target.GetNotificationOrNull(sourceNotification.Name);
if (existingNotification == null)
{
// 通知不存在,直接添加
var newNotification = target.AddNotification(
sourceNotification.Name,
sourceNotification.DisplayName,
sourceNotification.Description,
sourceNotification.NotificationType,
sourceNotification.NotificationLifetime,
sourceNotification.ContentType,
sourceNotification.AllowSubscriptionToClients
);
// 复制提供者
foreach (var provider in sourceNotification.Providers)
{
if (!newNotification.Providers.Contains(provider))
{
newNotification.Providers.Add(provider);
}
}
// 复制属性
foreach (var property in sourceNotification.Properties)
{
newNotification.Properties[property.Key] = property.Value;
}
// 复制模板
if (sourceNotification.Template != null)
{
newNotification.WithTemplate(sourceNotification.Template);
}
}
else
{
// 通知已存在,合并属性
foreach (var property in sourceNotification.Properties)
{
existingNotification.Properties[property.Key] = property.Value;
}
// 合并提供者
foreach (var provider in sourceNotification.Providers)
{
if (!existingNotification.Providers.Contains(provider))
{
existingNotification.Providers.Add(provider);
}
}
// 更新显示名称(如果源提供了)
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
}

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

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

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

320
aspnet-core/modules/webhooks/LINGYUN.Abp.Webhooks.Core/LINGYUN/Abp/Webhooks/WebhookDefinitionManager.cs

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
@ -13,6 +14,7 @@ namespace LINGYUN.Abp.Webhooks;
internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonDependency
{
private readonly AbpWebhooksOptions _webhooksOptions;
private readonly IServiceProvider _serviceProvider;
private readonly IStaticWebhookDefinitionStore _staticStore;
private readonly IDynamicWebhookDefinitionStore _dynamicStore;
@ -20,11 +22,13 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD
public WebhookDefinitionManager(
IServiceProvider serviceProvider,
IStaticWebhookDefinitionStore staticStore,
IDynamicWebhookDefinitionStore dynamicStore)
IDynamicWebhookDefinitionStore dynamicStore,
IOptions<AbpWebhooksOptions> webhooksOptions)
{
_serviceProvider = serviceProvider;
_staticStore = staticStore;
_dynamicStore = dynamicStore;
_webhooksOptions = webhooksOptions.Value;
}
public async virtual Task<WebhookDefinition> GetOrNullAsync(string name)
@ -49,15 +53,16 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD
public async virtual Task<IReadOnlyList<WebhookDefinition>> GetWebhooksAsync()
{
var staticWebhooks = await _staticStore.GetWebhooksAsync();
var staticWebhookNames = staticWebhooks
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicWebhooks = await _dynamicStore.GetWebhooksAsync();
return staticWebhooks
.Concat(dynamicWebhooks.Where(d => !staticWebhookNames.Contains(d.Name)))
.ToImmutableList();
// 根据策略处理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)
};
}
public async virtual Task<WebhookGroupDefinition> GetGroupOrNullAsync(string name)
@ -82,15 +87,16 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD
public async virtual Task<IReadOnlyList<WebhookGroupDefinition>> GetGroupsAsync()
{
var staticGroups = await _staticStore.GetGroupsAsync();
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicGroups = await _dynamicStore.GetGroupsAsync();
return staticGroups
.Concat(dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name)))
.ToImmutableList();
// 根据策略处理分组定义
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)
};
}
public async Task<bool> IsAvailableAsync(Guid? tenantId, string name)
@ -124,4 +130,286 @@ internal class WebhookDefinitionManager : IWebhookDefinitionManager, ISingletonD
return true;
}
}
#region Webhook定义策略
/// <summary>
/// 忽略策略:静态优先,过滤掉同名的动态Webhook
/// </summary>
protected virtual Task<IReadOnlyList<WebhookDefinition>> GetWebhooksWithIgnoreStrategy(
IReadOnlyList<WebhookDefinition> staticWebhooks,
IReadOnlyList<WebhookDefinition> dynamicWebhooks)
{
var staticWebhookNames = staticWebhooks
.Select(p => p.Name)
.ToImmutableHashSet();
return Task.FromResult<IReadOnlyList<WebhookDefinition>>(
staticWebhooks
.Concat(dynamicWebhooks.Where(d => !staticWebhookNames.Contains(d.Name)))
.ToImmutableList()
);
}
/// <summary>
/// 覆盖策略:动态完全覆盖静态Webhook
/// </summary>
protected virtual Task<IReadOnlyList<WebhookDefinition>> GetWebhooksWithCoveringStrategy(
IReadOnlyList<WebhookDefinition> staticWebhooks,
IReadOnlyList<WebhookDefinition> dynamicWebhooks)
{
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);
}
/// <summary>
/// 合并策略:合并静态和动态Webhook,创建新实例
/// </summary>
protected virtual Task<IReadOnlyList<WebhookDefinition>> GetWebhooksWithMergeStrategy(
IReadOnlyList<WebhookDefinition> staticWebhooks,
IReadOnlyList<WebhookDefinition> dynamicWebhooks)
{
var mergedWebhooks = new Dictionary<string, WebhookDefinition>();
// 先添加所有静态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, // 保持名称不变
displayName,
description
);
// 设置分组名称(优先使用动态的)
if (!string.IsNullOrEmpty(dynamicWebhook.GroupName))
{
mergedWebhook.GroupName = dynamicWebhook.GroupName;
}
else if (!string.IsNullOrEmpty(staticWebhook.GroupName))
{
mergedWebhook.GroupName = staticWebhook.GroupName;
}
// 合并必需的功能特性
foreach (var feature in staticWebhook.RequiredFeatures)
{
if (!mergedWebhook.RequiredFeatures.Contains(feature))
{
mergedWebhook.RequiredFeatures.Add(feature);
}
}
foreach (var feature in dynamicWebhook.RequiredFeatures)
{
if (!mergedWebhook.RequiredFeatures.Contains(feature))
{
mergedWebhook.RequiredFeatures.Add(feature);
}
}
// 合并属性(动态覆盖静态)
foreach (var property in staticWebhook.Properties)
{
mergedWebhook.Properties[property.Key] = property.Value;
}
foreach (var property in dynamicWebhook.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()
);
}
/// <summary>
/// 合并分组的Webhook列表
/// </summary>
private void MergeGroupWebhooks(WebhookGroupDefinition target, WebhookGroupDefinition source)
{
foreach (var sourceWebhook in source.Webhooks)
{
var existingWebhook = target.GetWebhookOrNull(sourceWebhook.Name);
if (existingWebhook == null)
{
// Webhook不存在,直接添加
var newWebhook = target.AddWebhook(
sourceWebhook.Name,
sourceWebhook.DisplayName,
sourceWebhook.Description
);
// 设置分组名称
newWebhook.GroupName = target.Name;
// 复制必需的功能特性
foreach (var feature in sourceWebhook.RequiredFeatures)
{
if (!newWebhook.RequiredFeatures.Contains(feature))
{
newWebhook.RequiredFeatures.Add(feature);
}
}
// 复制属性
foreach (var property in sourceWebhook.Properties)
{
newWebhook.Properties[property.Key] = property.Value;
}
}
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))
{
existingWebhook.RequiredFeatures.Add(feature);
}
}
// 更新显示名称(如果源提供了)
if (sourceWebhook.DisplayName != null)
{
existingWebhook.DisplayName = sourceWebhook.DisplayName;
}
// 更新描述(如果源提供了)
if (sourceWebhook.Description != null)
{
existingWebhook.Description = sourceWebhook.Description;
}
// 更新分组名称(确保保持一致)
existingWebhook.GroupName = target.Name;
}
}
}
#endregion
}
Loading…
Cancel
Save