Browse Source

Added background services application.

pull/29/head
Halil İbrahim Kalkan 6 years ago
parent
commit
f00dfbfac9
  1. 7
      EventHub.sln
  2. 5
      src/EventHub.Application/EventHub.Application.csproj
  3. 23
      src/EventHub.Application/EventHubApplicationModule.cs
  4. 34
      src/EventHub.BackgroundServices/EventHub.BackgroundServices.csproj
  5. 36
      src/EventHub.BackgroundServices/EventHubBackgroundServicesHostedService.cs
  6. 32
      src/EventHub.BackgroundServices/EventHubBackgroundServicesModule.cs
  7. 0
      src/EventHub.BackgroundServices/Events/EventReminderNotifier.cs
  8. 0
      src/EventHub.BackgroundServices/Events/EventReminderWorker.cs
  9. 161
      src/EventHub.BackgroundServices/Events/EventTimingChangeNotifier.cs
  10. 100
      src/EventHub.BackgroundServices/Events/EventTimingChangeWorker.cs
  11. 23
      src/EventHub.BackgroundServices/Events/NewEventDetectorWorker.cs
  12. 34
      src/EventHub.BackgroundServices/Events/NewEventNotifier.cs
  13. 53
      src/EventHub.BackgroundServices/Program.cs
  14. 3
      src/EventHub.BackgroundServices/appsettings.json
  15. 0
      src/EventHub.Domain/Emailing/EmailTemplateDefinitionProvider.cs
  16. 0
      src/EventHub.Domain/Emailing/EmailTemplates.cs
  17. 0
      src/EventHub.Domain/Emailing/Templates/EventRegistration.tpl
  18. 0
      src/EventHub.Domain/Emailing/Templates/EventReminder.tpl
  19. 312
      src/EventHub.Domain/Emailing/Templates/EventTimingChanged.tpl
  20. 0
      src/EventHub.Domain/Emailing/Templates/NewEventCreated.tpl
  21. 10
      src/EventHub.Domain/EventHub.Domain.csproj
  22. 6
      src/EventHub.Domain/EventHubDomainModule.cs

7
EventHub.sln

@ -41,6 +41,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventHub.Web.Admin", "src\E
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Web", "src\EventHub.Web\EventHub.Web.csproj", "{19F3FAD2-740F-4933-A394-4B030E2ABE6A}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.Web", "src\EventHub.Web\EventHub.Web.csproj", "{19F3FAD2-740F-4933-A394-4B030E2ABE6A}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventHub.BackgroundServices", "src\EventHub.BackgroundServices\EventHub.BackgroundServices.csproj", "{7A16923F-06FA-40B4-8A54-69E7828B1D69}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -115,6 +117,10 @@ Global
{19F3FAD2-740F-4933-A394-4B030E2ABE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU {19F3FAD2-740F-4933-A394-4B030E2ABE6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{19F3FAD2-740F-4933-A394-4B030E2ABE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU {19F3FAD2-740F-4933-A394-4B030E2ABE6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{19F3FAD2-740F-4933-A394-4B030E2ABE6A}.Release|Any CPU.Build.0 = Release|Any CPU {19F3FAD2-740F-4933-A394-4B030E2ABE6A}.Release|Any CPU.Build.0 = Release|Any CPU
{7A16923F-06FA-40B4-8A54-69E7828B1D69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A16923F-06FA-40B4-8A54-69E7828B1D69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A16923F-06FA-40B4-8A54-69E7828B1D69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A16923F-06FA-40B4-8A54-69E7828B1D69}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -137,6 +143,7 @@ Global
{AA94D832-1CCC-4715-95A9-A483F23A1A5D} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} {AA94D832-1CCC-4715-95A9-A483F23A1A5D} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{27B2DDC7-8B75-4322-A312-25419C15D9D8} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} {27B2DDC7-8B75-4322-A312-25419C15D9D8} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{19F3FAD2-740F-4933-A394-4B030E2ABE6A} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0} {19F3FAD2-740F-4933-A394-4B030E2ABE6A} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
{7A16923F-06FA-40B4-8A54-69E7828B1D69} = {CA9AC87F-097E-4F15-8393-4BC07735A5B0}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6AAFA1C6-603E-13FA-45E5-7910AA9F661D} SolutionGuid = {6AAFA1C6-603E-13FA-45E5-7910AA9F661D}

5
src/EventHub.Application/EventHub.Application.csproj

@ -18,9 +18,4 @@
<PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="4.2.1" /> <PackageReference Include="Volo.Abp.PermissionManagement.Application" Version="4.2.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Remove="Emailing\Templates\*.tpl" />
<EmbeddedResource Include="Emailing\Templates\*.tpl" />
</ItemGroup>
</Project> </Project>

