Implemented remaining business functionality

This commit is contained in:
2026-09-25 19:00:50 +02:00
parent 1efbf9d99f
commit 86e2bad4fe
42 changed files with 1755 additions and 172 deletions
@@ -1,4 +1,5 @@
using PostFundManagement.Application.Abstractions.Services;
using PostFundManagement.Application.Services.Shared;
using PostFundManagement.Domain;
using PostFundManagement.Domain.Abstractions;
using PostFundManagement.Domain.Errors;
@@ -8,7 +9,8 @@ using PostFundManagement.Infrastructure.Database;
namespace PostFundManagement.Application.Services.Communications;
public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext> contextFactory, ITenantContextService tenantContext) : ICommunicationsService
public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext> contextFactory,
HashService hashService, ITenantContextService tenantContext) : ICommunicationsService
{
public async Task<Result<Invite>> SendInviteAsync(InviteRequest request, Guid userId, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Invite>>>)(async () =>
@@ -23,24 +25,19 @@ public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext
ContractId = request.ContractId,
Recipient = request.Recipient,
CreatedBy = userId,
CreatedAt = DateTime.UtcNow
CreatedAt = DateTime.UtcNow,
TenantId = tenantContext.TenantId
};
context.Invites.Add(invite);
context.Notifications.Add(new Domain.Entities.Notification
{
OrganisationId = request.OrganisationId,
Recipient = request.Recipient,
Platform = request.Platform,
Status = NotificationStatus.Pending,
Subject = "Invitation to Onboard",
Message = request.Message,
CreatedAt = DateTime.UtcNow,
TenantId = tenantContext.TenantId
});
var notificationRequest = new CreateNotificationRequest(request.InvitedOrganisationId, request.Recipient,
request.Platform, "Invitation to Onboard", string.Empty, request.Message);
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0, CreateError.New)
var notificationResult = await CreateNotificationAsync(notificationRequest, userId, cancellationToken);
return Result.FailIf(await context.SaveChangesAsync(cancellationToken) <= 0 && notificationResult.IsFailed,
CreateError.Create().CausedBy(notificationResult.Errors))
.ToResult(invite.Map());
}));
@@ -86,23 +83,74 @@ public sealed class CommunicationsService(IDbContextFactory<ApplicationDbContext
await Result.Try((Func<Task<Result>>)(async () =>
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
context.Notifications.Add(new Domain.Entities.Notification
{
CreatedAt = DateTime.UtcNow,
Recipient = request.Recipient,
OrganisationId = request.OrganisationId,
Destination = request.Destination,
Subject = request.Subject,
Message = request.Message,
Platform = request.Platform,
Status = NotificationStatus.Queued,
TenantId = tenantContext.TenantId
});
return Result.OkIf(await context.SaveChangesAsync(cancellationToken) > 0, CreateError.Create("Failed to create entry"));
}));
public async Task<Result> UpdateNotificationAsync(UpdateNotificationRequest request, Guid userId, CancellationToken cancellationToken = default) =>
public async Task<Result> UpdateNotificationAsync(UpdateNotificationRequest request, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result>>)(async () =>
{
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var changedTotal = await context.Notifications
.Where(n => n.Id == request.NotificationId)
.ExecuteUpdateAsync(f => f
.SetProperty(u => u.Destination, request.Destination)
.SetProperty(u => u.SentAt, request.SentAt)
.SetProperty(u => u.Error, request.Error)
.SetProperty(u => u.UpdatedAt, DateTime.UtcNow)
.SetProperty(u => u.Status, request.Status), cancellationToken);
return Result.OkIf(changedTotal > 0, UpdateError.Create($"Failed to update notification: {request.NotificationId}"));
}));
public async Task<Result<Notification[]>> GetNotificationsAsync(string organisationIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Notification[]>>>)(async () =>
{
var decodeResult = hashService.DecodeLongIdHash(organisationIdHash);
if (decodeResult.IsFailed)
return Result.Fail(IdDecodeError.Create()
.CausedBy(decodeResult.Errors));
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var notifications = await context.Notifications.AsNoTracking()
.Where(n => n.OrganisationId == decodeResult.Value)
.OrderByDescending(o => o.Id)
.ToListAsync(cancellationToken);
return Result.Ok(notifications?.Select(i => i.Map()).ToArray() ?? []);
}));
public async Task<Result<Notification>> GetNotificationAsync(string notificationIdHash, CancellationToken cancellationToken = default) =>
await Result.Try((Func<Task<Result<Notification>>>)(async () =>
{
var decodeResult = hashService.DecodeLongIdHash(notificationIdHash);
if (decodeResult.IsFailed)
return Result.Fail(IdDecodeError.Create()
.CausedBy(decodeResult.Errors));
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
var notification = await context.Notifications.AsNoTracking()
.FirstOrDefaultAsync(n => n.Id == decodeResult.Value, cancellationToken);
return Result.FailIf(notification is null, NotFoundError.New)
.ToResult(notification!.Map());
}));
}