From e68dea143e1cb93c7c46111fcbe3b06f76b1dfab Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 18 May 2026 13:57:16 +0800 Subject: [PATCH 1/3] feat: Add notification publish interceptor --- .../INotificationPublishInterceptor.cs | 20 +++++ .../NotificationPublishProvider.cs | 77 ++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishInterceptor.cs diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishInterceptor.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishInterceptor.cs new file mode 100644 index 000000000..27437a1b5 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/INotificationPublishInterceptor.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; + +namespace LINGYUN.Abp.Notifications; +/// +/// 通知发布拦截器, 实现此接口来增加发布前的自定义检查 +/// +public interface INotificationPublishInterceptor +{ + /// + /// 拦截优先级,数值越小优先级越高 + /// + int Priority { get; } + + /// + /// 在通知发布前执行检查 + /// + /// 通知信息 + /// true-允许发布, false-阻止发布 + Task CanPublishAsync(NotificationInfo notification); +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs index 8bf1cc899..8e52907a7 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications/LINGYUN/Abp/Notifications/NotificationPublishProvider.cs @@ -1,6 +1,8 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -21,6 +23,11 @@ public abstract class NotificationPublishProvider : INotificationPublishProvider public ICancellationTokenProvider CancellationTokenProvider => ServiceProvider.LazyGetService(NullCancellationTokenProvider.Instance); + private IEnumerable _interceptors; + protected IEnumerable Interceptors => + _interceptors ??= ServiceProvider.LazyGetService>() + ?? Enumerable.Empty(); + public async virtual Task CanPublishAsync(NotificationInfo notification) { return await CanPublishAsync(notification, GetCancellationToken()); @@ -28,9 +35,23 @@ public abstract class NotificationPublishProvider : INotificationPublishProvider public async Task PublishAsync(NotificationPublishContext context) { - await PublishAsync(context, GetCancellationToken()); + var cancellationToken = GetCancellationToken(); + + // 通知拦截器检查 + if (!await ExecuteInterceptorsAsync(context, cancellationToken)) + { + return; + } + + await PublishAsync(context, cancellationToken); } + /// + /// 是否允许发布通知 + /// + /// + /// + /// protected virtual Task CanPublishAsync( NotificationInfo notification, CancellationToken cancellationToken = default) @@ -38,6 +59,60 @@ public abstract class NotificationPublishProvider : INotificationPublishProvider return Task.FromResult(true); } + /// + /// 执行通知发布拦截器 + /// + /// + /// + /// + protected virtual async Task ExecuteInterceptorsAsync( + NotificationPublishContext context, + CancellationToken cancellationToken = default) + { + if (context.Notification.Type == NotificationType.System || + context.Notification.Type == NotificationType.ServiceCallback) + { + Logger.LogDebug( + "System or service callback notification {NotificationName}, skip all interceptors.", + context.Notification.Name); + return true; + } + + if (!Interceptors.Any()) + { + Logger.LogDebug("No notification publishing interceptors have been registered."); + return true; + } + + var orderedInterceptors = Interceptors.OrderBy(i => i.Priority); + + foreach (var interceptor in orderedInterceptors) + { + try + { + if (!await interceptor.CanPublishAsync(context.Notification)) + { + var reason = string.Format("The interceptor {0} prevented the publication of the notification {1}.", + interceptor.GetType().Name, + context.Notification.Name); + + context.Cancel(reason); + + Logger.LogWarning(reason); + return false; + } + } + catch (Exception ex) + { + Logger.LogWarning(ex, + "An exception occurred while executing the interceptor {InterceptorType}. By default, publication is allowed.", + interceptor.GetType().Name); + } + } + + return true; + } + protected virtual CancellationToken GetCancellationToken(CancellationToken cancellationToken = default) { return CancellationTokenProvider.FallbackToProvider(cancellationToken); From 24dad660f8835a785559952822578cf22a9d444e Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 18 May 2026 13:57:58 +0800 Subject: [PATCH 2/3] feat: Implement the notification workday check interceptor --- aspnet-core/LINGYUN.MicroService.Aspire.slnx | 1 + .../FodyWeavers.xml | 3 + .../LINGYUN.Abp.Notifications.Calendar.csproj | 22 +++++++ .../AbpNotificationsCalendarModule.cs | 16 +++++ .../WorkdayCalendarPublishInterceptor.cs | 66 +++++++++++++++++++ .../README.md | 15 +++++ 6 files changed, 123 insertions(+) create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/FodyWeavers.xml create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN.Abp.Notifications.Calendar.csproj create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/AbpNotificationsCalendarModule.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/WorkdayCalendarPublishInterceptor.cs create mode 100644 aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/README.md diff --git a/aspnet-core/LINGYUN.MicroService.Aspire.slnx b/aspnet-core/LINGYUN.MicroService.Aspire.slnx index a19022d70..f44c32ec2 100644 --- a/aspnet-core/LINGYUN.MicroService.Aspire.slnx +++ b/aspnet-core/LINGYUN.MicroService.Aspire.slnx @@ -448,6 +448,7 @@ + diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/FodyWeavers.xml b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/FodyWeavers.xml new file mode 100644 index 000000000..1715698cc --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN.Abp.Notifications.Calendar.csproj b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN.Abp.Notifications.Calendar.csproj new file mode 100644 index 000000000..3715f079e --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN.Abp.Notifications.Calendar.csproj @@ -0,0 +1,22 @@ + + + + + + + net10.0 + LINGYUN.Abp.Notifications.Calendar + LINGYUN.Abp.Notifications.Calendar + false + false + false + enable + + + + + + + + + diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/AbpNotificationsCalendarModule.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/AbpNotificationsCalendarModule.cs new file mode 100644 index 000000000..781030660 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/AbpNotificationsCalendarModule.cs @@ -0,0 +1,16 @@ +using LINGYUN.Abp.Calendar; +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.Notifications.Calendar; + +[DependsOn( + typeof(AbpNotificationsModule), + typeof(AbpCalendarModule))] +public class AbpNotificationsCalendarModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.AddTransient(); + } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/WorkdayCalendarPublishInterceptor.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/WorkdayCalendarPublishInterceptor.cs new file mode 100644 index 000000000..a24d997bd --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/WorkdayCalendarPublishInterceptor.cs @@ -0,0 +1,66 @@ +using LINGYUN.Abp.Calendar; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Timing; + +namespace LINGYUN.Abp.Notifications.Calendar; + +/// +/// 工作日通知发布拦截器 +/// +public class WorkdayCalendarPublishInterceptor : INotificationPublishInterceptor, ITransientDependency +{ + private readonly IClock _clock; + private readonly ICurrentTenant _currentTenant; + private readonly IWorkdayCalendarService _workdayCalendarService; + public ILogger Logger { protected get; set; } + + public WorkdayCalendarPublishInterceptor( + IClock clock, + ICurrentTenant currentTenant, + IWorkdayCalendarService workdayCalendarService) + { + _clock = clock; + _currentTenant = currentTenant; + _workdayCalendarService = workdayCalendarService; + + Logger = NullLogger.Instance; + } + + public int Priority => 100; + + public async Task CanPublishAsync(NotificationInfo notification) + { + try + { + using (_currentTenant.Change(notification.TenantId)) + { + var isWorktime = await _workdayCalendarService.IsInWorkTimeAsync(_clock.Now); + + if (!isWorktime) + { + Logger.LogInformation( + "Notification {NotificationName} was blocked during the working time check. Type: {NotificationType}, Time: {Time}.", + notification.Name, + notification.Type, + _clock.Now.ToString("yyyy-MM-dd HH:mm:ss")); + return false; + } + + return true; + } + } + catch (Exception ex) + { + Logger.LogWarning(ex, + "If an abnormality occurs during the working day check, the default setting allows {NotificationName} to be notified to publish.", + notification.Name); + + return true; + } + } +} diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/README.md b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/README.md new file mode 100644 index 000000000..8695a85e8 --- /dev/null +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/README.md @@ -0,0 +1,15 @@ +# LINGYUN.Abp.Notifications.Calendar + +通知发布提供程序的工作日检查拦截器, 非系统定义工作日时不发布通知. + +#### 注意 + + +## 功能特性 + + +## 配置使用 + + +## 更多信息 + From 978a524a995d41866069f1d240034dd0bacb755c Mon Sep 17 00:00:00 2001 From: colin Date: Mon, 18 May 2026 14:22:45 +0800 Subject: [PATCH 3/3] feat: Add the dependency of the notification workday interceptor module --- aspnet-core/LINGYUN.MicroService.All.slnx | 4 ++++ aspnet-core/LINGYUN.MicroService.SingleProject.slnx | 4 ++++ .../LINGYUN.Abp.MicroService.MessageService.csproj | 1 + .../MessageServiceModule.cs | 2 ++ .../LINGYUN/Abp/Calendar/WorkdayCalendarService.cs | 4 ++-- .../Calendar/WorkdayCalendarPublishInterceptor.cs | 3 +-- .../LY.MicroService.Applications.Single.csproj | 1 + .../MicroServiceApplicationsSingleModule.cs | 3 +++ .../LY.MicroService.RealtimeMessage.HttpApi.Host.csproj | 1 + .../RealtimeMessageHttpApiHostModule.cs | 2 ++ 10 files changed, 21 insertions(+), 4 deletions(-) diff --git a/aspnet-core/LINGYUN.MicroService.All.slnx b/aspnet-core/LINGYUN.MicroService.All.slnx index 011923378..a990dac8d 100644 --- a/aspnet-core/LINGYUN.MicroService.All.slnx +++ b/aspnet-core/LINGYUN.MicroService.All.slnx @@ -105,6 +105,9 @@ + + + @@ -433,6 +436,7 @@ + diff --git a/aspnet-core/LINGYUN.MicroService.SingleProject.slnx b/aspnet-core/LINGYUN.MicroService.SingleProject.slnx index 269419841..7f687aba2 100644 --- a/aspnet-core/LINGYUN.MicroService.SingleProject.slnx +++ b/aspnet-core/LINGYUN.MicroService.SingleProject.slnx @@ -36,6 +36,9 @@ + + + @@ -96,6 +99,7 @@ + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/LINGYUN.Abp.MicroService.MessageService.csproj b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/LINGYUN.Abp.MicroService.MessageService.csproj index c266d7bb7..d56ea12ac 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/LINGYUN.Abp.MicroService.MessageService.csproj +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/LINGYUN.Abp.MicroService.MessageService.csproj @@ -55,6 +55,7 @@ + diff --git a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/MessageServiceModule.cs b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/MessageServiceModule.cs index 5445db5cd..4bc4d45f3 100644 --- a/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/MessageServiceModule.cs +++ b/aspnet-core/aspire/LINGYUN.Abp.MicroService.MessageService/MessageServiceModule.cs @@ -23,6 +23,7 @@ using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; using LINGYUN.Abp.MessageService; using LINGYUN.Abp.MessageService.EntityFrameworkCore; using LINGYUN.Abp.Notifications; +using LINGYUN.Abp.Notifications.Calendar; using LINGYUN.Abp.Notifications.Common; using LINGYUN.Abp.Notifications.Emailing; using LINGYUN.Abp.Notifications.EntityFrameworkCore; @@ -102,6 +103,7 @@ namespace LINGYUN.Abp.MicroService.MessageService; typeof(AbpNotificationsWeChatWorkModule), typeof(AbpNotificationsExceptionHandlingModule), typeof(AbpNotificationsTemplatingModule), + typeof(AbpNotificationsCalendarModule), typeof(AbpWeChatWorkHandlersModule), typeof(AbpWeChatOfficialHandlersModule), typeof(AbpIdentityNotificationsModule), diff --git a/aspnet-core/framework/calendars/LINGYUN.Abp.Calendar/LINGYUN/Abp/Calendar/WorkdayCalendarService.cs b/aspnet-core/framework/calendars/LINGYUN.Abp.Calendar/LINGYUN/Abp/Calendar/WorkdayCalendarService.cs index e63ff3150..56f796efc 100644 --- a/aspnet-core/framework/calendars/LINGYUN.Abp.Calendar/LINGYUN/Abp/Calendar/WorkdayCalendarService.cs +++ b/aspnet-core/framework/calendars/LINGYUN.Abp.Calendar/LINGYUN/Abp/Calendar/WorkdayCalendarService.cs @@ -87,7 +87,7 @@ public class WorkdayCalendarService : IWorkdayCalendarService, ITransientDepende return false; } if (_options.EnableWorkTimeRestriction && - !_options.DefaultWorkTimes.Any(timeRange => timeRange.StartTime >= time.TimeOfDay && timeRange.EndTime <= time.TimeOfDay)) + !_options.DefaultWorkTimes.Any(timeRange => time.TimeOfDay >= timeRange.StartTime && time.TimeOfDay <= timeRange.EndTime)) { return false; } @@ -109,7 +109,7 @@ public class WorkdayCalendarService : IWorkdayCalendarService, ITransientDepende if (calendar.EnableWorkTimeRestriction && calendar.WorkTimes != null) { - return calendar.WorkTimes.Any(x => x.StartTime >= time.TimeOfDay && x.EndTime <= time.TimeOfDay); + return calendar.WorkTimes.Any(timeRange => time.TimeOfDay >= timeRange.StartTime && time.TimeOfDay <= timeRange.EndTime); } return false; diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/WorkdayCalendarPublishInterceptor.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/WorkdayCalendarPublishInterceptor.cs index a24d997bd..f93beff5e 100644 --- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/WorkdayCalendarPublishInterceptor.cs +++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Calendar/LINGYUN/Abp/Notifications/Calendar/WorkdayCalendarPublishInterceptor.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System; using System.Threading.Tasks; -using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; using Volo.Abp.Timing; @@ -12,7 +11,7 @@ namespace LINGYUN.Abp.Notifications.Calendar; /// /// 工作日通知发布拦截器 /// -public class WorkdayCalendarPublishInterceptor : INotificationPublishInterceptor, ITransientDependency +public class WorkdayCalendarPublishInterceptor : INotificationPublishInterceptor { private readonly IClock _clock; private readonly ICurrentTenant _currentTenant; diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj index c2023f0f5..5537e2c55 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj +++ b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj @@ -211,6 +211,7 @@ + diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs index e8693c8c6..287123617 100644 --- a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs +++ b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs @@ -1,4 +1,5 @@ using LINGYUN.Abp.Exporter.MiniSoftware; +using LINGYUN.Abp.Notifications.Calendar; namespace LY.MicroService.Applications.Single; @@ -316,6 +317,8 @@ namespace LY.MicroService.Applications.Single; typeof(AbpNotificationsWeChatMiniProgramModule), // 通知模块 企业微信 typeof(AbpNotificationsWeChatWorkModule), + // 通知模块 工作日检查拦截器 + typeof(AbpNotificationsCalendarModule), // 多租户模块 版本 typeof(AbpMultiTenancyEditionsModule), diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj index 0660c8477..f947c5d5a 100644 --- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj +++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj @@ -74,6 +74,7 @@ + diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs index ca465dfb5..e98996175 100644 --- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs +++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs @@ -23,6 +23,7 @@ using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore; using LINGYUN.Abp.MessageService; using LINGYUN.Abp.MessageService.EntityFrameworkCore; using LINGYUN.Abp.Notifications; +using LINGYUN.Abp.Notifications.Calendar; using LINGYUN.Abp.Notifications.Common; using LINGYUN.Abp.Notifications.Emailing; using LINGYUN.Abp.Notifications.EntityFrameworkCore; @@ -107,6 +108,7 @@ namespace LY.MicroService.RealtimeMessage; typeof(AbpNotificationsWeChatWorkModule), typeof(AbpNotificationsExceptionHandlingModule), typeof(AbpNotificationsTemplatingModule), + typeof(AbpNotificationsCalendarModule), typeof(AbpWeChatWorkHandlersModule), typeof(AbpWeChatOfficialHandlersModule), typeof(AbpIdentityNotificationsModule),