committed by
GitHub
16 changed files with 269 additions and 8 deletions
@ -0,0 +1,9 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.BackgroundTasks; |
|||
|
|||
public interface IJobExceptionNotifier |
|||
{ |
|||
Task NotifyAsync([NotNull] JobExceptionNotificationContext context); |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.BackgroundTasks; |
|||
|
|||
public class JobExceptionNotificationContext |
|||
{ |
|||
public JobInfo JobInfo { get; } |
|||
public Exception Exception { get; } |
|||
public JobExceptionNotificationContext( |
|||
JobInfo jobInfo, |
|||
Exception exception) |
|||
{ |
|||
JobInfo = jobInfo; |
|||
Exception = exception; |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using JetBrains.Annotations; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.BackgroundTasks; |
|||
|
|||
[Dependency(TryRegister = true)] |
|||
public class NullJobExceptionNotifier : IJobExceptionNotifier, ISingletonDependency |
|||
{ |
|||
public Task NotifyAsync([NotNull] JobExceptionNotificationContext context) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Timing" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\common\LINGYUN.Abp.ExceptionHandling\LINGYUN.Abp.ExceptionHandling.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks.Jobs\LINGYUN.Abp.BackgroundTasks.Jobs.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks\LINGYUN.Abp.BackgroundTasks.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,10 @@ |
|||
using LINGYUN.Abp.ExceptionHandling; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling; |
|||
|
|||
[DependsOn(typeof(AbpExceptionHandlingModule))] |
|||
public class AbpBackgroundTasksExceptionHandlingModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
using JetBrains.Annotations; |
|||
using LINGYUN.Abp.BackgroundTasks.Jobs; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Globalization; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace LINGYUN.Abp.BackgroundTasks.ExceptionHandling; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
public class JobExceptionNotifier : IJobExceptionNotifier, ITransientDependency |
|||
{ |
|||
public const string Prefix = "exception."; |
|||
|
|||
protected IClock Clock { get; } |
|||
protected IJobStore JobStore { get; } |
|||
protected IJobScheduler JobScheduler { get; } |
|||
|
|||
public JobExceptionNotifier( |
|||
IClock clock, |
|||
IJobStore jobStore, |
|||
IJobScheduler jobScheduler) |
|||
{ |
|||
Clock = clock; |
|||
JobStore = jobStore; |
|||
JobScheduler = jobScheduler; |
|||
} |
|||
|
|||
public virtual async Task NotifyAsync([NotNull] JobExceptionNotificationContext context) |
|||
{ |
|||
var notifyKey = Prefix + SendEmailJob.PropertyTo; |
|||
if (context.JobInfo.Args.TryGetValue(notifyKey, out var exceptionTo)) |
|||
{ |
|||
var template = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyTemplate) ?? ""; |
|||
var subject = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertySubject) ?? "From job execute exception"; |
|||
var globalContext = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyContext) ?? "{}"; |
|||
var from = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyFrom) ?? ""; |
|||
var culture = context.JobInfo.Args.GetOrDefault(Prefix + SendEmailJob.PropertyCulture) ?? CultureInfo.CurrentCulture.Name; |
|||
|
|||
var jobId = Guid.NewGuid(); |
|||
var jobArgs = new Dictionary<string, object> |
|||
{ |
|||
{ SendEmailJob.PropertyTo, exceptionTo.ToString() }, |
|||
{ SendEmailJob.PropertySubject, subject }, |
|||
{ SendEmailJob.PropertyBody, context.Exception.GetBaseException().Message }, |
|||
{ SendEmailJob.PropertyTemplate, template }, |
|||
{ SendEmailJob.PropertyContext, globalContext }, |
|||
{ SendEmailJob.PropertyFrom, from }, |
|||
{ SendEmailJob.PropertyCulture, culture } |
|||
}; |
|||
var jobInfo = new JobInfo |
|||
{ |
|||
Id = jobId, |
|||
Name = jobId.ToString(), |
|||
Group = "ExceptionHandling", |
|||
Priority = JobPriority.Normal, |
|||
BeginTime = Clock.Now, |
|||
Args = jobArgs, |
|||
Description = subject.ToString(), |
|||
JobType = JobType.Once, |
|||
Interval = 5, |
|||
MaxCount = 1, |
|||
MaxTryCount = 1, |
|||
CreationTime = Clock.Now, |
|||
Status = JobStatus.None, |
|||
Type = DefaultJobNames.SendEmailJob, |
|||
}; |
|||
|
|||
await JobStore.StoreAsync(jobInfo); |
|||
|
|||
await JobScheduler.TriggerAsync(jobInfo); |
|||
} |
|||
} |
|||
} |
|||
@ -1,3 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait /> |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -1,26 +1,76 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Emailing; |
|||
using Volo.Abp.TextTemplating; |
|||
using Volo.Abp.Json; |
|||
using System.Collections.Generic; |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.BackgroundTasks.Jobs; |
|||
|
|||
public class SendEmailJob : IJobRunnable |
|||
{ |
|||
public const string PropertyFrom = "from"; |
|||
/// <summary>
|
|||
/// 接收者
|
|||
/// </summary>
|
|||
public const string PropertyTo = "to"; |
|||
/// <summary>
|
|||
/// 必须,邮件主体
|
|||
/// </summary>
|
|||
public const string PropertySubject = "subject"; |
|||
/// <summary>
|
|||
/// 消息内容, 文本消息时必须
|
|||
/// </summary>
|
|||
public const string PropertyBody = "body"; |
|||
public const string PropertyIsBodyHtml = "isBodyHtml"; |
|||
|
|||
/// <summary>
|
|||
/// 发送模板消息
|
|||
/// </summary>
|
|||
public const string PropertyTemplate = "template"; |
|||
/// <summary>
|
|||
/// 可选, 模板消息中的上下文参数
|
|||
/// </summary>
|
|||
public const string PropertyContext = "context"; |
|||
/// <summary>
|
|||
/// 可选, 模板消息中的区域性
|
|||
/// </summary>
|
|||
public const string PropertyCulture = "culture"; |
|||
|
|||
public virtual async Task ExecuteAsync(JobRunnableContext context) |
|||
{ |
|||
context.TryGetString(PropertyFrom, out var from); |
|||
var to = context.GetString(PropertyTo); |
|||
var subject = context.GetString(PropertySubject); |
|||
var body = context.GetString(PropertyBody); |
|||
context.TryGetJobData<bool>(PropertyIsBodyHtml, out var isBodyHtml); |
|||
|
|||
var emailSender = context.GetRequiredService<IEmailSender>(); |
|||
|
|||
await emailSender.QueueAsync(from, to, subject, body, isBodyHtml); |
|||
if (context.TryGetString(PropertyTemplate, out var template) && !template.IsNullOrWhiteSpace()) |
|||
{ |
|||
var globalContext = new Dictionary<string, object>(); |
|||
context.TryGetString(PropertyCulture, out var culture); |
|||
if (context.TryGetString(PropertyContext, out var globalCtx)) |
|||
{ |
|||
var jsonSerializer = context.GetRequiredService<IJsonSerializer>(); |
|||
try |
|||
{ |
|||
globalContext = jsonSerializer.Deserialize<Dictionary<string, object>>(globalCtx); |
|||
} |
|||
catch { } |
|||
} |
|||
|
|||
var templateRenderer = context.GetRequiredService<ITemplateRenderer>(); |
|||
|
|||
var content = await templateRenderer.RenderAsync( |
|||
templateName: template, |
|||
cultureName: culture, |
|||
globalContext: globalContext); |
|||
|
|||
await emailSender.QueueAsync(from, to, subject, content, true); |
|||
return; |
|||
} |
|||
|
|||
var body = context.GetString(PropertyBody); |
|||
|
|||
await emailSender.QueueAsync(from, to, subject, body, false); |
|||
} |
|||
} |
|||
|
|||
Loading…
Reference in new issue