Implemented scheduling service and first two jobs
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
using PostFundManagement.Application.Abstractions.Services;
|
||||
using PostFundManagement.Application.Services.Communications;
|
||||
using PostFundManagement.Domain;
|
||||
using PostFundManagement.Domain.Abstractions;
|
||||
using PostFundManagement.Infrastructure.Database;
|
||||
|
||||
namespace PostFundManagement.Application.Events;
|
||||
|
||||
public sealed class LateProjectEvent : IEvent
|
||||
{
|
||||
public Guid Id { get; set; } = Guid.CreateVersion7();
|
||||
|
||||
public string Name { get; set; } = nameof(LateProjectEvent);
|
||||
|
||||
public DateTimeOffset EnqueueAt { get; set; } = DateTimeOffset.UtcNow;
|
||||
|
||||
public string CorrelationId { get; set; } = "lateproject-reminder";
|
||||
|
||||
public static LateProjectEvent Create() => new();
|
||||
}
|
||||
|
||||
public sealed class LateProjectEventHandler(IDbContextFactory<ApplicationDbContext> contextFactory,
|
||||
ICommunicationsService service) : INotificationHandler<LateProjectEvent>
|
||||
{
|
||||
readonly int lateDays = 3;
|
||||
|
||||
public async ValueTask Handle(LateProjectEvent notification, CancellationToken cancellationToken)
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var lateMilestones = await context.Milestones
|
||||
.AsNoTracking()
|
||||
.Include(i => i.Project!.Programme!.Portfolio!.Organisation!.User)
|
||||
.Where(m => (DateTime.UtcNow - m.Project!.StartedAt!).Value.TotalDays < lateDays)
|
||||
.OrderBy(o => o.CreatedAt)
|
||||
.GroupBy(g => g.ProjectId)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if (lateMilestones is null) return;
|
||||
|
||||
using var htmlTemplateReader = new StreamReader(Assembly.GetAssembly(typeof(IDomain))?
|
||||
.GetManifestResourceStream("PostFundManagement.Domain.Templates.lateprojectsmilestones.html")!);
|
||||
|
||||
var template = await htmlTemplateReader.ReadToEndAsync(cancellationToken);
|
||||
|
||||
foreach (var group in lateMilestones)
|
||||
{
|
||||
if (!group.Any()) continue;
|
||||
|
||||
var project = group.FirstOrDefault()?.Project;
|
||||
|
||||
if (project is null) continue;
|
||||
|
||||
var mostLateMilestone = group.OrderBy(m => m.DueAt).FirstOrDefault();
|
||||
var missedMilestone = group.Where(m => DateTime.UtcNow > m.DueAt).FirstOrDefault();
|
||||
|
||||
var messageBody = template;
|
||||
messageBody = messageBody.Replace("[ProjectName]", project.Name);
|
||||
messageBody = messageBody.Replace("[ProjectStartDate]", $"{project.StartedAt:yyyy-MM-dd}");
|
||||
messageBody = messageBody.Replace("[ProjectEndDate]", $"{project.EndedAt:yyyy-MM-dd}");
|
||||
messageBody = messageBody.Replace("[TotalLateMiletones]", $"{group.Count()}");
|
||||
|
||||
// TODO: output information of most late milestone
|
||||
// TODO: output information of milestones already missed
|
||||
|
||||
var organisation = mostLateMilestone?.Project?.Programme?.Portfolio?.Organisation;
|
||||
|
||||
if (organisation is null) continue;
|
||||
|
||||
var notificationRequest = new CreateNotificationRequest(organisation.Id, organisation.User!.Id, NotificationPlatform.Email,
|
||||
"Project Status Update", organisation.User!.Email, messageBody, NotificationStatus.Queued);
|
||||
|
||||
_ = await service.CreateNotificationAsync(notificationRequest, SystemUser, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user