23
src/EventHub.Application/EventHubApplicationModule.cs

@ -1,12 +1,8 @@
using EventHub.Events; using Volo.Abp.Account;
using Volo.Abp;
using Volo.Abp.Account;
using Volo.Abp.AutoMapper; using Volo.Abp.AutoMapper;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.Identity; using Volo.Abp.Identity;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement; using Volo.Abp.PermissionManagement;
using Volo.Abp.VirtualFileSystem;
namespace EventHub namespace EventHub
{ {
@ -15,29 +11,16 @@ namespace EventHub
typeof(AbpAccountApplicationModule), typeof(AbpAccountApplicationModule),
typeof(EventHubApplicationContractsModule), typeof(EventHubApplicationContractsModule),
typeof(AbpIdentityApplicationModule), typeof(AbpIdentityApplicationModule),
typeof(AbpPermissionManagementApplicationModule), typeof(AbpPermissionManagementApplicationModule)
typeof(AbpBackgroundWorkersModule) )]
)]
public class EventHubApplicationModule : AbpModule public class EventHubApplicationModule : AbpModule
{ {
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
context.AddBackgroundWorker<EventReminderWorker>();
context.AddBackgroundWorker<NewEventDetectorWorker>();
context.AddBackgroundWorker<EventTimingChangeWorker>();
}
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
Configure<AbpAutoMapperOptions>(options => Configure<AbpAutoMapperOptions>(options =>
{ {
options.AddMaps<EventHubApplicationModule>(); options.AddMaps<EventHubApplicationModule>();
}); });
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EventHubApplicationModule>("EventHub");
});
} }
} }
} }

34
src/EventHub.BackgroundServices/EventHub.BackgroundServices.csproj

@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<RootNamespace>EventHub</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Autofac" Version="4.2.1" />
<PackageReference Include="Volo.Abp.BackgroundWorkers" Version="4.2.1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.*" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
</ItemGroup>
<ItemGroup>
<Content Include="appsettings.json">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EventHub.Domain\EventHub.Domain.csproj" />
</ItemGroup>
</Project>

36
src/EventHub.BackgroundServices/EventHubBackgroundServicesHostedService.cs

@ -0,0 +1,36 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Volo.Abp;
namespace EventHub
{
public class EventHubBackgroundServicesHostedService : IHostedService
{
private readonly IAbpApplicationWithExternalServiceProvider _application;
private readonly IServiceProvider _serviceProvider;
public EventHubBackgroundServicesHostedService(
IAbpApplicationWithExternalServiceProvider application,
IServiceProvider serviceProvider)
{
_application = application;
_serviceProvider = serviceProvider;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_application.Initialize(_serviceProvider);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_application.Shutdown();
return Task.CompletedTask;
}
}
}

32
src/EventHub.BackgroundServices/EventHubBackgroundServicesModule.cs

@ -0,0 +1,32 @@
using EventHub.Events;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Volo.Abp;
using Volo.Abp.Autofac;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.Modularity;
namespace EventHub
{
[DependsOn(
typeof(AbpAutofacModule)
)]
public class EventHubBackgroundServicesModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
var hostEnvironment = context.Services.GetSingletonInstance<IHostEnvironment>();
context.Services.AddHostedService<EventHubBackgroundServicesHostedService>();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
context.AddBackgroundWorker<EventReminderWorker>();
context.AddBackgroundWorker<NewEventDetectorWorker>();
context.AddBackgroundWorker<EventTimingChangeWorker>();
}
}
}

0
src/EventHub.Application/Events/EventReminderNotifier.cs → src/EventHub.BackgroundServices/Events/EventReminderNotifier.cs

0
src/EventHub.Application/Events/EventReminderWorker.cs → src/EventHub.BackgroundServices/Events/EventReminderWorker.cs

161
src/EventHub.Application/Events/EventTimingChangeNotifier.cs → src/EventHub.BackgroundServices/Events/EventTimingChangeNotifier.cs

@ -1,82 +1,81 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Linq.Dynamic.Core; using System.Threading.Tasks;
using System.Threading.Tasks; using EventHub.Emailing;
using EventHub.Emailing; using EventHub.Events.Registrations;
using EventHub.Events.Registrations; using EventHub.Users;
using EventHub.Users; using Volo.Abp.DependencyInjection;
using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Emailing;
using Volo.Abp.Emailing; using Volo.Abp.Linq;
using Volo.Abp.Linq; using Volo.Abp.TextTemplating;
using Volo.Abp.TextTemplating;
namespace EventHub.Events
namespace EventHub.Events {
{ public class EventTimingChangeNotifier : ITransientDependency
public class EventTimingChangeNotifier : ITransientDependency {
{ private readonly IEmailSender _emailSender;
private readonly IEmailSender _emailSender; private readonly ITemplateRenderer _templateRenderer;
private readonly ITemplateRenderer _templateRenderer; private readonly IRepository<EventRegistration, Guid> _eventRegistrationRepository;
private readonly IRepository<EventRegistration, Guid> _eventRegistrationRepository; private readonly IAsyncQueryableExecuter _asyncExecuter;
private readonly IAsyncQueryableExecuter _asyncExecuter; private readonly IRepository<AppUser, Guid> _userRepository;
private readonly IRepository<AppUser, Guid> _userRepository;
public EventTimingChangeNotifier(
public EventTimingChangeNotifier( IEmailSender emailSender,
IEmailSender emailSender, ITemplateRenderer templateRenderer,
ITemplateRenderer templateRenderer, IRepository<EventRegistration, Guid> eventRegistrationRepository,
IRepository<EventRegistration, Guid> eventRegistrationRepository, IAsyncQueryableExecuter asyncExecuter,
IAsyncQueryableExecuter asyncExecuter, IRepository<AppUser, Guid> userRepository
IRepository<AppUser, Guid> userRepository )
) {
{ _emailSender = emailSender;
_emailSender = emailSender; _templateRenderer = templateRenderer;
_templateRenderer = templateRenderer; _eventRegistrationRepository = eventRegistrationRepository;
_eventRegistrationRepository = eventRegistrationRepository; _asyncExecuter = asyncExecuter;
_asyncExecuter = asyncExecuter; _userRepository = userRepository;
_userRepository = userRepository; }
}
public async Task NotifyAsync(Event @event)
public async Task NotifyAsync(Event @event) {
{ if (@event is null)
if (@event is null) {
{ return;
return; }
}
var eventRegistrationQueryable = await _eventRegistrationRepository.GetQueryableAsync();
var eventRegistrationQueryable = await _eventRegistrationRepository.GetQueryableAsync(); var query = eventRegistrationQueryable
var query = eventRegistrationQueryable .Where(x => x.EventId == @event.Id && !x.IsTimingChangeEmailSent);
.Where(x => x.EventId == @event.Id && !x.IsTimingChangeEmailSent);
var eventRegistrations = await _asyncExecuter.ToListAsync(query);
var eventRegistrations = await _asyncExecuter.ToListAsync(query);
foreach (var eventRegistration in eventRegistrations)
foreach (var eventRegistration in eventRegistrations) {
{ var user = await _userRepository.FindAsync(eventRegistration.UserId);
var user = await _userRepository.FindAsync(eventRegistration.UserId);
if (user is null)
if (user is null) {
{ continue;
continue; }
}
var model = new
var model = new {
{ UserName = user.GetFullNameOrUsername(),
UserName = user.GetFullNameOrUsername(), Title = @event.Title,
Title = @event.Title, StartTime = @event.StartTime,
StartTime = @event.StartTime, EndTime = @event.EndTime,
EndTime = @event.EndTime, Url = @event.Url
Url = @event.Url };
};
await _emailSender.QueueAsync(
await _emailSender.QueueAsync( user.Email,
user.Email, "Event time has been changed!",
"Event time has been changed!", await _templateRenderer.RenderAsync(EmailTemplates.EventTimingChanged, model)
await _templateRenderer.RenderAsync(EmailTemplates.EventTimingChanged, model) );
);
eventRegistration.IsTimingChangeEmailSent = true;
eventRegistration.IsTimingChangeEmailSent = true; }
}
await _eventRegistrationRepository.UpdateManyAsync(eventRegistrations, autoSave: true);
await _eventRegistrationRepository.UpdateManyAsync(eventRegistrations, autoSave: true); }
} }
}
} }

100
src/EventHub.Application/Events/EventTimingChangeWorker.cs → src/EventHub.BackgroundServices/Events/EventTimingChangeWorker.cs

@ -1,51 +1,51 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using EventHub.Events.Registrations; using EventHub.Events.Registrations;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Volo.Abp.BackgroundWorkers; using Volo.Abp.BackgroundWorkers;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq; using Volo.Abp.Linq;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.Uow; using Volo.Abp.Uow;
namespace EventHub.Events namespace EventHub.Events
{ {
public class EventTimingChangeWorker : AsyncPeriodicBackgroundWorkerBase public class EventTimingChangeWorker : AsyncPeriodicBackgroundWorkerBase
{ {
public EventTimingChangeWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory) public EventTimingChangeWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory)
{ {
Timer.Period = 60000; //1 minute Timer.Period = 60000; //1 minute
} }
[UnitOfWork(isTransactional: false)] [UnitOfWork(isTransactional: false)]
protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{ {
var eventTimingChangeNotifier = workerContext.ServiceProvider.GetRequiredService<EventTimingChangeNotifier>(); var eventTimingChangeNotifier = workerContext.ServiceProvider.GetRequiredService<EventTimingChangeNotifier>();
var eventRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<Event, Guid>>(); var eventRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<Event, Guid>>();
var eventRegistrationRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<EventRegistration, Guid>>(); var eventRegistrationRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<EventRegistration, Guid>>();
var asyncExecuter = workerContext.ServiceProvider.GetRequiredService<IAsyncQueryableExecuter>(); var asyncExecuter = workerContext.ServiceProvider.GetRequiredService<IAsyncQueryableExecuter>();
var eventRegistrationQueryable = await eventRegistrationRepository.GetQueryableAsync(); var eventRegistrationQueryable = await eventRegistrationRepository.GetQueryableAsync();
var query = eventRegistrationQueryable var query = eventRegistrationQueryable
.Where(x => !x.IsTimingChangeEmailSent) .Where(x => !x.IsTimingChangeEmailSent)
.Select(x => x.EventId) .Select(x => x.EventId)
.Distinct(); .Distinct();
var eventRegistrations = await asyncExecuter.ToListAsync(query); var eventRegistrations = await asyncExecuter.ToListAsync(query);
foreach (var eventRegistration in eventRegistrations) foreach (var eventRegistration in eventRegistrations)
{ {
try try
{ {
await eventTimingChangeNotifier.NotifyAsync(await eventRepository.FindAsync(eventRegistration)); await eventTimingChangeNotifier.NotifyAsync(await eventRepository.FindAsync(eventRegistration));
} }
catch (Exception e) catch (Exception e)
{ {
Logger.LogError($"An error occurred while sending an email to the attendees of the event after the event time has changed. Error message: {e.Message}"); Logger.LogError($"An error occurred while sending an email to the attendees of the event after the event time has changed. Error message: {e.Message}");
} }
} }
} }
} }
} }

23
src/EventHub.Application/Events/NewEventDetectorWorker.cs → src/EventHub.BackgroundServices/Events/NewEventDetectorWorker.cs

@ -13,8 +13,12 @@ namespace EventHub.Events
{ {
public class NewEventDetectorWorker : AsyncPeriodicBackgroundWorkerBase public class NewEventDetectorWorker : AsyncPeriodicBackgroundWorkerBase
{ {
public NewEventDetectorWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) public NewEventDetectorWorker(
: base(timer, serviceScopeFactory) AbpAsyncTimer timer,
IServiceScopeFactory serviceScopeFactory)
: base(
timer,
serviceScopeFactory)
{ {
Timer.Period = 60_000; Timer.Period = 60_000;
} }
@ -26,23 +30,22 @@ namespace EventHub.Events
var eventRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<Event, Guid>>(); var eventRepository = workerContext.ServiceProvider.GetRequiredService<IRepository<Event, Guid>>();
var asyncExecuter = workerContext.ServiceProvider.GetRequiredService<IAsyncQueryableExecuter>(); var asyncExecuter = workerContext.ServiceProvider.GetRequiredService<IAsyncQueryableExecuter>();
var eventQueryable = await eventRepository.GetQueryableAsync(); var queryable = await eventRepository.GetQueryableAsync();
var newEvents = await asyncExecuter.ToListAsync(
var eventQuery = eventQueryable.Where(x => x.IsEmailSentToMembers == false); queryable.Where(x => x.IsEmailSentToMembers == false)
);
var newEvents = await asyncExecuter.ToListAsync(eventQuery);
foreach (var @event in newEvents) foreach (var @event in newEvents)
{ {
try try
{ {
await newEventNotifier.NotifyAsync(@event); await newEventNotifier.NotifyAsync(@event);
@event.IsEmailSentToMembers = true; @event.IsEmailSentToMembers = true;
await eventRepository.UpdateAsync(@event);
} }
catch (Exception e) catch (Exception ex)
{ {
@event.IsEmailSentToMembers = false; Logger.LogException(ex);
Logger.LogError($"An error occurred while sending an email to the members of the organization after the new event was created. Error message: {e.Message}");
} }
} }
} }

34
src/EventHub.Application/Events/NewEventNotifier.cs → src/EventHub.BackgroundServices/Events/NewEventNotifier.cs

@ -16,15 +16,15 @@ namespace EventHub.Events
{ {
private readonly IEmailSender _emailSender; private readonly IEmailSender _emailSender;
private readonly ITemplateRenderer _templateRenderer; private readonly ITemplateRenderer _templateRenderer;
private readonly IRepository<OrganizationMembership, Guid> _organizationMembershipsRepository; private readonly IRepository<OrganizationMembership, Guid> _organizationMembershipsRepository;
private readonly IRepository<AppUser, Guid> _userRepository; private readonly IRepository<AppUser, Guid> _userRepository;
private readonly IAsyncQueryableExecuter _asyncExecuter; private readonly IAsyncQueryableExecuter _asyncExecuter;
public NewEventNotifier( public NewEventNotifier(
IEmailSender emailSender, IEmailSender emailSender,
ITemplateRenderer templateRenderer, ITemplateRenderer templateRenderer,
IRepository<OrganizationMembership, Guid> organizationMembershipsRepository, IRepository<OrganizationMembership, Guid> organizationMembershipsRepository,
IRepository<AppUser, Guid> userRepository, IRepository<AppUser, Guid> userRepository,
IAsyncQueryableExecuter asyncExecuter) IAsyncQueryableExecuter asyncExecuter)
{ {
_emailSender = emailSender; _emailSender = emailSender;
@ -33,31 +33,23 @@ namespace EventHub.Events
_userRepository = userRepository; _userRepository = userRepository;
_asyncExecuter = asyncExecuter; _asyncExecuter = asyncExecuter;
} }
public async Task NotifyAsync(Event @event) public async Task NotifyAsync(Event @event)
{ {
if (@event is null) var queryable = await _organizationMembershipsRepository.GetQueryableAsync();
{ var organizationMembers = await _asyncExecuter.ToListAsync(
return; queryable.Where(x => x.OrganizationId == @event.OrganizationId)
} );
var organizationMembershipsQueryable = await _organizationMembershipsRepository.GetQueryableAsync();
var membershipQuery = organizationMembershipsQueryable
.Where(x => x.OrganizationId == @event.OrganizationId);
var organizationMembers = await _asyncExecuter.ToListAsync(membershipQuery);
foreach (var member in organizationMembers) foreach (var member in organizationMembers)
{ {
var user = await _userRepository.FindAsync(member.UserId); var user = await _userRepository.FindAsync(member.UserId);
if (user is null) if (user is null)
{ {
continue; continue;
} }
var model = new var templateModel = new
{ {
UserName = user.GetFullNameOrUsername(), UserName = user.GetFullNameOrUsername(),
Title = @event.Title, Title = @event.Title,
@ -65,12 +57,12 @@ namespace EventHub.Events
EndTime = @event.EndTime, EndTime = @event.EndTime,
Url = @event.Url Url = @event.Url
}; };
await _emailSender.QueueAsync( await _emailSender.QueueAsync(
user.Email, user.Email,
$"Do you attend the {@event.Title} event?", $"Do you attend the {@event.Title} event?",
await _templateRenderer.RenderAsync(EmailTemplates.NewEventCreated, model) await _templateRenderer.RenderAsync(EmailTemplates.NewEventCreated, templateModel)
); );
} }
} }
} }

53
src/EventHub.BackgroundServices/Program.cs

@ -0,0 +1,53 @@
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
namespace EventHub
{
public class Program
{
public static async Task<int> Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
try
{
Log.Information("Starting console host.");
await CreateHostBuilder(args).RunConsoleAsync();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly!");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
internal static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseAutofac()
.UseSerilog()
.ConfigureServices((hostContext, services) =>
{
services.AddApplication<EventHubBackgroundServicesModule>();
});
}
}

3
src/EventHub.BackgroundServices/appsettings.json

@ -0,0 +1,3 @@
{
}

0
src/EventHub.Application/Emailing/EmailTemplateDefinitionProvider.cs → src/EventHub.Domain/Emailing/EmailTemplateDefinitionProvider.cs

0
src/EventHub.Application/Emailing/EmailTemplates.cs → src/EventHub.Domain/Emailing/EmailTemplates.cs

0
src/EventHub.Application/Emailing/Templates/EventRegistration.tpl → src/EventHub.Domain/Emailing/Templates/EventRegistration.tpl

0
src/EventHub.Application/Emailing/Templates/EventReminder.tpl → src/EventHub.Domain/Emailing/Templates/EventReminder.tpl

312
src/EventHub.Application/Emailing/Templates/EventTimingChanged.tpl → src/EventHub.Domain/Emailing/Templates/EventTimingChanged.tpl

@ -1,157 +1,157 @@
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width"> <meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Eventhub</title> <title>Eventhub</title>
<style> <style>
@media only screen and (max-width: 620px) { @media only screen and (max-width: 620px) {
table[class=mail-body] h1{ table[class=mail-body] h1{
font-size: 36px !important; font-size: 36px !important;
margin-bottom: 10px !important; margin-bottom: 10px !important;
} }
table[class=mail-body] .content { table[class=mail-body] .content {
padding: 0 !important; padding: 0 !important;
} }
table[class=mail-body] .container { table[class=mail-body] .container {
padding: 0 !important; padding: 0 !important;
width: 100% !important; width: 100% !important;
} }
table[class=mail-body] .main { table[class=mail-body] .main {
border-left-width: 0 !important; border-left-width: 0 !important;
border-radius: 0 !important; border-radius: 0 !important;
border-right-width: 0 !important; border-right-width: 0 !important;
} }
table[class=mail-body] td { table[class=mail-body] td {
width: 100% !important; width: 100% !important;
float: left!important; float: left!important;
} }
table[class=mail-body] td a.btn { table[class=mail-body] td a.btn {
display: block !important; display: block !important;
text-align: center; text-align: center;
} }
table[class=mail-body] td h2 { table[class=mail-body] td h2 {
margin: 10px 0 !important; margin: 10px 0 !important;
} }
table[class=mail-body] td .cont { table[class=mail-body] td .cont {
padding: 30px !important; padding: 30px !important;
} }
} }
</style> </style>
</head> </head>
<body> <body>
<table border="0" cellpadding="0" cellspacing="0" class="mail-body" style="background-color: #ffffff;width: 100%; font-size: 16px;line-height: 1.4;font-family: Arial, sans-serif;"> <table border="0" cellpadding="0" cellspacing="0" class="mail-body" style="background-color: #ffffff;width: 100%; font-size: 16px;line-height: 1.4;font-family: Arial, sans-serif;">
<tbody> <tbody>
<tr> <tr>
<td class="container" width="800" style="display: block;margin: 0 auto !important; max-width: 800px;padding: 10px;width: 800px;"> <td class="container" width="800" style="display: block;margin: 0 auto !important; max-width: 800px;padding: 10px;width: 800px;">
<div class="content" style="box-sizing: border-box;display: block;margin: 0 auto;max-width: 800px;padding: 0px;"> <div class="content" style="box-sizing: border-box;display: block;margin: 0 auto;max-width: 800px;padding: 0px;">
<div style="display: flex; padding-top: 1.5rem"> <div style="display: flex; padding-top: 1.5rem">
<h1 style="justify-content: center;margin-left: auto;margin-right: auto;align-content: center;">EventHub</h1> <h1 style="justify-content: center;margin-left: auto;margin-right: auto;align-content: center;">EventHub</h1>
</div> </div>
<!-- START CENTERED WHITE CONTAINER --> <!-- START CENTERED WHITE CONTAINER -->
<div style="display: flex;"> <div style="display: flex;">
<h1 style="justify-content: center;margin-left: auto;margin-right: auto;align-content: center;">{{model.title}} event time has been changed!</h1> <h1 style="justify-content: center;margin-left: auto;margin-right: auto;align-content: center;">{{model.title}} event time has been changed!</h1>
</div> </div>
<table class="main" style="background: #ffffff;border-radius: 3px; width: 100%;"> <table class="main" style="background: #ffffff;border-radius: 3px; width: 100%;">
<!-- START MAIN CONTENT AREA --> <!-- START MAIN CONTENT AREA -->
<tbody> <tbody>
<tr> <tr>
<td class="wrapper" width="800"> <td class="wrapper" width="800">
<table class="main" style="background: #ffffff;border-radius: 3px;width: 100%;"> <table class="main" style="background: #ffffff;border-radius: 3px;width: 100%;">
<tbody> <tbody>
<tr> <tr>
<td class="wrapper" width="800"> <td class="wrapper" width="800">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%; "> <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%; ">
<tbody> <tbody>
<tr> <tr>
<td style="vertical-align: top;" width="100%"> <td style="vertical-align: top;" width="100%">
<div class="cont" style="background: #eeeeee; border-radius: 10px; padding: 40px; margin-top: 20px;"> <div class="cont" style="background: #eeeeee; border-radius: 10px; padding: 40px; margin-top: 20px;">
<table style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%; "> <table style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%; ">
<tbody> <tbody>
<tr> <tr>
<td width="100%" style="vertical-align: top; font-size: 16px; font-family: Arial, sans-serif; color: #444; margin-top: 20px; margin-bottom: 20px; line-height: 1.75"> <td width="100%" style="vertical-align: top; font-size: 16px; font-family: Arial, sans-serif; color: #444; margin-top: 20px; margin-bottom: 20px; line-height: 1.75">
<p> <p>
Hi {{model.user_name}}, Hi {{model.user_name}},
</p> </p>
<p> <p>
The "{{model.title}}" event time just rescheduled. The "{{model.title}}" event time just rescheduled.
</p> </p>
<div> <div>
<table border="0" cellpadding="0" cellspacing="0" style=" border-collapse: separate; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;"> <table border="0" cellpadding="0" cellspacing="0" style=" border-collapse: separate; vertical-align: top; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tbody> <tbody>
<tr style="text-align: center"> <tr style="text-align: center">
<td style="vertical-align: top;"> <td style="vertical-align: top;">
<p style="font-size: 13px; font-family: Arial, sans-serif; color: #777777; margin: 0; margin-top: 30px;">Start Time</p> <p style="font-size: 13px; font-family: Arial, sans-serif; color: #777777; margin: 0; margin-top: 30px;">Start Time</p>
<p style="font-size: 21px; font-family: Arial, sans-serif; color: #444444; margin-bottom: 30px; margin-top: 6px;">{{model.start_time}}</p> <p style="font-size: 21px; font-family: Arial, sans-serif; color: #444444; margin-bottom: 30px; margin-top: 6px;">{{model.start_time}}</p>
</td> </td>
<td style="vertical-align: top;"> <td style="vertical-align: top;">
<p style="font-size: 13px; font-family: Arial, sans-serif; color: #777777; margin: 0; margin-top: 30px;">End Time</p> <p style="font-size: 13px; font-family: Arial, sans-serif; color: #777777; margin: 0; margin-top: 30px;">End Time</p>
<p style="font-size: 21px; font-family: Arial, sans-serif; color: #444444; margin-bottom: 30px; margin-top: 6px;">{{model.end_time}}</p> <p style="font-size: 21px; font-family: Arial, sans-serif; color: #444444; margin-bottom: 30px; margin-top: 6px;">{{model.end_time}}</p>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p style="margin-bottom: 20px; margin-top: 20px; text-align: center;"> <p style="margin-bottom: 20px; margin-top: 20px; text-align: center;">
<a href="/events/{{model.url}}" class="btn" target="_blank" style="padding: 15px; background: #e90052; border-radius: 40px; text-align: center; color: white; font-weight: bold; text-decoration: none; font-size: 18px; padding-left: 45px; padding-right: 45px;">Event Details</a> <a href="/events/{{model.url}}" class="btn" target="_blank" style="padding: 15px; background: #e90052; border-radius: 40px; text-align: center; color: white; font-weight: bold; text-decoration: none; font-size: 18px; padding-left: 45px; padding-right: 45px;">Event Details</a>
</p> </p>
</div> </div>
<p style="font-size: 16px; font-family: Arial, sans-serif; color: #444; margin-top: 20px; margin-bottom: 0px;"> <p style="font-size: 16px; font-family: Arial, sans-serif; color: #444; margin-top: 20px; margin-bottom: 0px;">
Thank you,<br> EventHub Thank you,<br> EventHub
</p> </p>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<!-- START FOOTER --> <!-- START FOOTER -->
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;"> <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; vertical-align: top;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
<tbody> <tbody>
<tr> <tr>
<td class="content-block" style="vertical-align: top;" width="800"> <td class="content-block" style="vertical-align: top;" width="800">
<div class="cont" style="background: #ffffff;border-top: 2px solid #eee; padding: 35px 40px 5px; margin-top: 0px; text-align: center;"> <div class="cont" style="background: #ffffff;border-top: 2px solid #eee; padding: 35px 40px 5px; margin-top: 0px; text-align: center;">
<a href="https://eventhub.abp.io/" style="font-size: 16px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; text-decoration: none;">Home</a> <a href="https://eventhub.abp.io/" style="font-size: 16px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; text-decoration: none;">Home</a>
</div> </div>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="content-block powered-by" style="vertical-align: top;" width="100%"> <td class="content-block powered-by" style="vertical-align: top;" width="100%">
<div class="cont" style="background: #ffffff; border-radius: 10px; padding: 5px 40px; margin-top: 10px; margin-bottom: 50px; text-align: center;"> <div class="cont" style="background: #ffffff; border-radius: 10px; padding: 5px 40px; margin-top: 10px; margin-bottom: 50px; text-align: center;">
<a href="mailto:info@eventhub.abp.io" style="font-size: 16px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; text-decoration: none;"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAARCAYAAADHeGwwAAABMElEQVQ4je3TvyvFYRQG8M+9LEwWiV0xyeAi2ZTUFQuZDAqTxUCJzY9BFotS/gAySAZFEgYlySzFLosfs169w6XL/V6uzbO973vO85z3OeekstlsGRYxrrTYw1g56jGNO6yivAQyS5hFdRpHGMQauvDwC+IXDGAGnTgJApXYxAJO0YLrH5Dfog3bmMAhatI5AUF1B4/oiIFJcYAMbrCRa3X6E0EvzlEbbZtLILCMHlQESzCS+/hZIKARF7Ef8+jDc564VwxhCq24jL/4gHwCAVXYxyR2o7e3Oe/30cbQuzEcB7/zEX03kkF8BU2RJFS3hVS07wnrGP2GI9HMD6MB/eiOd9Wx6vZCyUmXKhM9Hojn8JO6JInFbG2YrLMi4t/xVZNLhn+BgghNvkLzn7Bz9QbIADMmEt9imAAAAABJRU5ErkJggg==" width="16"> info@eventhub.abp.io</a> <a href="mailto:info@eventhub.abp.io" style="font-size: 16px; font-family: Arial, sans-serif; color: #777; font-weight: bold; margin-right: 30px; text-decoration: none;"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAARCAYAAADHeGwwAAABMElEQVQ4je3TvyvFYRQG8M+9LEwWiV0xyeAi2ZTUFQuZDAqTxUCJzY9BFotS/gAySAZFEgYlySzFLosfs169w6XL/V6uzbO973vO85z3OeekstlsGRYxrrTYw1g56jGNO6yivAQyS5hFdRpHGMQauvDwC+IXDGAGnTgJApXYxAJO0YLrH5Dfog3bmMAhatI5AUF1B4/oiIFJcYAMbrCRa3X6E0EvzlEbbZtLILCMHlQESzCS+/hZIKARF7Ef8+jDc564VwxhCq24jL/4gHwCAVXYxyR2o7e3Oe/30cbQuzEcB7/zEX03kkF8BU2RJFS3hVS07wnrGP2GI9HMD6MB/eiOd9Wx6vZCyUmXKhM9Hojn8JO6JInFbG2YrLMi4t/xVZNLhn+BgghNvkLzn7Bz9QbIADMmEt9imAAAAABJRU5ErkJggg==" width="16"> info@eventhub.abp.io</a>
<span style="font-size: 16px; font-family: Arial, sans-serif; color: #777; margin-right: 0px; text-decoration: none;"><strong>EventHub</strong> - Powered by Volosoft</span> <span style="font-size: 16px; font-family: Arial, sans-serif; color: #777; margin-right: 0px; text-decoration: none;"><strong>EventHub</strong> - Powered by Volosoft</span>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<!-- END FOOTER --> <!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER --> <!-- END CENTERED WHITE CONTAINER -->
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</body> </body>
</html> </html>

0
src/EventHub.Application/Emailing/Templates/NewEventCreated.tpl → src/EventHub.Domain/Emailing/Templates/NewEventCreated.tpl

10
src/EventHub.Domain/EventHub.Domain.csproj

@ -5,6 +5,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<RootNamespace>EventHub</RootNamespace> <RootNamespace>EventHub</RootNamespace>
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -23,4 +24,13 @@
<PackageReference Include="Volo.Abp.SettingManagement.Domain" Version="4.2.1" /> <PackageReference Include="Volo.Abp.SettingManagement.Domain" Version="4.2.1" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Remove="Emailing\Templates\*.tpl" />
<EmbeddedResource Include="Emailing\Templates\*.tpl" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="5.0.*" />
</ItemGroup>
</Project> </Project>

6
src/EventHub.Domain/EventHubDomainModule.cs

@ -10,6 +10,7 @@ using Volo.Abp.PermissionManagement.Identity;
using Volo.Abp.PermissionManagement.IdentityServer; using Volo.Abp.PermissionManagement.IdentityServer;
using Volo.Abp.SettingManagement; using Volo.Abp.SettingManagement;
using Volo.Abp.BlobStoring.Database; using Volo.Abp.BlobStoring.Database;
using Volo.Abp.VirtualFileSystem;
namespace EventHub namespace EventHub
{ {
@ -29,6 +30,11 @@ namespace EventHub
{ {
public override void ConfigureServices(ServiceConfigurationContext context) public override void ConfigureServices(ServiceConfigurationContext context)
{ {
Configure<AbpVirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<EventHubDomainModule>();
});
#if DEBUG #if DEBUG
context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>()); context.Services.Replace(ServiceDescriptor.Singleton<IEmailSender, NullEmailSender>());
#endif #endif

Loading…
Cancel
Save