diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN.Abp.ExceptionHandling.Emailing.csproj b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN.Abp.ExceptionHandling.Emailing.csproj new file mode 100644 index 000000000..83d348032 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN.Abp.ExceptionHandling.Emailing.csproj @@ -0,0 +1,8 @@ + + + + netstandard2.0 + + + + diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailExceptionHandlingOptions.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailExceptionHandlingOptions.cs new file mode 100644 index 000000000..6ea7c1e61 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailExceptionHandlingOptions.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace LINGYUN.Abp.ExceptionHandling +{ + public class AbpEmailExceptionHandlingOptions + { + /// + /// 默认异常收件人 + /// + public string DefaultReceiveEmail { get; set; } + /// + /// 异常类型指定收件人处理映射列表 + /// + public IDictionary Handlers { get; set; } + public AbpEmailExceptionHandlingOptions() + { + Handlers = new Dictionary(); + } + /// + /// 把需要接受异常通知的用户加进处理列表 + /// + /// 处理的异常类型 + /// 接收邮件的用户类别,群发用,符号分隔 + public void HandReceivedException(Exception ex, string receivedEmails) + { + if (Handlers.ContainsKey(ex)) + { + Handlers[ex] += receivedEmails; + } + else + { + Handlers.Add(ex, receivedEmails); + } + } + + public string GetReceivedEmailOrDefault(Exception ex) + { + if (Handlers.TryGetValue(ex, out string receivedUsers)) + { + return receivedUsers; + } + return DefaultReceiveEmail; + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailingExceptionHandlingModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailingExceptionHandlingModule.cs new file mode 100644 index 000000000..25b16fcc5 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailingExceptionHandlingModule.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.ExceptionHandling.Emailing +{ + [DependsOn(typeof(AbpExceptionHandlingModule))] + public class AbpEmailingExceptionHandlingModule : AbpModule + { + + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailingExceptionSubscriber.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailingExceptionSubscriber.cs new file mode 100644 index 000000000..7333ce221 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Emailing/LINGYUN/Abp/ExceptionHandling/AbpEmailingExceptionSubscriber.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; +using Volo.Abp.Emailing; + +namespace LINGYUN.Abp.ExceptionHandling +{ + public class AbpEmailingExceptionSubscriber : AbpExceptionSubscriberBase + { + protected IEmailSender EmailSender { get; } + protected AbpEmailExceptionHandlingOptions EmailOptions { get; } + public AbpEmailingExceptionSubscriber( + IEmailSender emailSender, + IServiceScopeFactory serviceScopeFactory, + IOptions options, + IOptions emailOptions) + : base(serviceScopeFactory, options) + { + EmailSender = emailSender; + EmailOptions = emailOptions.Value; + } + + protected override async Task SendErrorNotifierAsync(ExceptionSendNotifierContext context) + { + var receivedUsers = EmailOptions.GetReceivedEmailOrDefault(context.Exception); + + if (!receivedUsers.IsNullOrWhiteSpace()) + { + // TODO: 使用 Template 格式化推送 + await EmailSender.SendAsync(receivedUsers, + context.Exception.GetType().FullName, + context.Exception.Message); + } + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN.Abp.ExceptionHandling.Notifications.csproj b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN.Abp.ExceptionHandling.Notifications.csproj new file mode 100644 index 000000000..045d86cd5 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN.Abp.ExceptionHandling.Notifications.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + + + + + + + + + diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingNotificationDefinitionProvider.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingNotificationDefinitionProvider.cs new file mode 100644 index 000000000..577026ba2 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingNotificationDefinitionProvider.cs @@ -0,0 +1,12 @@ +using LINGYUN.Abp.Notifications; + +namespace LINGYUN.Abp.ExceptionHandling +{ + public class AbpExceptionHandlingNotificationDefinitionProvider : NotificationDefinitionProvider + { + public override void Define(INotificationDefinitionContext context) + { + context.Add(new NotificationDefinition(AbpExceptionHandlingNotificationNames.NotificationName)); + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingNotificationNames.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingNotificationNames.cs new file mode 100644 index 000000000..36d32946f --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingNotificationNames.cs @@ -0,0 +1,7 @@ +namespace LINGYUN.Abp.ExceptionHandling +{ + public class AbpExceptionHandlingNotificationNames + { + public const string NotificationName = "Abp.ExceptionHandling.Notifier"; + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpNotificationsExceptionHandlingModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpNotificationsExceptionHandlingModule.cs new file mode 100644 index 000000000..a52a75a9e --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpNotificationsExceptionHandlingModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.ExceptionHandling +{ + [DependsOn(typeof(AbpExceptionHandlingModule))] + public class AbpNotificationsExceptionHandlingModule : AbpModule + { + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpNotificationsExceptionSubscriber.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpNotificationsExceptionSubscriber.cs new file mode 100644 index 000000000..5a8d2e039 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling.Notifications/LINGYUN/Abp/ExceptionHandling/AbpNotificationsExceptionSubscriber.cs @@ -0,0 +1,38 @@ +using LINGYUN.Abp.Notifications; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Abp.ExceptionHandling +{ + public class AbpNotificationsExceptionSubscriber : AbpExceptionSubscriberBase + { + protected ICurrentTenant CurrentTenant { get; } + public AbpNotificationsExceptionSubscriber( + ICurrentTenant currentTenant, + IServiceScopeFactory serviceScopeFactory, + IOptions options) + : base(serviceScopeFactory, options) + { + CurrentTenant = currentTenant; + } + + protected override async Task SendErrorNotifierAsync(ExceptionSendNotifierContext context) + { + var notificationDispatcher = context.ServiceProvider.GetRequiredService(); + var notificationName = NotificationNameNormalizer + .NormalizerName(AbpExceptionHandlingNotificationNames.NotificationName); + var notificationData = new NotificationData(); + // 写入通知数据 + //TODO:集成TextTemplate完成格式化的推送 + notificationData.WriteStandardData( + context.Exception.GetType().FullName, context.Exception.Message, + DateTime.Now, "System"); + + await notificationDispatcher.DispatchAsync(notificationName, notificationData, + CurrentTenant.Id, NotificationSeverity.Error); + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN.Abp.ExceptionHandling.csproj b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN.Abp.ExceptionHandling.csproj new file mode 100644 index 000000000..248eb0396 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN.Abp.ExceptionHandling.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + + + + + + + + diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingModule.cs new file mode 100644 index 000000000..acd417376 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingModule.cs @@ -0,0 +1,8 @@ +using Volo.Abp.Modularity; + +namespace LINGYUN.Abp.ExceptionHandling +{ + public class AbpExceptionHandlingModule : AbpModule + { + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingOptions.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingOptions.cs new file mode 100644 index 000000000..106cb89d9 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionHandlingOptions.cs @@ -0,0 +1,24 @@ +using System; +using System.Linq; +using Volo.Abp.Collections; + +namespace LINGYUN.Abp.ExceptionHandling +{ + public class AbpExceptionHandlingOptions + { + public ITypeList Handlers { get; } + public AbpExceptionHandlingOptions() + { + Handlers = new TypeList(); + } + + public bool HasNotifierError(Exception ex) + { + if (typeof(IHasNotifierErrorMessage).IsAssignableFrom(ex.GetType())) + { + return true; + } + return Handlers.Any(x => x.IsAssignableFrom(ex.GetType())); + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionSubscriberBase.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionSubscriberBase.cs new file mode 100644 index 000000000..73d3e3a4b --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/AbpExceptionSubscriberBase.cs @@ -0,0 +1,68 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; +using Volo.Abp.ExceptionHandling; + +namespace LINGYUN.Abp.ExceptionHandling +{ + public abstract class AbpExceptionSubscriberBase : ExceptionSubscriber + { + protected IServiceScopeFactory ServiceScopeFactory { get; } + protected AbpExceptionHandlingOptions Options { get; } + + public IServiceProvider ServiceProvider { get; set; } + protected readonly object ServiceProviderLock = new object(); + + protected TService LazyGetRequiredService(ref TService reference) + => LazyGetRequiredService(typeof(TService), ref reference); + + protected TRef LazyGetRequiredService(Type serviceType, ref TRef reference) + { + if (reference == null) + { + lock (ServiceProviderLock) + { + if (reference == null) + { + reference = (TRef)ServiceProvider.GetRequiredService(serviceType); + } + } + } + + return reference; + } + + protected ILoggerFactory LoggerFactory => LazyGetRequiredService(ref _loggerFactory); + private ILoggerFactory _loggerFactory; + + protected ILogger Logger => _lazyLogger.Value; + private Lazy _lazyLogger => new Lazy(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); + + + protected AbpExceptionSubscriberBase( + IServiceScopeFactory serviceScopeFactory, + IOptions options) + { + Options = options.Value; + ServiceScopeFactory = serviceScopeFactory; + } + + public override async Task HandleAsync(ExceptionNotificationContext context) + { + if (context.Handled && + Options.HasNotifierError(context.Exception)) + { + using (var scope = ServiceScopeFactory.CreateScope()) + { + await SendErrorNotifierAsync( + new ExceptionSendNotifierContext(scope.ServiceProvider, context.Exception, context.LogLevel)); + } + } + } + + protected abstract Task SendErrorNotifierAsync(ExceptionSendNotifierContext context); + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/ExceptionSendNotifierContext.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/ExceptionSendNotifierContext.cs new file mode 100644 index 000000000..e681b3f83 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/ExceptionSendNotifierContext.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.Logging; +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.ExceptionHandling +{ + public class ExceptionSendNotifierContext + { + [NotNull] + public Exception Exception { get; } + + [NotNull] + public IServiceProvider ServiceProvider { get; } + + public LogLevel LogLevel { get; } + internal ExceptionSendNotifierContext( + [NotNull] IServiceProvider serviceProvider, + [NotNull] Exception exception, + LogLevel? logLevel = null) + { + ServiceProvider = Check.NotNull(serviceProvider, nameof(serviceProvider)); + Exception = Check.NotNull(exception, nameof(exception)); + LogLevel = logLevel ?? exception.GetLogLevel(); + } + } +} diff --git a/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/IHasNotifierErrorMessage.cs b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/IHasNotifierErrorMessage.cs new file mode 100644 index 000000000..19391e445 --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.ExceptionHandling/LINGYUN/Abp/ExceptionHandling/IHasNotifierErrorMessage.cs @@ -0,0 +1,9 @@ +namespace LINGYUN.Abp.ExceptionHandling +{ + /// + /// 需要发送异常通知的自定义异常需要实现此接口 + /// + public interface IHasNotifierErrorMessage + { + } +} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs deleted file mode 100644 index ba1909d8e..000000000 --- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Controllers/NotificationController.cs +++ /dev/null @@ -1,75 +0,0 @@ -using LINGYUN.Abp.Notifications; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Volo.Abp.AspNetCore.Mvc; - -namespace LINGYUN.Abp.MessageService.Controllers -{ - [Route("api/app/notifications")] - public class NotificationController : AbpController - { - private readonly INotificationDispatcher _notificationDispatcher; - public NotificationController( - INotificationDispatcher notificationDispatcher) - { - _notificationDispatcher = notificationDispatcher; - } - - [HttpGet] - [Route("Test")] - public async Task> Test() - { - await Task.CompletedTask; - - return new Dictionary() - { - {"thing2", "测试标题" }, - {"name3", "测试人员" }, - }; - } - - [HttpPost] - [Route("Send")] - public async Task SendNofitication([FromBody] SendNotification notification) - { - var notificationData = new NotificationData(); - notificationData.Properties["title"] = notification.Title; - notificationData.Properties["message"] = notification.Message; - notificationData.Properties["datetime"] = Clock.Now; - notificationData.Properties["severity"] = notification.Severity; - - notificationData.Properties.AddIfNotContains(notification.Data); - - var notificationName = NotificationNameNormalizer.NormalizerName("TestApplicationNotofication"); - - await _notificationDispatcher.DispatchAsync(notificationName, notificationData, - notificationSeverity: notification.Severity); - - // await _notificationDispatcher.DispatcheAsync(notificationInfo); - } - } - - public class SendNotification - { - public Guid UserId { get; set; } - public string Title { get; set; } - public string Message { get; set; } - public Dictionary Data { get; set; } = new Dictionary(); - public NotificationSeverity Severity { get; set; } = NotificationSeverity.Success; - - } - - public class TestApplicationNotificationData : NotificationData - { - public object Message - { - get { return this[nameof(Message)]; } - set - { - Properties[nameof(Message)] = value; - } - } - } -} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj index 5d9096ddd..e2f59b8a9 100644 --- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj @@ -43,6 +43,7 @@ + diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs index e0abc20ae..30ea5dc1e 100644 --- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs @@ -3,6 +3,7 @@ using Hangfire; using IdentityModel; using LINGYUN.Abp.BackgroundJobs.Hangfire; using LINGYUN.Abp.EventBus.CAP; +using LINGYUN.Abp.ExceptionHandling; using LINGYUN.Abp.Hangfire.Storage.MySql; using LINGYUN.Abp.IM.SignalR; using LINGYUN.Abp.MessageService.BackgroundJobs; @@ -51,6 +52,7 @@ namespace LINGYUN.Abp.MessageService typeof(AbpIMSignalRModule), typeof(AbpNotificationsSignalRModule), typeof(AbpNotificationsWeChatWeAppModule), + typeof(AbpNotificationsExceptionHandlingModule), typeof(AbpCAPEventBusModule), typeof(AbpBackgroundJobsHangfireModule), typeof(AbpHangfireMySqlStorageModule), @@ -84,6 +86,12 @@ namespace LINGYUN.Abp.MessageService options.UseMySQL(); }); + Configure(options => + { + // 加入需要处理的异常类型 + // options.Handlers.Add(); + }); + Configure(options => { // 滑动过期30天