18 changed files with 337 additions and 75 deletions
@ -0,0 +1,8 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,46 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.ExceptionHandling |
|||
{ |
|||
public class AbpEmailExceptionHandlingOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 默认异常收件人
|
|||
/// </summary>
|
|||
public string DefaultReceiveEmail { get; set; } |
|||
/// <summary>
|
|||
/// 异常类型指定收件人处理映射列表
|
|||
/// </summary>
|
|||
public IDictionary<Exception, string> Handlers { get; set; } |
|||
public AbpEmailExceptionHandlingOptions() |
|||
{ |
|||
Handlers = new Dictionary<Exception, string>(); |
|||
} |
|||
/// <summary>
|
|||
/// 把需要接受异常通知的用户加进处理列表
|
|||
/// </summary>
|
|||
/// <param name="ex">处理的异常类型</param>
|
|||
/// <param name="receivedEmails">接收邮件的用户类别,群发用,符号分隔</param>
|
|||
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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.ExceptionHandling.Emailing |
|||
{ |
|||
[DependsOn(typeof(AbpExceptionHandlingModule))] |
|||
public class AbpEmailingExceptionHandlingModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -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<AbpExceptionHandlingOptions> options, |
|||
IOptions<AbpEmailExceptionHandlingOptions> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.ExceptionHandling\LINGYUN.Abp.ExceptionHandling.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -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)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace LINGYUN.Abp.ExceptionHandling |
|||
{ |
|||
public class AbpExceptionHandlingNotificationNames |
|||
{ |
|||
public const string NotificationName = "Abp.ExceptionHandling.Notifier"; |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.ExceptionHandling |
|||
{ |
|||
[DependsOn(typeof(AbpExceptionHandlingModule))] |
|||
public class AbpNotificationsExceptionHandlingModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -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<AbpExceptionHandlingOptions> options) |
|||
: base(serviceScopeFactory, options) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
} |
|||
|
|||
protected override async Task SendErrorNotifierAsync(ExceptionSendNotifierContext context) |
|||
{ |
|||
var notificationDispatcher = context.ServiceProvider.GetRequiredService<INotificationDispatcher>(); |
|||
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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Core" Version="2.9.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.ExceptionHandling |
|||
{ |
|||
public class AbpExceptionHandlingModule : AbpModule |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using Volo.Abp.Collections; |
|||
|
|||
namespace LINGYUN.Abp.ExceptionHandling |
|||
{ |
|||
public class AbpExceptionHandlingOptions |
|||
{ |
|||
public ITypeList<Exception> Handlers { get; } |
|||
public AbpExceptionHandlingOptions() |
|||
{ |
|||
Handlers = new TypeList<Exception>(); |
|||
} |
|||
|
|||
public bool HasNotifierError(Exception ex) |
|||
{ |
|||
if (typeof(IHasNotifierErrorMessage).IsAssignableFrom(ex.GetType())) |
|||
{ |
|||
return true; |
|||
} |
|||
return Handlers.Any(x => x.IsAssignableFrom(ex.GetType())); |
|||
} |
|||
} |
|||
} |
|||
@ -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<TService>(ref TService reference) |
|||
=> LazyGetRequiredService(typeof(TService), ref reference); |
|||
|
|||
protected TRef LazyGetRequiredService<TRef>(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<ILogger> _lazyLogger => new Lazy<ILogger>(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); |
|||
|
|||
|
|||
protected AbpExceptionSubscriberBase( |
|||
IServiceScopeFactory serviceScopeFactory, |
|||
IOptions<AbpExceptionHandlingOptions> 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); |
|||
} |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace LINGYUN.Abp.ExceptionHandling |
|||
{ |
|||
/// <summary>
|
|||
/// 需要发送异常通知的自定义异常需要实现此接口
|
|||
/// </summary>
|
|||
public interface IHasNotifierErrorMessage |
|||
{ |
|||
} |
|||
} |
|||
@ -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<Dictionary<string, object>> Test() |
|||
{ |
|||
await Task.CompletedTask; |
|||
|
|||
return new Dictionary<string, object>() |
|||
{ |
|||
{"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<string, object> Data { get; set; } = new Dictionary<string, object>(); |
|||
public NotificationSeverity Severity { get; set; } = NotificationSeverity.Success; |
|||
|
|||
} |
|||
|
|||
public class TestApplicationNotificationData : NotificationData |
|||
{ |
|||
public object Message |
|||
{ |
|||
get { return this[nameof(Message)]; } |
|||
set |
|||
{ |
|||
Properties[nameof(Message)] = value; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue