committed by
GitHub
19 changed files with 368 additions and 0 deletions
@ -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,26 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net10.0</TargetFramework> |
||||
|
<AssemblyName>LINGYUN.Platform.Jobs</AssemblyName> |
||||
|
<PackageId>LINGYUN.Platform.Jobs</PackageId> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Platform\Jobs\Localization\Resources\*.json" /> |
||||
|
<EmbeddedResource Include="LINGYUN\Platform\Jobs\Localization\Resources\*.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\task-management\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Platform.Domain\LINGYUN.Platform.Domain.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,12 @@ |
|||||
|
using LINGYUN.Platform.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Jobs; |
||||
|
|
||||
|
internal static class LocalizableStatic |
||||
|
{ |
||||
|
public static ILocalizableString Create(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<PlatformResource>(name); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"SmsMessageRetrySendJob": "Re-sending of SMS message job", |
||||
|
"EmailMessageRetrySendJob": "Re-sending of Email message job", |
||||
|
"DisplayName:RecentSnedFailedHours": "The latest failure in sending occurred at", |
||||
|
"Description:RecentSnedFailedHours": "Recent failure sending time, unit: hours, default: 2 hours.", |
||||
|
"DisplayName:PropertyMaxOfFailedAttempts": "Maximum number of failure attempts", |
||||
|
"Description:PropertyMaxOfFailedAttempts": "Maximum number of failures, default: 3.", |
||||
|
"DisplayName:PropertyPollingBatchCount": "Polling batch count", |
||||
|
"Description:PropertyPollingBatchCount": "Polling batch count, default: 100." |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"SmsMessageRetrySendJob": "短信重新发送作业", |
||||
|
"EmailMessageRetrySendJob": "邮件重新发送作业", |
||||
|
"DisplayName:RecentSnedFailedHours": "最近发送失败时间", |
||||
|
"Description:RecentSnedFailedHours": "最近发送失败时间, 单位: 小时, 默认: 2.", |
||||
|
"DisplayName:PropertyMaxOfFailedAttempts": "失败次数上限", |
||||
|
"Description:PropertyMaxOfFailedAttempts": "失败次数上限, 默认: 3.", |
||||
|
"DisplayName:PropertyPollingBatchCount": "轮询批次数量", |
||||
|
"Description:PropertyPollingBatchCount": "轮询批次数量, 默认: 100." |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks; |
||||
|
using LINGYUN.Platform.Messages; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.DistributedLocking; |
||||
|
using Volo.Abp.Specifications; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Jobs.Messages; |
||||
|
/// <summary>
|
||||
|
/// 邮件重新发送作业
|
||||
|
/// </summary>
|
||||
|
[DisableAuditing] |
||||
|
[DisableJobAction] |
||||
|
public class EmailMessageRetrySendJob : IJobRunnable |
||||
|
{ |
||||
|
public const string Name = "EmailMessageRetrySendJob"; |
||||
|
|
||||
|
#region Definition Paramters
|
||||
|
|
||||
|
public readonly static IReadOnlyList<JobDefinitionParamter> Paramters = |
||||
|
new List<JobDefinitionParamter> |
||||
|
{ |
||||
|
new JobDefinitionParamter( |
||||
|
PropertyRecentSnedFailedHours, |
||||
|
LocalizableStatic.Create("DisplayName:RecentSnedFailedHours"), |
||||
|
LocalizableStatic.Create("Description:RecentSnedFailedHours")), |
||||
|
new JobDefinitionParamter( |
||||
|
PropertyMaxOfFailedAttempts, |
||||
|
LocalizableStatic.Create("DisplayName:PropertyMaxOfFailedAttempts"), |
||||
|
LocalizableStatic.Create("Description:PropertyMaxOfFailedAttempts")), |
||||
|
new JobDefinitionParamter( |
||||
|
PropertyPollingBatchCount, |
||||
|
LocalizableStatic.Create("DisplayName:PropertyPollingBatchCount"), |
||||
|
LocalizableStatic.Create("Description:PropertyPollingBatchCount")), |
||||
|
}; |
||||
|
|
||||
|
#endregion
|
||||
|
/// <summary>
|
||||
|
/// 最近发送失败时间, 单位: 小时, 默认: 2
|
||||
|
/// </summary>
|
||||
|
public const string PropertyRecentSnedFailedHours = "RecentSnedFailedHours"; |
||||
|
/// <summary>
|
||||
|
/// 失败次数上限, 默认: 3
|
||||
|
/// </summary>
|
||||
|
public const string PropertyMaxOfFailedAttempts = "MaxOfFailedAttempts"; |
||||
|
/// <summary>
|
||||
|
/// 轮询批次数量, 默认: 100
|
||||
|
/// </summary>
|
||||
|
public const string PropertyPollingBatchCount = "PollingBatchCount"; |
||||
|
|
||||
|
public async virtual Task ExecuteAsync(JobRunnableContext context) |
||||
|
{ |
||||
|
var logger = context.GetRequiredService<ILogger<EmailMessageRetrySendJob>>(); |
||||
|
|
||||
|
var distributedLock = context.GetRequiredService<IAbpDistributedLock>(); |
||||
|
var distributedLockKey = nameof(EmailMessageRetrySendJob); |
||||
|
await using var jobLockHandle = await distributedLock.TryAcquireAsync(distributedLockKey); |
||||
|
if (jobLockHandle == null) |
||||
|
{ |
||||
|
logger.LogWarning("Handle is null because of the locking for : {distributedLockKey}", distributedLockKey); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
logger.LogDebug("Lock is acquired for {distributedLockKey}.", distributedLockKey); |
||||
|
|
||||
|
var clock = context.GetRequiredService<IClock>(); |
||||
|
var emailMessageManager = context.GetRequiredService<IEmailMessageManager>(); |
||||
|
var emailMessageRepo = context.GetRequiredService<IEmailMessageRepository>(); |
||||
|
|
||||
|
var recentSnedFailedHours = context.GetOrDefaultJobData(PropertyRecentSnedFailedHours, 2); |
||||
|
var maxOfFailedAttempts = context.GetOrDefaultJobData(PropertyMaxOfFailedAttempts, 3); |
||||
|
var pollingBatchCount = context.GetOrDefaultJobData(PropertyPollingBatchCount, 100); |
||||
|
var sendTime = clock.Now.AddHours(-recentSnedFailedHours); |
||||
|
|
||||
|
var recentSnedFailedMessages = await emailMessageRepo.GetListAsync( |
||||
|
new ExpressionSpecification<EmailMessage>(x => x.Status == MessageStatus.Failed && |
||||
|
x.SendCount < maxOfFailedAttempts && x.SendTime >= sendTime), |
||||
|
maxResultCount: pollingBatchCount); |
||||
|
|
||||
|
if (recentSnedFailedMessages.Count > 0) |
||||
|
{ |
||||
|
logger.LogDebug("In the last {Hour} hours, a total of {Count} Email messages need to be resent.", recentSnedFailedHours, recentSnedFailedMessages.Count); |
||||
|
|
||||
|
foreach (var message in recentSnedFailedMessages) |
||||
|
{ |
||||
|
await emailMessageManager.SendAsync(message); |
||||
|
} |
||||
|
|
||||
|
await emailMessageRepo.UpdateManyAsync(recentSnedFailedMessages); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
logger.LogDebug($"There are no Email messages that need to be resent."); |
||||
|
} |
||||
|
|
||||
|
logger.LogDebug($"The batch retry operation of the Email messages has been successfully completed."); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,101 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks; |
||||
|
using LINGYUN.Platform.Messages; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.DistributedLocking; |
||||
|
using Volo.Abp.Specifications; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Jobs.Messages; |
||||
|
/// <summary>
|
||||
|
/// 短信重新发送作业
|
||||
|
/// </summary>
|
||||
|
[DisableAuditing] |
||||
|
[DisableJobAction] |
||||
|
public class SmsMessageRetrySendJob : IJobRunnable |
||||
|
{ |
||||
|
public const string Name = "SmsMessageRetrySendJob"; |
||||
|
|
||||
|
#region Definition Paramters
|
||||
|
|
||||
|
public readonly static IReadOnlyList<JobDefinitionParamter> Paramters = |
||||
|
new List<JobDefinitionParamter> |
||||
|
{ |
||||
|
new JobDefinitionParamter( |
||||
|
PropertyRecentSnedFailedHours, |
||||
|
LocalizableStatic.Create("DisplayName:RecentSnedFailedHours"), |
||||
|
LocalizableStatic.Create("Description:RecentSnedFailedHours")), |
||||
|
new JobDefinitionParamter( |
||||
|
PropertyMaxOfFailedAttempts, |
||||
|
LocalizableStatic.Create("DisplayName:PropertyMaxOfFailedAttempts"), |
||||
|
LocalizableStatic.Create("Description:PropertyMaxOfFailedAttempts")), |
||||
|
new JobDefinitionParamter( |
||||
|
PropertyPollingBatchCount, |
||||
|
LocalizableStatic.Create("DisplayName:PropertyPollingBatchCount"), |
||||
|
LocalizableStatic.Create("Description:PropertyPollingBatchCount")), |
||||
|
}; |
||||
|
|
||||
|
#endregion
|
||||
|
/// <summary>
|
||||
|
/// 最近发送失败时间, 单位: 小时, 默认: 2
|
||||
|
/// </summary>
|
||||
|
public const string PropertyRecentSnedFailedHours = "RecentSnedFailedHours"; |
||||
|
/// <summary>
|
||||
|
/// 失败次数上限, 默认: 3
|
||||
|
/// </summary>
|
||||
|
public const string PropertyMaxOfFailedAttempts = "MaxOfFailedAttempts"; |
||||
|
/// <summary>
|
||||
|
/// 轮询批次数量, 默认: 100
|
||||
|
/// </summary>
|
||||
|
public const string PropertyPollingBatchCount = "PollingBatchCount"; |
||||
|
|
||||
|
public async virtual Task ExecuteAsync(JobRunnableContext context) |
||||
|
{ |
||||
|
var logger = context.GetRequiredService<ILogger<SmsMessageRetrySendJob>>(); |
||||
|
|
||||
|
var distributedLock = context.GetRequiredService<IAbpDistributedLock>(); |
||||
|
var distributedLockKey = nameof(SmsMessageRetrySendJob); |
||||
|
await using var jobLockHandle = await distributedLock.TryAcquireAsync(distributedLockKey); |
||||
|
if (jobLockHandle == null) |
||||
|
{ |
||||
|
logger.LogWarning("Handle is null because of the locking for : {distributedLockKey}", distributedLockKey); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
logger.LogDebug("Lock is acquired for {distributedLockKey}.", distributedLockKey); |
||||
|
|
||||
|
var clock = context.GetRequiredService<IClock>(); |
||||
|
var smsMessageManager = context.GetRequiredService<ISmsMessageManager>(); |
||||
|
var smsMessageRepo = context.GetRequiredService<ISmsMessageRepository>(); |
||||
|
|
||||
|
var recentSnedFailedHours = context.GetOrDefaultJobData(PropertyRecentSnedFailedHours, 2); |
||||
|
var maxOfFailedAttempts = context.GetOrDefaultJobData(PropertyMaxOfFailedAttempts, 3); |
||||
|
var pollingBatchCount = context.GetOrDefaultJobData(PropertyPollingBatchCount, 100); |
||||
|
var sendTime = clock.Now.AddHours(-recentSnedFailedHours); |
||||
|
|
||||
|
var recentSnedFailedMessages = await smsMessageRepo.GetListAsync( |
||||
|
new ExpressionSpecification<SmsMessage>(x => x.Status == MessageStatus.Failed && |
||||
|
x.SendCount < maxOfFailedAttempts && x.SendTime >= sendTime), |
||||
|
maxResultCount: pollingBatchCount); |
||||
|
|
||||
|
if (recentSnedFailedMessages.Count > 0) |
||||
|
{ |
||||
|
logger.LogDebug("In the last {Hour} hours, a total of {Count} SMS messages need to be resent.", recentSnedFailedHours, recentSnedFailedMessages.Count); |
||||
|
|
||||
|
foreach (var message in recentSnedFailedMessages) |
||||
|
{ |
||||
|
await smsMessageManager.SendAsync(message); |
||||
|
} |
||||
|
|
||||
|
await smsMessageRepo.UpdateManyAsync(recentSnedFailedMessages); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
logger.LogDebug($"There are no SMS messages that need to be resent."); |
||||
|
} |
||||
|
|
||||
|
logger.LogDebug($"The batch retry operation of the SMS messages has been successfully completed."); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using LINGYUN.Abp.BackgroundTasks; |
||||
|
using LINGYUN.Platform.Jobs.Messages; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Jobs; |
||||
|
public class IdentityJobDefinitionProvider : JobDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IJobDefinitionContext context) |
||||
|
{ |
||||
|
context.Add( |
||||
|
new JobDefinition( |
||||
|
EmailMessageRetrySendJob.Name, |
||||
|
typeof(EmailMessageRetrySendJob), |
||||
|
LocalizableStatic.Create("EmailMessageRetrySendJob"), |
||||
|
EmailMessageRetrySendJob.Paramters), |
||||
|
new JobDefinition( |
||||
|
SmsMessageRetrySendJob.Name, |
||||
|
typeof(SmsMessageRetrySendJob), |
||||
|
LocalizableStatic.Create("SmsMessageRetrySendJob"), |
||||
|
SmsMessageRetrySendJob.Paramters) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using LINGYUN.Platform.Localization; |
||||
|
using Volo.Abp.BackgroundJobs; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Platform.Jobs; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpBackgroundJobsAbstractionsModule), |
||||
|
typeof(PlatformDomainModule))] |
||||
|
public class PlatformJobsModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<PlatformJobsModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<PlatformResource>() |
||||
|
.AddVirtualJson("/LINGYUN/Platform/Jobs/Localization/Resources"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue